def mount(self, docker_volume_name): cinder_volume, state = self._get_docker_volume(docker_volume_name) LOG.info(_LI("Get docker volume {0} {1} with state " "{2}").format(docker_volume_name, cinder_volume, state)) if state != ATTACH_TO_THIS: msg = _("Volume {0} is not in correct state, current state " "is {1}").format(docker_volume_name, state) LOG.error(msg) raise exceptions.FuxiException(msg) connector = self._get_connector() link_path = connector.get_device_path(cinder_volume) if not os.path.exists(link_path): LOG.warn(_LW("Could not find device link file, " "so rebuild it")) connector.disconnect_volume(cinder_volume) connector.connect_volume(cinder_volume) devpath = os.path.realpath(link_path) if not devpath or not os.path.exists(devpath): msg = _("Can't find volume device path") LOG.error(msg) raise exceptions.FuxiException(msg) mountpoint = self._get_mountpoint(docker_volume_name) self._create_mountpoint(mountpoint) fstype = cinder_volume.metadata.get('fstype', cinder_conf.fstype) mount.do_mount(devpath, mountpoint, fstype) return mountpoint
def init_app_conf(): # Init volume providers. volume_providers = CONF.volume_providers if not volume_providers: raise Exception( _("Must define volume providers in " "configuration file")) app.volume_providers = collections.OrderedDict() for provider in volume_providers: if provider in volume_providers_conf: app.volume_providers[provider] = importutils\ .import_class(volume_providers_conf[provider])() LOG.info("Load volume provider: %s", provider) else: LOG.warning("Could not find volume provider: %s", provider) if not app.volume_providers: raise Exception( _("Not provide at least one effective " "volume provider")) # Init volume store directory. try: volume_dir = CONF.volume_dir if not os.path.exists(volume_dir) or not os.path.isdir(volume_dir): utils.execute('mkdir', '-p', '-m=700', volume_dir, run_as_root=True) except processutils.ProcessExecutionError: raise
def mount(self, docker_volume_name): cinder_volume, state = self._get_docker_volume(docker_volume_name) LOG.info(_LI("Get docker volume %(d_v)s %(vol)s with state %(st)s"), { 'd_v': docker_volume_name, 'vol': cinder_volume, 'st': state }) connector = self._get_connector() if state == NOT_ATTACH: connector.connect_volume(cinder_volume) elif state == ATTACH_TO_OTHER: if cinder_volume.multiattach: connector.connect_volume(cinder_volume) else: msg = _("Volume {0} {1} is not shareable").format( docker_volume_name, cinder_volume) raise exceptions.FuxiException(msg) elif state != ATTACH_TO_THIS: msg = _("Volume %(vol_name)s %(c_vol)s is not in correct state, " "current state is %(state)s") LOG.error( msg, { 'vol_name': docker_volume_name, 'c_vol': cinder_volume, 'state': state }) raise exceptions.NotMatchedState() link_path = connector.get_device_path(cinder_volume) if not os.path.exists(link_path): LOG.warning( _LW("Could not find device link file, " "so rebuild it")) connector.disconnect_volume(cinder_volume) connector.connect_volume(cinder_volume) devpath = os.path.realpath(link_path) if not devpath or not os.path.exists(devpath): msg = _("Can't find volume device path") LOG.error(msg) raise exceptions.FuxiException(msg) mountpoint = self._get_mountpoint(docker_volume_name) self._create_mountpoint(mountpoint) fstype = cinder_volume.metadata.get('fstype', cinder_conf.fstype) mount.do_mount(devpath, mountpoint, fstype) return mountpoint
def read_mounts(self, filter_device=(), filter_fstype=()): """Read all mounted filesystems. Read all mounted filesystems except filtered option. :param filter_device: Filter for device, the result will not contain the mounts whose device argument in it. :param filter_fstype: Filter for mount point. :return: All mounts. """ try: (out, err) = processutils.execute('cat', proc_mounts_path, check_exit_code=0) except processutils.ProcessExecutionError: msg = _("Failed to read mounts.") raise exceptions.FileNotFound(msg) lines = out.split('\n') mounts = [] for line in lines: if not line: continue tokens = line.split() if len(tokens) < 4: continue if tokens[0] in filter_device or tokens[1] in filter_fstype: continue mounts.append(MountInfo(device=tokens[0], mountpoint=tokens[1], fstype=tokens[2], opts=tokens[3])) return mounts
def _delete_volume(self, volume): try: self.cinderclient.volumes.delete(volume) except cinder_exception.NotFound: return except cinder_exception.ClientException as e: LOG.error( "Error happened when delete volume from Cinder." " Error: %s", e) raise start_time = time.time() # Wait until the volume is not there or until the operation timeout while (time.time() - start_time < consts.DESTROY_VOLUME_TIMEOUT): try: self.cinderclient.volumes.get(volume.id) except cinder_exception.NotFound: return time.sleep(consts.VOLUME_SCAN_TIME_DELAY) # If the volume is not deleted, raise an exception msg_ft = _("Timed out while waiting for volume. " "Expected Volume: {0}, " "Expected State: {1}, " "Elapsed Time: {2}").format(volume, None, time.time() - start_time) raise exceptions.TimeoutException(msg_ft)
def _get_connector(self): connector = cinder_conf.volume_connector if not connector or connector not in volume_connector_conf: msg = _("Must provide an valid volume connector") LOG.error(msg) raise exceptions.FuxiException(msg) return importutils.import_class(volume_connector_conf[connector])()
def unmount(self, mountpoint): try: utils.execute('umount', mountpoint, run_as_root=True) except processutils.ProcessExecutionError as e: msg = _("Unexpected err while unmount block device. " "Mountpoint: {0}, " "Error: {1}").format(mountpoint, e) raise exceptions.UnmountException(msg)
def make_filesystem(self, devpath, fstype): try: utils.execute('mkfs', '-t', fstype, '-F', devpath, run_as_root=True) except processutils.ProcessExecutionError as e: msg = _("Unexpected error while make filesystem. " "Devpath: {0}, " "Fstype: {1}" "Error: {2}").format(devpath, fstype, e) raise exceptions.MakeFileSystemException(msg)
def volumedriver_create(): json_data = flask.request.get_json(force=True) LOG.info("Received JSON data %s for /VolumeDriver.Create", json_data) docker_volume_name = json_data.get('Name', None) volume_opts = json_data.get('Opts', None) or {} if not docker_volume_name: msg = _("Request /VolumeDriver.Create need parameter 'Name'") LOG.error(msg) raise exceptions.InvalidInput(msg) if not isinstance(volume_opts, dict): msg = _("Request parameter 'Opts' must be dict type") LOG.error(msg) raise exceptions.InvalidInput(msg) volume_provider_type = volume_opts.get('volume_provider', None) if not volume_provider_type: volume_provider_type = list(app.volume_providers.keys())[0] if volume_provider_type not in app.volume_providers: msg_fmt = _("Could not find a handler for %(volume_provider_type)s " "volume") % { 'volume_provider_type': volume_provider_type } LOG.error(msg_fmt) return flask.jsonify(Err=msg_fmt) # If the volume with the same name already exists in other volume # provider backend, then raise an error for vpt, provider in app.volume_providers.items(): if volume_provider_type != vpt \ and provider.check_exist(docker_volume_name): msg_fmt = _("The volume with the same name already exists in " "other volume provider backend") LOG.error(msg_fmt) return flask.jsonify(Err=msg_fmt) # Create if volume does not exist, or attach to this server if needed # volume exists in related volume provider. app.volume_providers[volume_provider_type].create(docker_volume_name, volume_opts) return flask.jsonify(Err=u'')
def volumedriver_create(): json_data = flask.request.get_json(force=True) app.logger.info(_LI("Received JSON data {0} for " "/VolumeDriver.Create").format(json_data)) docker_volume_name = json_data.get('Name', None) volume_opts = json_data.get('Opts', None) or {} if not docker_volume_name: msg = _("Request /VolumeDriver.Create need parameter 'Name'") app.logger.error(msg) raise exceptions.InvalidInput(msg) if not isinstance(volume_opts, dict): msg = _("Request parameter 'Opts' must be dict type") app.logger.error(msg) raise exceptions.InvalidInput(msg) volume_provider_type = volume_opts.get('volume_provider', None) if not volume_provider_type: volume_provider_type = app.volume_providers.keys()[0] if volume_provider_type not in app.volume_providers: msg_fmt = _("Could not find a handler for %(volume_provider_type)s " "volume") % {'volume_provider_type': volume_provider_type} app.logger.error(msg_fmt) return flask.jsonify(Err=msg_fmt) # If the volume with the same name already exists in other volume # provider backend, then raise an error for vpt, provider in app.volume_providers.items(): if volume_provider_type != vpt \ and provider.check_exist(docker_volume_name): msg_fmt = _("The volume with the same name already exists in " "other volume provider backend") app.logger.error(msg_fmt) return flask.jsonify(Err=msg_fmt) # Create if volume does not exist, or attach to this server if needed # if volume exists in related volume provider. app.volume_providers[volume_provider_type].create(docker_volume_name, volume_opts) return flask.jsonify(Err=u'')
def get_cinder_volume_kwargs(docker_volume_name, docker_volume_opt): """Retrieve parameters for creating Cinder volume. Retrieve required parameters and remove unsupported arguments from client input. These parameters are used to create a Cinder volume. :param docker_volume_name: Name for Cinder volume :type docker_volume_name: str :param docker_volume_opt: Optional parameters for Cinder volume :type docker_volume_opt: dict :rtype: dict """ options = [ 'size', 'consistencygroup_id', 'snapshot_id', 'source_volid', 'description', 'volume_type', 'user_id', 'project_id', 'availability_zone', 'scheduler_hints', 'source_replica', 'multiattach' ] kwargs = {} if 'size' in docker_volume_opt: try: size = int(docker_volume_opt.pop('size')) except ValueError: msg = _("Volume size must be able to convert to int type") LOG.error(msg) raise exceptions.InvalidInput(msg) else: size = CONF.default_volume_size LOG.info( "Volume size doesn't provide from command, so use" " default size %sG", size) kwargs['size'] = size for key, value in docker_volume_opt.items(): if key in options: kwargs[key] = value if not kwargs.get('availability_zone', None): kwargs['availability_zone'] = cinder_conf.availability_zone if not kwargs.get('volume_type', None): kwargs['volume_type'] = cinder_conf.volume_type kwargs['name'] = docker_volume_name kwargs['metadata'] = { consts.VOLUME_FROM: CONF.volume_from, 'fstype': kwargs.pop('fstype', cinder_conf.fstype) } req_multiattach = kwargs.pop('multiattach', cinder_conf.multiattach) kwargs['multiattach'] = strutils.bool_from_string(req_multiattach, strict=True) return kwargs
def __init__(self): super(Manila, self).__init__() self.manilaclient = utils.get_manilaclient() conn_conf = manila_conf.volume_connector if not conn_conf or conn_conf not in volume_connector_conf: msg = _("Must provide a valid volume connector") LOG.error(msg) raise exceptions.InvalidInput(msg) self.connector = importutils.import_object( volume_connector_conf[conn_conf], manilaclient=self.manilaclient)
def show(self, docker_volume_name): cinder_volume, state = self._get_docker_volume(docker_volume_name) LOG.info("Get docker volume %(d_v)s %(vol)s with state %(st)s", { 'd_v': docker_volume_name, 'vol': cinder_volume, 'st': state }) if state == ATTACH_TO_THIS: devpath = os.path.realpath( self._get_connector().get_device_path(cinder_volume)) mp = self._get_mountpoint(docker_volume_name) LOG.info( ("Expected devpath: %(dp)s and mountpoint: %(mp)s for" " volume: %(d_v)s %(vol)s"), { 'dp': devpath, 'mp': mp, 'd_v': docker_volume_name, 'vol': cinder_volume }) mounter = mount.Mounter() return { "Name": docker_volume_name, "Mountpoint": mp if mp in mounter.get_mps_by_device(devpath) else '' } elif state in (NOT_ATTACH, ATTACH_TO_OTHER): return {'Name': docker_volume_name, 'Mountpoint': ''} elif state == UNKNOWN: msg = _("Can't find this volume '{0}' in " "Cinder").format(docker_volume_name) LOG.warning(msg) raise exceptions.NotFound(msg) else: msg = _("Volume '{0}' exists, but not attached to this volume," "and current state is {1}").format(docker_volume_name, state) raise exceptions.NotMatchedState(msg)
def mount(self, devpath, mountpoint, fstype=None): try: if fstype: utils.execute('mount', '-t', fstype, devpath, mountpoint, run_as_root=True) else: utils.execute('mount', devpath, mountpoint, run_as_root=True) except processutils.ProcessExecutionError as e: msg = _("Unexpected error while mount block device. " "Devpath: {0}, " "Mountpoint: {1} " "Error: {2}").format(devpath, mountpoint, e) raise exceptions.MountException(msg)
def volumedriver_remove(): json_data = flask.request.get_json(force=True) LOG.info("Received JSON data %s for /VolumeDriver.Remove", json_data) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Remove need parameter 'Name'") LOG.error(msg) raise exceptions.InvalidInput(msg) for provider in app.volume_providers.values(): if provider.delete(docker_volume_name): return flask.jsonify(Err=u'') return flask.jsonify(Err=u'')
def volumedriver_remove(): json_data = flask.request.get_json(force=True) app.logger.info(_LI("Received JSON data {0} for " "/VolumeDriver.Remove").format(json_data)) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Remove need parameter 'Name'") app.logger.error(msg) raise exceptions.InvalidInput(msg) for provider in app.volume_providers.values(): if provider.delete(docker_volume_name): return flask.jsonify(Err=u'') return flask.jsonify(Err=u'')
def volumedriver_mount(): json_data = flask.request.get_json(force=True) LOG.info("Receive JSON data %s for /VolumeDriver.Mount", json_data) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Mount need parameter 'Name'") LOG.error(msg) raise exceptions.InvalidInput(msg) for provider in app.volume_providers.values(): if provider.check_exist(docker_volume_name): mountpoint = provider.mount(docker_volume_name) return flask.jsonify(Mountpoint=mountpoint, Err=u'') return flask.jsonify(Err=u'Mount Failed')
def volumedriver_mount(): json_data = flask.request.get_json(force=True) app.logger.info(_LI("Receive JSON data {0} for " "/VolumeDriver.Mount").format(json_data)) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Mount need parameter 'Name'") app.logger.error(msg) raise exceptions.InvalidInput(msg) for provider in app.volume_providers.values(): if provider.check_exist(docker_volume_name): mountpoint = provider.mount(docker_volume_name) return flask.jsonify(Mountpoint=mountpoint, Err=u'') return flask.jsonify(Err=u'Mount Failed')
def volumedriver_get(): json_data = flask.request.get_json(force=True) LOG.info("Receive JSON data %s for /VolumeDriver.Get", json_data) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Get need parameter 'Name'") LOG.error(msg) raise exceptions.InvalidInput(msg) volume = get_docker_volume(docker_volume_name) if volume is not None: LOG.info("Get docker volume: %s", volume) return flask.jsonify(Volume=volume, Err=u'') LOG.warning("Can't find volume %s from every provider", docker_volume_name) return flask.jsonify(Err=u'Volume Not Found')
def _reached_desired_state(self, current_state): if current_state == self.desired_state: return True elif current_state in self.transient_states: idx = self.transient_states.index(current_state) if idx > 0: self.transient_states = self.transient_states[idx:] return False else: msg = _("Unexpected state while waiting for volume. " "Expected Volume: {0}, " "Expected State: {1}, " "Reached State: {2}").format(self.expected_obj, self.desired_state, current_state) LOG.error(msg) raise exceptions.UnexpectedStateException(msg)
def extract_share_kwargs(docker_volume_name, docker_volume_opts): """Extract parameters for creating manila share. Retrieve required parameters and remove unsupported arguments from client input. These parameters are used to create a Cinder volume. :param docker_volume_name: Name for Manila share. :param docker_volume_opts: Optional parameters for Manila share. :rtype: dict """ options = [ 'share_proto', 'size', 'snapshot_id', 'description', 'share_network', 'share_type', 'is_public', 'availability_zone', 'consistency_group_id' ] kwargs = {} if 'size' in docker_volume_opts: try: size = int(docker_volume_opts.pop('size')) except ValueError: msg = _("Volume size must able to convert to int type") LOG.error(msg) raise exceptions.InvalidInput(msg) else: size = CONF.default_volume_size LOG.info( "Volume size doesn't provide from command, so use " "default size %sG", size) kwargs['size'] = size share_proto = docker_volume_opts.pop('share_proto', None) \ or manila_conf.share_proto kwargs['share_proto'] = share_proto for key, value in docker_volume_opts.items(): if key in options: kwargs[key] = value kwargs['name'] = docker_volume_name kwargs['metadata'] = {consts.VOLUME_FROM: CONF.volume_from} return kwargs
def volumedriver_get(): json_data = flask.request.get_json(force=True) app.logger.info(_LI("Receive JSON data {0} for " "/VolumeDriver.Get").format(json_data)) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Get need parameter 'Name'") app.logger.error(msg) raise exceptions.InvalidInput(msg) volume = get_docker_volume(docker_volume_name) if volume is not None: msg_fmt = _LI("Get docker volume: {0}").format(volume) app.logger.info(msg_fmt) return flask.jsonify(Volume=volume, Err=u'') app.logger.warn(_LW("Can't find volume {0} from every " "provider").format(docker_volume_name)) return flask.jsonify(Err=u'Volume Not Found')
def volumedriver_path(): json_data = flask.request.get_json(force=True) app.logger.info(_LI("Receive JSON data {0} for " "/VolumeDriver.Path").format(json_data)) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Path need parameter 'Name'") app.logger.error(msg) raise exceptions.InvalidInput(msg) volume = get_docker_volume(docker_volume_name) if volume is not None: mountpoint = volume.get('Mountpoint', '') app.logger.info("Get mountpoint %(mp)s for docker volume %(name)s" % {'mp': mountpoint, 'name': docker_volume_name}) return flask.jsonify(Mountpoint=mountpoint, Err=u'') app.logger.warn(_LW("Can't find mountpoint for docker volume " "%(name)s") % {'name': docker_volume_name}) return flask.jsonify(Err=u'Mountpoint Not Found')
def volumedriver_path(): json_data = flask.request.get_json(force=True) LOG.info("Receive JSON data %s for /VolumeDriver.Path", json_data) docker_volume_name = json_data.get('Name', None) if not docker_volume_name: msg = _("Request /VolumeDriver.Path need parameter 'Name'") LOG.error(msg) raise exceptions.InvalidInput(msg) volume = get_docker_volume(docker_volume_name) if volume is not None: mountpoint = volume.get('Mountpoint', '') LOG.info("Get mountpoint %(mp)s for docker volume %(name)s", { 'mp': mountpoint, 'name': docker_volume_name }) return flask.jsonify(Mountpoint=mountpoint, Err=u'') LOG.warning("Can't find mountpoint for docker volume %(name)s", {'name': docker_volume_name}) return flask.jsonify(Err=u'Mountpoint Not Found')
def delete(self, docker_volume_name): cinder_volume, state = self._get_docker_volume(docker_volume_name) LOG.info("Get docker volume %(d_v)s %(vol)s with state %(st)s", { 'd_v': docker_volume_name, 'vol': cinder_volume, 'st': state }) if state == ATTACH_TO_THIS: link_path = self._get_connector().get_device_path(cinder_volume) if not link_path or not os.path.exists(link_path): msg = _("Could not find device link path for volume {0} {1} " "in host").format(docker_volume_name, cinder_volume) LOG.error(msg) raise exceptions.FuxiException(msg) devpath = os.path.realpath(link_path) if not os.path.exists(devpath): msg = ("Could not find device path for volume {0} {1} in " "host").format(docker_volume_name, cinder_volume) LOG.error(msg) raise exceptions.FuxiException(msg) mounter = mount.Mounter() mps = mounter.get_mps_by_device(devpath) ref_count = len(mps) if ref_count > 0: mountpoint = self._get_mountpoint(docker_volume_name) if mountpoint in mps: mounter.unmount(mountpoint) self._clear_mountpoint(mountpoint) # If this volume is still mounted on other mount point, # then return. if ref_count > 1: return True else: return True # Detach device from this server. self._get_connector().disconnect_volume(cinder_volume) available_volume = self.cinderclient.volumes.get(cinder_volume.id) # If this volume is not used by other server anymore, # than delete it from Cinder. if not available_volume.attachments: LOG.warning(("No other servers still use this volume %(d_v)s" " %(vol)s any more, so delete it from Cinder"), { 'd_v': docker_volume_name, 'vol': cinder_volume }) self._delete_volume(available_volume) return True elif state == NOT_ATTACH: self._delete_volume(cinder_volume) return True elif state == ATTACH_TO_OTHER: msg = "Volume %s is still in use, could not delete it" LOG.warning(msg, cinder_volume) return True elif state == UNKNOWN: return False else: msg = ("Volume %(vol_name)s %(c_vol)s " "state %(state)s is invalid") LOG.error( msg, { 'vol_name': docker_volume_name, 'c_vol': cinder_volume, 'state': state }) raise exceptions.NotMatchedState()
# License for the specific language governing permissions and limitations # under the License. import os import sys from fuxi.i18n import _, _LI from fuxi.common import config as lib_config from oslo_config import cfg from oslo_log import log as logging import pbr.version LOG = logging.getLogger(__name__) k8s_opts = [ cfg.StrOpt('api_root', help=_("The root URL of the Kubernetes API"), default=os.environ.get('K8S_API', 'http://localhost:8080')), cfg.StrOpt('volume_mount', default='/fuxi/data', help=_('At which the docker volume will mount.')), ] CONF = cfg.CONF CONF.register_opts(k8s_opts, group='kubernetes') CONF.register_opts(lib_config.default_opts) logging.register_options(CONF) def init(args, **kwargs):
def connect_volume(self, volume, **connect_opts): bdm = blockdevice.BlockerDeviceManager() ori_devices = bdm.device_scan() # Do volume-attach try: server_id = connect_opts.get('server_id', None) if not server_id: server_id = utils.get_instance_uuid() LOG.info("Start to connect to volume %s", volume) nova_volume = self.novaclient.volumes.create_server_volume( server_id=server_id, volume_id=volume.id, device=None) volume_monitor = state_monitor.StateMonitor( self.cinderclient, nova_volume, 'in-use', ( 'available', 'attaching', )) attached_volume = volume_monitor.monitor_cinder_volume() except nova_exception.ClientException as ex: LOG.error( "Attaching volume %(vol)s to server %(s)s " "failed. Error: %(err)s", { 'vol': volume.id, 's': server_id, 'err': ex }) raise # Get all devices on host after do volume-attach, # and then find attached device. LOG.info("After connected to volume, scan the added " "block device on host") curr_devices = bdm.device_scan() start_time = time.time() delta_devices = list(set(curr_devices) - set(ori_devices)) while not delta_devices: time.sleep(consts.DEVICE_SCAN_TIME_DELAY) curr_devices = bdm.device_scan() delta_devices = list(set(curr_devices) - set(ori_devices)) if time.time() - start_time > consts.DEVICE_SCAN_TIMEOUT: msg = _("Could not detect added device with " "limited time") raise exceptions.FuxiException(msg) LOG.info("Get extra added block device %s", delta_devices) for device in delta_devices: if bdm.get_device_size(device) == volume.size: device = device.replace('/sys/block', '/dev') LOG.info( "Find attached device %(dev)s" " for volume %(at)s %(vol)s", { 'dev': device, 'at': attached_volume.name, 'vol': volume }) link_path = os.path.join(consts.VOLUME_LINK_DIR, volume.id) try: utils.execute('ln', '-s', device, link_path, run_as_root=True) except processutils.ProcessExecutionError as e: LOG.error( "Error happened when create link file for" " block device attached by Nova." " Error: %s", e) raise return {'path': link_path} LOG.warning("Could not find matched device") raise exceptions.NotFound("Not Found Matched Device")
def connect_volume(self, volume, **connect_opts): bdm = blockdevice.BlockerDeviceManager() ori_devices = bdm.device_scan() # Do volume-attach try: server_id = connect_opts.get('server_id', None) if not server_id: server_id = utils.get_instance_uuid() LOG.info(_LI("Start to connect to volume {0}").format(volume)) nova_volume = self.novaclient.volumes.create_server_volume( server_id=server_id, volume_id=volume.id, device=None) volume_monitor = state_monitor.StateMonitor( self.cinderclient, nova_volume, 'in-use', ('available', 'attaching',)) attached_volume = volume_monitor.monitor_cinder_volume() except nova_exception.ClientException as ex: LOG.error(_LE("Attaching volume {0} to server {1} " "failed. Error: {2}").format(volume.id, server_id, ex)) raise # Get all devices on host after do volume-attach, # and then find attached device. LOG.info(_LI("After connected to volume, scan the added " "block device on host")) curr_devices = bdm.device_scan() start_time = time.time() delta_devices = list(set(curr_devices) - set(ori_devices)) while not delta_devices: time.sleep(consts.DEVICE_SCAN_TIME_DELAY) curr_devices = bdm.device_scan() delta_devices = list(set(curr_devices) - set(ori_devices)) if time.time() - start_time > consts.DEVICE_SCAN_TIMEOUT: msg = _("Could not detect added device with " "limited time") raise exceptions.FuxiException(msg) LOG.info(_LI("Get extra added block device {0}" "").format(delta_devices)) for device in delta_devices: if bdm.get_device_size(device) == volume.size: device = device.replace('/sys/block', '/dev') msg = _LI("Find attached device {0} for volume {1} " "{2}").format(device, attached_volume.name, volume) LOG.info(msg) link_path = os.path.join(consts.VOLUME_LINK_DIR, volume.id) try: utils.execute('ln', '-s', device, link_path, run_as_root=True) except processutils.ProcessExecutionError as e: msg = _LE("Error happened when create link file for " "block device attached by Nova. " "Error: {0}").format(e) LOG.error(msg) raise return {'path': link_path} LOG.warm(_LW("Could not find matched device")) raise exceptions.NotFound("Not Found Matched Device")
# 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 os from kuryr.lib import config as kuryr_config from kuryr.lib import opts as kuryr_opts from oslo_config import cfg from oslo_log import log as logging from fuxi.i18n import _ from fuxi.version import version_info default_opts = [ cfg.HostAddressOpt('my_ip', help=_('IP address of this machine.')), cfg.IntOpt('fuxi_port', default=7879, help=_('Port for fuxi volume driver server.')), cfg.StrOpt('volume_dir', default='/fuxi/data', help=_('At which the docker volume will create.')), cfg.ListOpt('volume_providers', help=_('Volume storage backends that provide volume for ' 'Docker')), cfg.StrOpt('volume_from', default='fuxi', help=_('Setting label for volume.')), cfg.IntOpt('default_volume_size', default=1, help=_('Default size for volume.')),
def create(self, docker_volume_name, volume_opts): if not volume_opts: volume_opts = {} connector = self._get_connector() cinder_volume, state = self._get_docker_volume(docker_volume_name) LOG.info("Get docker volume %(d_v)s %(vol)s with state %(st)s", { 'd_v': docker_volume_name, 'vol': cinder_volume, 'st': state }) device_info = {} if state == ATTACH_TO_THIS: LOG.warning( "The volume %(d_v)s %(vol)s already exists " "and attached to this server", { 'd_v': docker_volume_name, 'vol': cinder_volume }) device_info = {'path': connector.get_device_path(cinder_volume)} elif state == NOT_ATTACH: LOG.warning( "The volume %(d_v)s %(vol)s is already exists " "but not attached", { 'd_v': docker_volume_name, 'vol': cinder_volume }) device_info = connector.connect_volume(cinder_volume) elif state == ATTACH_TO_OTHER: if cinder_volume.multiattach: fstype = volume_opts.get('fstype', cinder_conf.fstype) vol_fstype = cinder_volume.metadata.get( 'fstype', cinder_conf.fstype) if fstype != vol_fstype: LOG.error( ("Volume already exists with fstype: %{v_fs}s, but " "currently provided fstype is %{fs}s, not " "match"), { 'v_fs': vol_fstype, 'fs': fstype }) raise exceptions.FuxiException('FSType Not Match') device_info = connector.connect_volume(cinder_volume) else: msg = _("The volume {0} {1} is already attached to another " "server").format(docker_volume_name, cinder_volume) LOG.error(msg) raise exceptions.FuxiException(msg) elif state == UNKNOWN: if 'volume_id' in volume_opts: cinder_volume = self._create_from_existing_volume( docker_volume_name, volume_opts.pop('volume_id'), volume_opts) if self._check_attached_to_this(cinder_volume): device_info = { 'path': connector.get_device_path(cinder_volume) } else: device_info = connector.connect_volume(cinder_volume) else: cinder_volume = self._create_volume(docker_volume_name, volume_opts) device_info = connector.connect_volume(cinder_volume) return device_info