summaryrefslogtreecommitdiff
path: root/nova/virt/libvirt/volume/vzstorage.py
blob: babfdef55c67d07ec4d49db69801188dfbdd9212 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import collections
import re

from os_brick import initiator
from os_brick.initiator import connector
from oslo_config import cfg
from oslo_log import log as logging

from nova import exception
from nova.i18n import _
from nova import utils
from nova.virt.libvirt.volume import fs

LOG = logging.getLogger(__name__)
CONF = cfg.CONF

VzShare = collections.namedtuple('VzShare',
                                 ['cluster_name', 'mds_list', 'password'])


class LibvirtVZStorageVolumeDriver(fs.LibvirtBaseFileSystemVolumeDriver):
    """Class implements libvirt part of volume driver for VzStorage."""

    SHARE_FORMAT_REGEX = r'(?:(\S+):/)?([a-zA-Z0-9_-]+)(?::(\S+))?$'
    SHARE_LOCK_NAME = "vz_share-%s"

    def __init__(self, connection):
        super(LibvirtVZStorageVolumeDriver, self).__init__(connection)

        # Check for duplicate options:
        # -c - cluster name
        # -l - log file, includes %(cluster_name)s, so it's handled as a
        #      separate config parameter
        # -C - SSD cache file, the same thing with %(cluster_name)s
        # -u, -g, -m - there are default values for these options, so
        #              they're separate config parameters
        cfg_opts_set = set(CONF.libvirt.vzstorage_mount_opts)
        invalid_opts_set = set(('-c', '-l', '-C', '-u', '-g', '-m',))
        invalid_cfg_opts = cfg_opts_set.intersection(invalid_opts_set)

        if invalid_cfg_opts:
            msg = (_("You can't use %s options in vzstorage_mount_opts "
                     "configuration parameter.") %
                     ', '.join(invalid_cfg_opts))
            raise exception.InternalError(msg)

        # Call the factory here so we can support
        # more than x86 architectures.
        self.connector = connector.InitiatorConnector.factory(
            initiator.VZSTORAGE, utils.get_root_helper(),
            vzstorage_mount_point_base=CONF.libvirt.vzstorage_mount_point_base)

    def _get_mount_point_base(self):
        return CONF.libvirt.vzstorage_mount_point_base

    def get_config(self, connection_info, disk_info):
        """Returns xml for libvirt."""
        conf = super(LibvirtVZStorageVolumeDriver,
                     self).get_config(connection_info, disk_info)

        conf.source_type = 'file'
        conf.driver_cache = 'writeback'
        conf.source_path = connection_info['data']['device_path']
        conf.driver_format = connection_info['data'].get('format', 'raw')
        return conf

    def _parse_vz_share(self, vz_share):
        m = re.match(self.SHARE_FORMAT_REGEX, vz_share)
        if not m:
            msg = _("Valid share format is "
                    "[mds[,mds1[...]]:/]clustername[:password]")
            raise exception.InvalidVolume(msg)

        if m.group(1):
            mds_list = m.group(1).split(',')
        else:
            mds_list = None

        return VzShare(cluster_name=m.group(2),
                       mds_list=mds_list,
                       password=m.group(3))

    def _get_mount_opts(self, vz_share):
        cluster_name = self._parse_vz_share(vz_share).cluster_name

        # pstorage-mount man page:
        # https://static.openvz.org/vz-man/man1/pstorage-mount.1.gz.html
        mount_opts = ['-u', CONF.libvirt.vzstorage_mount_user,
                      '-g', CONF.libvirt.vzstorage_mount_group,
                      '-m', CONF.libvirt.vzstorage_mount_perms,
                      '-l', (CONF.libvirt.vzstorage_log_path %
                             {'cluster_name': cluster_name})]

        if CONF.libvirt.vzstorage_cache_path:
            mount_opts.extend(['-C', (CONF.libvirt.vzstorage_cache_path %
                                      {'cluster_name': cluster_name})])
        mount_opts.extend(CONF.libvirt.vzstorage_mount_opts)

        return ' '.join(mount_opts)

    def connect_volume(self, connection_info, instance):
        """Attach the volume to instance_name."""
        vz_share = connection_info['data']['export']
        share_lock = self.SHARE_LOCK_NAME % vz_share

        @utils.synchronized(share_lock)
        def _connect_volume(connection_info, instance):
            LOG.debug("Calling os-brick to mount vzstorage")
            connection_info['data']['options'] = self._get_mount_opts(vz_share)
            device_info = self.connector.connect_volume(
                connection_info['data'])
            LOG.debug("Attached vzstorage volume %s", device_info)
            connection_info['data']['device_path'] = device_info['path']

        return _connect_volume(connection_info, instance)

    def disconnect_volume(self, connection_info, instance, force=False):
        """Detach the volume from instance_name."""
        LOG.debug("calling os-brick to detach Vzstorage Volume",
                instance=instance)
        self.connector.disconnect_volume(
            connection_info['data'], None, force=force)
        LOG.debug("Disconnected Vzstorage Volume", instance=instance)