def _create_volume(self, docker_volume_name, volume_opts): LOG.info(_LI("Start to create docker volume {0} from " "Cinder").format(docker_volume_name)) cinder_volume_kwargs = get_cinder_volume_kwargs(docker_volume_name, volume_opts) try: volume = self.cinderclient.volumes.create(**cinder_volume_kwargs) except cinder_exception.ClientException as e: msg = _LE("Error happened when create an volume {0} from Cinder. " "Error: {1}").format(docker_volume_name, e) LOG.error(msg) raise LOG.info(_LI("Waiting volume {0} to be available").format(volume)) volume_monitor = state_monitor.StateMonitor( self.cinderclient, volume, 'available', ('creating',), time_delay=consts.VOLUME_SCAN_TIME_DELAY) volume = volume_monitor.monitor_cinder_volume() LOG.info(_LI("Create docker volume {0} {1} from Cinder " "successfully").format(docker_volume_name, volume)) return volume
def _create_volume(self, docker_volume_name, volume_opts): LOG.info( _LI("Start to create docker volume {0} from " "Cinder").format(docker_volume_name)) cinder_volume_kwargs = get_cinder_volume_kwargs( docker_volume_name, volume_opts) try: volume = self.cinderclient.volumes.create(**cinder_volume_kwargs) except cinder_exception.ClientException as e: msg = _LE("Error happened when create an volume {0} from Cinder. " "Error: {1}").format(docker_volume_name, e) LOG.error(msg) raise LOG.info(_LI("Waiting volume {0} to be available").format(volume)) volume_monitor = state_monitor.StateMonitor( self.cinderclient, volume, 'available', ('creating', ), time_delay=consts.VOLUME_SCAN_TIME_DELAY) volume = volume_monitor.monitor_cinder_volume() LOG.info( _LI("Create docker volume {0} {1} from Cinder " "successfully").format(docker_volume_name, volume)) return volume
def list(self): LOG.info(_LI("Start to retrieve all docker volumes from OpenSDS")) docker_volumes = [] try: for vol in self.opensdsclient.list(): # LOG.info(_LI("Retrieve docker volumes {0} from OpenSDS " # "successfully").format(vol)) docker_volume_name = vol['name'] if not docker_volume_name: continue mountpoint = self._get_mountpoint(docker_volume_name) vol = APIDictWrapper(vol) devpath = os.path.realpath( self._get_connector().get_device_path(vol)) mps = mount.Mounter().get_mps_by_device(devpath) mountpoint = mountpoint if mountpoint in mps else '' docker_vol = {'Name': docker_volume_name, 'Mountpoint': mountpoint} docker_volumes.append(docker_vol) except cinder_exception.ClientException as e: LOG.error(_LE("Retrieve volume list failed. Error: {0}").format(e)) raise LOG.info(_LI("Retrieve docker volumes {0} from OpenSDS " "successfully").format(docker_volumes)) return docker_volumes
def _create_volume(self, docker_volume_name, volume_opts): LOG.info(_LI("Start to create docker volume %s from Cinder"), docker_volume_name) cinder_volume_kwargs = get_cinder_volume_kwargs( docker_volume_name, volume_opts) try: volume = self.cinderclient.volumes.create(**cinder_volume_kwargs) except cinder_exception.ClientException as e: LOG.error( _LE("Error happened when create an volume %(vol)s from" " Cinder. Error: %(err)s"), { 'vol': docker_volume_name, 'err': e }) raise LOG.info(_LI("Waiting volume %s to be available"), volume) volume_monitor = state_monitor.StateMonitor( self.cinderclient, volume, 'available', ('creating', ), time_delay=consts.VOLUME_SCAN_TIME_DELAY) volume = volume_monitor.monitor_cinder_volume() LOG.info( _LI("Create docker volume %(d_v)s %(vol)s from Cinder " "successfully"), { 'd_v': docker_volume_name, 'vol': volume }) return volume
def list(self): LOG.info(_LI("Start to retrieve all docker volumes from Cinder")) docker_volumes = [] try: search_opts = {'metadata': {consts.VOLUME_FROM: 'fuxi'}} for vol in self.cinderclient.volumes.list(search_opts=search_opts): docker_volume_name = vol.name if not docker_volume_name or not vol.attachments: continue mountpoint = self._get_mountpoint(vol.name) if self._check_attached_to_this(vol): devpath = os.path.realpath( self._get_connector().get_device_path(vol)) mps = mount.Mounter().get_mps_by_device(devpath) mountpoint = mountpoint if mountpoint in mps else '' docker_vol = {'Name': docker_volume_name, 'Mountpoint': mountpoint} docker_volumes.append(docker_vol) except cinder_exception.ClientException as e: LOG.error(_LE("Retrieve volume list failed. Error: {0}").format(e)) raise LOG.info(_LI("Retrieve docker volumes {0} from Cinder " "successfully").format(docker_volumes)) return docker_volumes
def _access_allow(self, share): share_proto = share.share_proto if share_proto not in self.proto_access_type_map.keys(): raise exceptions.InvalidProtocol("Not enabled share protocol %s" % share_proto) try: if self.check_access_allowed(share): return access_type = self.proto_access_type_map[share_proto] access_to = self._get_access_to(access_type) LOG.info( _LI("Allow machine to access share %(shr)s with " "access_type %(type)s and access_to %(to)s"), { 'shr': share, 'type': access_type, 'to': access_to }) self.manilaclient.shares.allow(share, access_type, access_to, 'rw') except manila_exception.ClientException as e: LOG.error(_LE("Failed to grant access for server, %s"), e) raise LOG.info(_LI("Waiting share %s access to be active"), share) state_monitor.StateMonitor(self.manilaclient, share, 'active', ('new', )).monitor_share_access( access_type, access_to)
def list(self): LOG.info(_LI("Start to retrieve all docker volumes from Cinder")) docker_volumes = [] try: search_opts = {'metadata': {consts.VOLUME_FROM: CONF.volume_from}} for vol in self.cinderclient.volumes.list(search_opts=search_opts): docker_volume_name = vol.name if not docker_volume_name: continue mountpoint = self._get_mountpoint(vol.name) devpath = os.path.realpath( self._get_connector().get_device_path(vol)) mps = mount.Mounter().get_mps_by_device(devpath) mountpoint = mountpoint if mountpoint in mps else '' docker_vol = { 'Name': docker_volume_name, 'Mountpoint': mountpoint } docker_volumes.append(docker_vol) except cinder_exception.ClientException as e: LOG.error(_LE("Retrieve volume list failed. Error: %s"), e) raise LOG.info(_LI("Retrieve docker volumes %s from Cinder " "successfully"), docker_volumes) return docker_volumes
def setup_logging(): logging.setup(CONF, 'fuxi-kubernetes') logging.set_defaults(default_log_levels=logging.get_default_log_levels()) version_k8s = pbr.version.VersionInfo('fuxi-kubernetes').version_string() LOG.info(_LI("Logging enabled!")) LOG.info(_LI("%(prog)s version %(version)s"), { 'prog': sys.argv[0], 'version': version_k8s })
def volumedriver_list(): app.logger.info(_LI("/VolumeDriver.List")) docker_volumes = [] for provider in app.volume_providers.values(): vs = provider.list() if vs: docker_volumes.extend(vs) app.logger.info(_LI("Get volumes from volume providers. " "Volumes: {0}").format(docker_volumes)) return flask.jsonify(Err=u'', Volumes=docker_volumes)
def _watch(self, path): try: LOG.info(_LI("Started watching '%s'"), path) for event in self._client.watch(path): self._idle[path] = False self._handler(event) self._idle[path] = True if not (self._running and path in self._resources): return finally: self._watching.pop(path) self._idle.pop(path) LOG.info(_LI("Stopped watching '%s'"), path)
def _get_connection_info(self, volume_id): LOG.info( _LI("Get connection info for osbrick connector and use it to " "connect to volume")) try: conn_info = self.cinderclient.volumes.initialize_connection( volume_id, brick_get_connector_properties()) LOG.info(_LI("Get connection information %s"), conn_info) return conn_info except cinder_exception.ClientException as e: LOG.error( _LE("Error happened when initialize connection" " for volume. Error: %s"), e) raise
def _get_connection_info(self, volume_id): LOG.info(_LI("Get connection info for osbrick connector and use it to " "connect to volume")) try: conn_info = self.cinderclient.volumes.initialize_connection( volume_id, brick_get_connector_properties()) msg = _LI("Get connection information {0}").format(conn_info) LOG.info(msg) return conn_info except cinder_exception.ClientException as e: msg = _LE("Error happened when initialize connection for volume. " "Error: {0}").format(e) LOG.error(msg) raise
def _get_docker_volume(self, docker_volume_name): LOG.info(_LI("Retrieve docker volume {0} from " "OpenSDS").format(docker_volume_name)) try: host_id = get_host_id() volume_connector = cinder_conf.volume_connector # search_opts = {'name': docker_volume_name, # 'metadata': {consts.VOLUME_FROM: CONF.volume_from}} for vol in self.opensdsclient.list(): vol = APIDictWrapper(vol) LOG.info("dsl: vol=%s", vol.__dict__) if vol.name == docker_volume_name: if vol.attachments: for am in vol.attachments: if volume_connector == OPENSTACK: if am['server_id'] == host_id: return vol, ATTACH_TO_THIS elif volume_connector == OSBRICK: if (am['host_name'] or '').lower() == host_id: return vol, ATTACH_TO_THIS return vol, ATTACH_TO_OTHER else: return vol, NOT_ATTACH return None, UNKNOWN
def _create_share(self, docker_volume_name, share_opts): share_kwargs = extract_share_kwargs(docker_volume_name, share_opts) try: LOG.debug("Start to create share from Manila") share = self.manilaclient.shares.create(**share_kwargs) except manila_exception.ClientException as e: LOG.error(_LE("Create Manila share failed. Error: {0}"), e) raise LOG.info(_LI("Waiting for share %s status to be available"), share) share_monitor = state_monitor.StateMonitor(self.manilaclient, share, 'available', ('creating', )) share = share_monitor.monitor_manila_share() LOG.info(_LI("Creating share %s successfully"), share) return share
def connect_volume(self, volume, **connect_opts): mountpoint = connect_opts.get('mountpoint', None) host_name = utils.get_hostname() try: self.cinderclient.volumes.reserve(volume) except cinder_exception.ClientException: LOG.error(_LE("Reserve volume %s failed"), volume) raise try: device_info = self._connect_volume(volume) self.cinderclient.volumes.attach(volume=volume, instance_uuid=None, mountpoint=mountpoint, host_name=host_name) LOG.info(_LI("Attach volume to this server successfully")) except Exception: LOG.error(_LE("Attach volume %s to this server failed"), volume) with excutils.save_and_reraise_exception(): try: self._disconnect_volume(volume) except Exception: pass self.cinderclient.volumes.unreserve(volume) return device_info
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])() app.logger.info(_LI("Load volume provider: {0}").format(provider)) else: msg = _LW("Could not find volume provider: {0}").format(provider) app.logger.warn(msg) 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 disconnect_volume(self, share, **disconnect_opts): mountpoint = self.get_mountpoint(share) mount.Mounter().unmount(mountpoint) self._access_deny(share) def _check_access_binded(s): sal = self.manilaclient.shares.access_list(s) share_proto = s.share_proto access_type = self.proto_access_type_map.get(share_proto) access_to = self._get_access_to(access_type) for a in sal: if a.access_type == access_type and a.access_to == access_to: if a.state in ('error', 'error_deleting'): raise exceptions.NotMatchedState( "Revoke access {0} failed".format(a)) return True return False start_time = time.time() while time.time() - start_time < consts.ACCSS_DENY_TIMEOUT: if not _check_access_binded(share): LOG.info(_LI("Disconnect share %s successfully"), share) return time.sleep(consts.SCAN_INTERVAL) raise exceptions.TimeoutException("Disconnect volume timeout")
def _get_docker_volume(self, docker_volume_name): LOG.info( _LI("Retrieve docker volume {0} from " "Cinder").format(docker_volume_name)) try: host_id = get_host_id() volume_connector = cinder_conf.volume_connector search_opts = { 'name': docker_volume_name, 'metadata': { consts.VOLUME_FROM: CONF.volume_from } } for vol in self.cinderclient.volumes.list(search_opts=search_opts): if vol.name == docker_volume_name: if vol.attachments: for am in vol.attachments: if volume_connector == OPENSTACK: if am['server_id'] == host_id: return vol, ATTACH_TO_THIS elif volume_connector == OSBRICK: if (am['host_name'] or '').lower() == host_id: return vol, ATTACH_TO_THIS return vol, ATTACH_TO_OTHER else: return vol, NOT_ATTACH return None, UNKNOWN except cinder_exception.ClientException as ex: LOG.error( _LE("Error happened while getting volume list " "information from cinder. Error: {0}").format(ex)) raise
def show(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: devpath = os.path.realpath( self._get_connector().get_device_path(cinder_volume)) mp = self._get_mountpoint(docker_volume_name) LOG.info("Expected devpath: {0} and mountpoint: {1} for volume: " "{2} {3}".format(devpath, mp, docker_volume_name, 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 = _LW("Can't find this volume '{0}' in " "OpenSDS").format(docker_volume_name) LOG.warning(msg) raise exceptions.NotFound(msg) else: msg = _LE("Volume '{0}' exists, but not attached to this volume," "and current state is {1}").format(docker_volume_name, state) raise exceptions.NotMatchedState(msg)
def show(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: devpath = os.path.realpath( self._get_connector().get_device_path(cinder_volume)) mp = self._get_mountpoint(docker_volume_name) LOG.info("Expected devpath: {0} and mountpoint: {1} for volume: " "{2} {3}".format(devpath, mp, docker_volume_name, cinder_volume)) mounter = mount.Mounter() return {"Name": docker_volume_name, "Mountpoint": mp if mp in mounter.get_mps_by_device( devpath) else ''} elif state == UNKNOWN: msg = _LW("Can't find this volume '{0}' in " "Cinder").format(docker_volume_name) LOG.warn(msg) raise exceptions.NotFound(msg) else: msg = _LE("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, 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 _get_docker_volume(self, docker_volume_name): LOG.info(_LI("Retrieve docker volume {0} from " "Cinder").format(docker_volume_name)) try: host_id = get_host_id() volume_connector = cinder_conf.volume_connector search_opts = {'name': docker_volume_name, 'metadata': {consts.VOLUME_FROM: CONF.volume_from}} for vol in self.cinderclient.volumes.list(search_opts=search_opts): if vol.name == docker_volume_name: if vol.attachments: for am in vol.attachments: if volume_connector == OPENSTACK: if am['server_id'] == host_id: return vol, ATTACH_TO_THIS elif volume_connector == OSBRICK: if (am['host_name'] or '').lower() == host_id: return vol, ATTACH_TO_THIS return vol, ATTACH_TO_OTHER else: return vol, NOT_ATTACH return None, UNKNOWN except cinder_exception.ClientException as ex: LOG.error(_LE("Error happened while getting volume list " "information from cinder. Error: {0}").format(ex)) raise
def check_exist(self, docker_volume_name): _, state = self._get_docker_volume(docker_volume_name) LOG.info(_LI("Get docker volume {0} with state " "{1}").format(docker_volume_name, state)) if state == UNKNOWN: return False return True
def _create_volume(self, docker_volume_name, volume_opts): LOG.info(_LI("Start to create docker volume {0} from " "OpenSDS").format(docker_volume_name)) try: volume = self.opensdsclient.create(docker_volume_name, volume_opts['size']) except cinder_exception.ClientException as e: msg = _LE("Error happened when create an volume {0} from OpenSDS. " "Error: {1}").format(docker_volume_name, e) LOG.error(msg) raise volume = APIDictWrapper(volume) time.sleep(5) LOG.info(_LI("Create docker volume {0} {1} from OpenSDS " "successfully").format(docker_volume_name, volume)) return volume
def volumedriver_get(): json_data = flask.request.get_json(force=True) app.logger.info(_LI("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'") app.logger.error(msg) raise exceptions.InvalidInput(msg) volume = get_docker_volume(docker_volume_name) if volume is not None: app.logger.info(_LI("Get docker volume: %s"), volume) return flask.jsonify(Volume=volume, Err=u'') app.logger.warning(_LW("Can't find volume %s from every " "provider"), docker_volume_name) return flask.jsonify(Err=u'Volume Not Found')
def check_exist(self, docker_volume_name): _, state = self._get_docker_volume(docker_volume_name) LOG.info(_LI("Get docker volume %(d_v)s with state %(st)s"), { 'd_v': docker_volume_name, 'st': state }) if state == UNKNOWN: return False return True
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( _LI("Get docker volume {0} {1} with state " "{2}").format(docker_volume_name, cinder_volume, state)) device_info = {} if state == ATTACH_TO_THIS: LOG.warn( _LW("The volume {0} {1} already exists and attached to " "this server").format(docker_volume_name, cinder_volume)) device_info = {'path': connector.get_device_path(cinder_volume)} elif state == NOT_ATTACH: LOG.warn( _LW("The volume {0} {1} is already exists but not " "attached").format(docker_volume_name, 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: msg = _LE("Volume already exists with fstype: {0}, but " "currently provided fstype is {1}, not " "match").format(vol_fstype, fstype) LOG.error(msg) raise exceptions.FuxiException('FSType Not Match') device_info = connector.connect_volume(cinder_volume) else: msg = _LE("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
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 = _LE("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( _LI("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 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 _clear_mountpoint(self, mountpoint): """Clear mount point directory if it wouldn't used any more. :param mountpoint: The path of Docker volume. """ if os.path.exists(mountpoint) and os.path.isdir(mountpoint): try: utils.execute('rm', '-r', mountpoint, run_as_root=True) LOG.info(_LI("Clear mountpoint %s successfully"), mountpoint) except processutils.ProcessExecutionError as e: LOG.error(_LE("Error happened when clear mountpoint {0}"), e) raise
def show(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 }) 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( _LI("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 = _LW("Can't find this volume '{0}' in " "Cinder").format(docker_volume_name) LOG.warning(msg) raise exceptions.NotFound(msg) else: msg = _LE("Volume '{0}' exists, but not attached to this volume," "and current state is {1}").format( docker_volume_name, state) raise exceptions.NotMatchedState(msg)
def connect_volume(self, share, **connect_opts): self._access_allow(share) conn_prop = { 'export': self.get_device_path(share), 'name': share.share_proto } path_info = self._get_brick_connector(share).connect_volume(conn_prop) LOG.info(_LI("Connect share %(s)s successfully, path_info %(pi)s"), { 's': share, 'pi': path_info }) return {'path': share.export_location}
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 = _LE("Volume size must be able to convert to int type") LOG.error(msg) raise exceptions.InvalidInput(msg) else: size = CONF.default_volume_size msg = _LI("Volume size doesn't provide from command, so use " "default size {0}G").format(size) LOG.info(msg) 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 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 _create_mountpoint(self, mountpoint): """Create mount point directory for Docker volume. :param mountpoint: The path of Docker volume. """ try: if not os.path.exists(mountpoint) or not os.path.isdir(mountpoint): utils.execute('mkdir', '-p', '-m=755', mountpoint, run_as_root=True) LOG.info(_LI("Create mountpoint %s successfully"), mountpoint) except processutils.ProcessExecutionError as e: LOG.error(_LE("Error happened when create volume " "directory. Error: %s"), e) raise
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 _connect_volume(self, volume): conn_info = self._get_connection_info(volume.id) protocol = conn_info['driver_volume_type'] brick_connector = brick_get_connector(protocol) device_info = brick_connector.connect_volume(conn_info['data']) LOG.info(_LI("Get device_info after connect to " "volume %s") % device_info) try: link_path = os.path.join(consts.VOLUME_LINK_DIR, volume.id) utils.execute('ln', '-s', os.path.realpath(device_info['path']), link_path, run_as_root=True) except processutils.ProcessExecutionError as e: LOG.error(_LE("Failed to create link for device. %s"), e) raise return {'path': link_path, 'iscsi_path': device_info['path']}
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 disconnect_volume(self, volume, **disconnect_opts): self._disconnect_volume(volume) attachments = volume.attachments attachment_uuid = None for am in attachments: if am['host_name'].lower() == utils.get_hostname().lower(): attachment_uuid = am['attachment_id'] break try: self.cinderclient.volumes.detach(volume.id, attachment_uuid=attachment_uuid) LOG.info(_LI("Disconnect volume successfully")) except cinder_exception.ClientException as e: msg = _LE("Error happened when detach volume {0} {1} from this " "server. Error: {2}").format(volume.name, volume, e) LOG.error(msg) raise
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(_LI("Get docker volume {0} {1} with state " "{2}").format(docker_volume_name, cinder_volume, state)) device_info = {} if state == ATTACH_TO_THIS: LOG.warn(_LW("The volume {0} {1} already exists and attached to " "this server").format(docker_volume_name, cinder_volume)) device_info = {'path': connector.get_device_path(cinder_volume)} elif state == NOT_ATTACH: LOG.warn(_LW("The volume {0} {1} is already exists but not " "attached").format(docker_volume_name, 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: msg = _LE("Volume already exists with fstype: {0}, but " "currently provided fstype is {1}, not " "match").format(vol_fstype, fstype) LOG.error(msg) raise exceptions.FuxiException('FSType Not Match') device_info = connector.connect_volume(cinder_volume) else: msg = _LE("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: volume_opts['name'] = docker_volume_name cinder_volume = self._create_volume(docker_volume_name, volume_opts) device_info = connector.connect_volume(cinder_volume) return device_info
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 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 plugin_activate(): app.logger.info(_LI("/Plugin.Activate")) return flask.jsonify(Implements=[u'VolumeDriver'])
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")
def volumedriver_unmount(): json_data = flask.request.get_json(force=True) app.logger.info(_LI('Receive JSON data {0} for ' 'VolumeDriver.Unmount').format(json_data)) return flask.jsonify(Err=u'')
def delete(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: link_path = self._get_connector().get_device_path(cinder_volume) if not link_path or not os.path.exists(link_path): msg = _LE( "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 = _LE("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 any more, # than delete it from Cinder. if not available_volume.attachments: msg = _LW("No other servers still use this volume {0} " "{1} any more, so delete it from Cinder" "").format(docker_volume_name, cinder_volume) LOG.warn(msg) self._delete_volume(available_volume) return True elif state == UNKNOWN: return False else: msg = _LE("The volume {0} {1} state must be {2} when " "remove it from this server, but current state " "is {3}").format(docker_volume_name, cinder_volume, ATTACH_TO_THIS, state) LOG.error(msg) raise exceptions.NotMatchedState(msg)