From 896ac7324fdc6e0abd7c2e2f8a04e00492201961 Mon Sep 17 00:00:00 2001 From: "Andrei V. Ostapenko" Date: Mon, 17 Feb 2014 16:30:04 +0200 Subject: [PATCH] Splits service_instance module from generic driver This module may be used by arbitrary share driver if it uses multitenancy approach of generic driver. Methods, responsible for managing nova instances and setting up network connectivity were taken out from generic driver to this module. Implements: bp generic-driver-modularity Change-Id: Iedf5eee4db716fd0c75ac3b49af975e0e2284ab2 --- manila/exception.py | 4 + manila/network/neutron/api.py | 19 + manila/share/drivers/generic.py | 570 ++------------------ manila/share/drivers/service_instance.py | 578 ++++++++++++++++++++ manila/tests/conf_fixture.py | 1 + manila/tests/test_service_instance.py | 657 +++++++++++++++++++++++ manila/tests/test_share_generic.py | 619 +-------------------- 7 files changed, 1334 insertions(+), 1114 deletions(-) create mode 100644 manila/share/drivers/service_instance.py create mode 100644 manila/tests/test_service_instance.py diff --git a/manila/exception.py b/manila/exception.py index 64e05dcccf..a49216a344 100644 --- a/manila/exception.py +++ b/manila/exception.py @@ -537,3 +537,7 @@ class InstanceNotFound(NotFound): class BridgeDoesNotExist(ManilaException): message = _("Bridge %(bridge)s does not exist.") + + +class ServiceInstanceException(ManilaException): + message = _("Exception in service instance manager occurred.") diff --git a/manila/network/neutron/api.py b/manila/network/neutron/api.py index bd2c89cd46..00a2687a31 100644 --- a/manila/network/neutron/api.py +++ b/manila/network/neutron/api.py @@ -144,6 +144,13 @@ class API(base.Base): raise exception.NetworkException(code=e.status_code, message=e.message) + def delete_subnet(self, subnet_id): + try: + self.client.delete_subnet(subnet_id) + except neutron_client_exc.NeutronClientException as e: + raise exception.NetworkException(code=e.status_code, + message=e.message) + def list_ports(self, **search_opts): """List ports for the client based on search options.""" return self.client.list_ports(**search_opts).get('ports') @@ -233,6 +240,18 @@ class API(base.Base): raise exception.NetworkException(code=e.status_code, message=e.message) + def router_remove_interface(self, router_id, subnet_id, port_id=None): + body = {} + if subnet_id: + body['subnet_id'] = subnet_id + if port_id: + body['port_id'] = port_id + try: + self.client.remove_interface_router(router_id, body) + except neutron_client_exc.NeutronClientException as e: + raise exception.NetworkException(code=e.status_code, + message=e.message) + def router_list(self): try: return self.client.list_routers().get('routers', {}) diff --git a/manila/share/drivers/generic.py b/manila/share/drivers/generic.py index 1da5544875..6c46249d5b 100644 --- a/manila/share/drivers/generic.py +++ b/manila/share/drivers/generic.py @@ -1,4 +1,4 @@ -# Copyright 2014 Mirantis Inc. +# Copyright (c) 2014 NetApp, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -12,96 +12,55 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. -""" -Generic Driver for shares. -""" +"""Generic Driver for shares.""" import ConfigParser -import netaddr import os import re import shutil -import socket -import threading import time +from oslo.config import cfg + from manila import compute from manila import context from manila import exception -from manila.network.linux import ip_lib -from manila.network.neutron import api as neutron from manila.openstack.common import importutils from manila.openstack.common import log as logging from manila.share import driver -from manila import utils +from manila.share.drivers import service_instance from manila import volume -from oslo.config import cfg LOG = logging.getLogger(__name__) share_opts = [ - cfg.StrOpt('service_image_name', - default='manila-service-image', - help="Name of image in glance, that will be used to create " - "service instance"), cfg.StrOpt('smb_template_config_path', default='$state_path/smb.conf', - help="Path to smb config"), + help="Path to smb config."), cfg.StrOpt('service_instance_name_template', default='manila_service_instance-%s', - help="Name of service instance"), - cfg.StrOpt('service_instance_user', - help="User in service instance"), - cfg.StrOpt('service_instance_password', - default=None, - help="Password to service instance user"), + help="Name of service instance."), cfg.StrOpt('volume_name_template', default='manila-share-%s', - help="Volume name template"), - cfg.StrOpt('manila_service_keypair_name', - default='manila-service', - help="Name of keypair that will be created and used " - "for service instance"), - cfg.StrOpt('path_to_public_key', - default='/home/stack/.ssh/id_rsa.pub', - help="Path to hosts public key"), - cfg.StrOpt('path_to_private_key', - default='/home/stack/.ssh/id_rsa', - help="Path to hosts private key"), + help="Volume name template."), cfg.StrOpt('volume_snapshot_name_template', default='manila-snapshot-%s', - help="Volume snapshot name template"), - cfg.IntOpt('max_time_to_build_instance', - default=300, - help="Maximum time to wait for creating service instance"), + help="Volume snapshot name template."), cfg.StrOpt('share_mount_path', default='/shares', help="Parent path in service instance where shares " - "will be mounted"), + "will be mounted."), cfg.IntOpt('max_time_to_create_volume', default=180, - help="Maximum time to wait for creating cinder volume"), + help="Maximum time to wait for creating cinder volume."), cfg.IntOpt('max_time_to_attach', default=120, - help="Maximum time to wait for attaching cinder volume"), - cfg.IntOpt('service_instance_flavor_id', - default=100, - help="ID of flavor, that will be used for service instance " - "creation"), + help="Maximum time to wait for attaching cinder volume."), cfg.StrOpt('service_instance_smb_config_path', default='$share_mount_path/smb.conf', - help="Path to smb config in service instance"), - cfg.StrOpt('service_network_name', - default='manila_service_network', - help="Name of manila service network"), - cfg.StrOpt('service_network_cidr', - default='10.254.0.0/16', - help="CIDR of manila service network"), - cfg.StrOpt('interface_driver', - default='manila.network.linux.interface.OVSInterfaceDriver', - help="Vif driver"), + help="Path to smb config in service instance."), cfg.ListOpt('share_helpers', default=[ 'CIFS=manila.share.drivers.generic.CIFSHelper', @@ -113,31 +72,8 @@ share_opts = [ CONF = cfg.CONF CONF.register_opts(share_opts) - -def synchronized(f): - """Decorates function with unique locks for each share network.""" - def wrapped_func(self, *args, **kwargs): - for arg in args: - share_network_id = getattr(arg, 'share_network_id', None) - if isinstance(arg, dict): - share_network_id = arg.get('share_network_id', None) - if share_network_id: - break - else: - raise exception.\ - ManilaException(_('Could not get share network id')) - with self.share_networks_locks.setdefault(share_network_id, - threading.RLock()): - return f(self, *args, **kwargs) - return wrapped_func - - -def _ssh_exec(server, command): - """Executes ssh commands and checks/restores ssh connection.""" - if not server['ssh'].get_transport().is_active(): - server['ssh_pool'].remove(server['ssh']) - server['ssh'] = server['ssh_pool'].create() - return utils.ssh_execute(server['ssh'], ' '.join(command)) +_ssh_exec = service_instance._ssh_exec +synchronized = service_instance.synchronized class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): @@ -149,59 +85,31 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): self.admin_context = context.get_admin_context() self.db = db self.configuration.append_config_values(share_opts) - self._helpers = None + self._helpers = {} def check_for_setup_error(self): """Returns an error if prerequisites aren't met.""" - if not self.configuration.service_instance_user: - raise exception.ManilaException(_('Service instance user is not ' - 'specified')) + pass def do_setup(self, context): """Any initialization the generic driver does while starting.""" super(GenericShareDriver, self).do_setup(context) self.compute_api = compute.API() self.volume_api = volume.API() - self.neutron_api = neutron.API() - self.share_networks_locks = {} - self.share_networks_servers = {} - attempts = 5 - while attempts: - try: - self.service_tenant_id = self.neutron_api.admin_tenant_id - break - except exception.NetworkException: - LOG.debug(_('Connection to neutron failed')) - attempts -= 1 - time.sleep(3) - else: - raise exception.\ - ManilaException(_('Can not receive service tenant id')) - self.service_network_id = self._get_service_network() - self.vif_driver = importutils.\ - import_class(self.configuration.interface_driver)() - self._setup_connectivity_with_service_instances() + self.service_instance_manager = service_instance.\ + ServiceInstanceManager(self.db, self._helpers) + self.get_service_instance = self.service_instance_manager.\ + get_service_instance + self.share_networks_locks = self.service_instance_manager.\ + share_networks_locks + self.share_networks_servers = self.service_instance_manager.\ + share_networks_servers self._setup_helpers() - def _get_service_network(self): - """Finds existing or creates new service network.""" - service_network_name = self.configuration.service_network_name - networks = [network for network in self.neutron_api. - get_all_tenant_networks(self.service_tenant_id) - if network['name'] == service_network_name] - if len(networks) > 1: - raise exception.ManilaException(_('Ambiguous service networks')) - elif not networks: - return self.neutron_api.network_create(self.service_tenant_id, - service_network_name)['id'] - else: - return networks[0]['id'] - def _setup_helpers(self): """Initializes protocol-specific NAS drivers.""" - self._helpers = {} for helper_str in self.configuration.share_helpers: - share_proto, _, import_str = helper_str.partition('=') + share_proto, __, import_str = helper_str.partition('=') helper = importutils.import_class(import_str) self._helpers[share_proto.upper()] = helper(self._execute, self.configuration, @@ -210,9 +118,10 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): def create_share(self, context, share): """Creates share.""" if share['share_network_id'] is None: - raise exception.\ - ManilaException(_('Share Network is not specified')) - server = self._get_service_instance(self.admin_context, share) + raise exception.ManilaException( + _('Share Network is not specified')) + server = self.get_service_instance(self.admin_context, + share_network_id=share['share_network_id']) volume = self._allocate_container(context, share) volume = self._attach_volume(context, share, server, volume) self._format_device(server, volume) @@ -314,8 +223,8 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): if len(volume_snapshot_list) == 1: volume_snapshot = volume_snapshot_list[0] elif len(volume_snapshot_list) > 1: - raise exception.\ - ManilaException(_('Error. Ambiguous volume snaphots')) + raise exception.ManilaException( + _('Error. Ambiguous volume snaphots')) return volume_snapshot @synchronized @@ -341,382 +250,16 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): % self.configuration.max_time_to_attach) def _get_device_path(self, context, server): - """Returns device path, that will be used for cinder volume attaching. - """ + """Returns device path for cinder volume attaching.""" volumes = self.compute_api.instance_volumes_list(context, server['id']) used_literals = set(volume.device[-1] for volume in volumes if '/dev/vd' in volume.device) lit = 'b' while lit in used_literals: lit = chr(ord(lit) + 1) - device_name = '/dev/vd' + lit + device_name = '/dev/vd%s' % lit return device_name - def _get_service_instance_name(self, share): - """Returns service vms name.""" - return self.configuration.service_instance_name_template % \ - share['share_network_id'] - - def _get_server_ip(self, server): - """Returns service vms ip address.""" - net = server['networks'] - try: - net_ips = net[self.configuration.service_network_name] - return net_ips[0] - except KeyError: - msg = _('Service vm is not attached to %s network') - except IndexError: - msg = _('Service vm has no ips on %s network') - msg = msg % self.configuration.service_network_name - LOG.error(msg) - raise exception.ManilaException(msg) - - def _ensure_or_delete_server(self, context, server, update=False): - """Ensures that server exists and active, otherwise deletes it.""" - if update: - try: - server.update(self.compute_api.server_get(context, - server['id'])) - except exception.InstanceNotFound as e: - LOG.debug(e) - return False - if server['status'] == 'ACTIVE': - if self._check_server_availability(server): - return True - - self._delete_server(context, server) - return False - - def _delete_server(self, context, server): - """Deletes the server.""" - self.compute_api.server_delete(context, server['id']) - t = time.time() - while time.time() - t < self.configuration.\ - max_time_to_build_instance: - try: - server = self.compute_api.server_get(context, - server['id']) - except exception.InstanceNotFound: - LOG.debug(_('Service instance was deleted succesfully')) - break - time.sleep(1) - else: - raise exception.ManilaException(_('Instance have not been deleted ' - 'in %ss. Giving up') % - self.configuration.max_time_to_build_instance) - - @synchronized - def _get_service_instance(self, context, share, create=True): - """Finds or creates and setups service vm.""" - server = self.share_networks_servers.get(share['share_network_id'], {}) - old_server_ip = server.get('ip', None) - if server and self._ensure_or_delete_server(context, - server, - update=True): - return server - else: - server = {} - service_instance_name = self._get_service_instance_name(share) - search_opts = {'name': service_instance_name} - servers = self.compute_api.server_list(context, search_opts, True) - if len(servers) == 1: - server = servers[0] - server['ip'] = self._get_server_ip(server) - old_server_ip = server['ip'] - if not self._ensure_or_delete_server(context, server): - server.clear() - elif len(servers) > 1: - raise exception.\ - ManilaException(_('Ambiguous service instances')) - if not server and create: - server = self._create_service_instance(context, - service_instance_name, - share, old_server_ip) - if server: - server['share_network_id'] = share['share_network_id'] - server['ip'] = self._get_server_ip(server) - server['ssh_pool'] = self._get_ssh_pool(server) - server['ssh'] = server['ssh_pool'].create() - for helper in self._helpers.values(): - helper.init_helper(server) - - self.share_networks_servers[share['share_network_id']] = server - return server - - def _get_ssh_pool(self, server): - """Returns ssh connection pool for service vm.""" - ssh_pool = utils.SSHPool(server['ip'], 22, None, - self.configuration.service_instance_user, - password=self.configuration.service_instance_password, - privatekey=self.configuration.path_to_private_key, - max_size=1) - return ssh_pool - - def _get_key(self, context): - """Returns name of key, that will be injected to service vm.""" - if not self.configuration.path_to_public_key or \ - not self.configuration.path_to_private_key: - return - path_to_public_key = \ - os.path.expanduser(self.configuration.path_to_public_key) - path_to_private_key = \ - os.path.expanduser(self.configuration.path_to_private_key) - if not os.path.exists(path_to_public_key) or \ - not os.path.exists(path_to_private_key): - return - keypair_name = self.configuration.manila_service_keypair_name - keypairs = [k for k in self.compute_api.keypair_list(context) - if k.name == keypair_name] - if len(keypairs) > 1: - raise exception.ManilaException(_('Ambiguous keypairs')) - - public_key, _ = self._execute('cat', - path_to_public_key, - run_as_root=True) - if not keypairs: - keypair = self.compute_api.keypair_import(context, keypair_name, - public_key) - else: - keypair = keypairs[0] - if keypair.public_key != public_key: - LOG.debug('Public key differs from existing keypair. ' - 'Creating new keypair') - self.compute_api.keypair_delete(context, keypair.id) - keypair = self.compute_api.keypair_import(context, - keypair_name, - public_key) - return keypair.name - - def _get_service_image(self, context): - """Returns ID of service image, that will be used for service vm - creating. - """ - images = [image.id for image in self.compute_api.image_list(context) - if image.name == self.configuration.service_image_name] - if len(images) == 1: - return images[0] - elif not images: - raise exception.\ - ManilaException(_('No appropriate image was found')) - else: - raise exception.ManilaException(_('Ambiguous image name')) - - def _create_service_instance(self, context, instance_name, share, - old_server_ip): - """Creates service vm and sets up networking for it.""" - service_image_id = self._get_service_image(context) - key_name = self._get_key(context) - if not self.configuration.service_instance_password and not key_name: - raise exception.ManilaException(_('Neither service instance' - 'password nor key are available')) - - port = self._setup_network_for_instance(context, share, old_server_ip) - try: - self._setup_connectivity_with_service_instances() - except Exception as e: - LOG.debug(e) - self.neutron_api.delete_port(port['id']) - raise - service_instance = self.compute_api.server_create(context, - instance_name, service_image_id, - self.configuration.service_instance_flavor_id, - key_name, None, None, - nics=[{'port-id': port['id']}]) - - t = time.time() - while time.time() - t < self.configuration.max_time_to_build_instance: - if service_instance['status'] == 'ACTIVE': - break - if service_instance['status'] == 'ERROR': - raise exception.ManilaException(_('Failed to build service ' - 'instance')) - time.sleep(1) - try: - service_instance = self.compute_api.server_get(context, - service_instance['id']) - except exception.InstanceNotFound as e: - LOG.debug(e) - else: - raise exception.ManilaException(_('Instance have not been spawned ' - 'in %ss. Giving up') % - self.configuration.max_time_to_build_instance) - - service_instance['ip'] = self._get_server_ip(service_instance) - if not self._check_server_availability(service_instance): - raise exception.ManilaException(_('SSH connection have not been ' - 'established in %ss. Giving up') - % self.configuration.max_time_to_build_instance) - return service_instance - - def _check_server_availability(self, server): - t = time.time() - while time.time() - t < self.configuration.max_time_to_build_instance: - LOG.debug('Checking service vm availablity') - try: - socket.socket().connect((server['ip'], 22)) - LOG.debug(_('Service vm is available via ssh.')) - return True - except socket.error as e: - LOG.debug(e) - LOG.debug(_('Server is not available through ssh. Waiting...')) - time.sleep(5) - return False - - def _setup_network_for_instance(self, context, share, old_server_ip): - """Setups network for service vm.""" - service_network = self.neutron_api.get_network(self.service_network_id) - all_service_subnets = [self.neutron_api.get_subnet(subnet_id) - for subnet_id in service_network['subnets']] - service_subnets = [subnet for subnet in all_service_subnets - if subnet['name'] == share['share_network_id']] - if len(service_subnets) > 1: - raise exception.ManilaException(_('Ambiguous subnets')) - elif not service_subnets: - service_subnet = \ - self.neutron_api.subnet_create(self.service_tenant_id, - self.service_network_id, - share['share_network_id'], - self._get_cidr_for_subnet(all_service_subnets)) - else: - service_subnet = service_subnets[0] - - share_network = self.db.share_network_get(context, - share['share_network_id']) - private_router = self._get_private_router(share_network) - try: - self.neutron_api.router_add_interface(private_router['id'], - service_subnet['id']) - except exception.NetworkException as e: - if 'already has' not in e.msg: - raise - LOG.debug(_('Subnet %(subnet_id)s is already attached to the ' - 'router %(router_id)s') % - {'subnet_id': service_subnet['id'], - 'router_id': private_router['id']}) - - return self.neutron_api.create_port(self.service_tenant_id, - self.service_network_id, - subnet_id=service_subnet['id'], - fixed_ip=old_server_ip, - device_owner='manila') - - def _get_private_router(self, share_network): - """Returns router attached to private subnet gateway.""" - private_subnet = self.neutron_api.\ - get_subnet(share_network['neutron_subnet_id']) - if not private_subnet['gateway_ip']: - raise exception.ManilaException(_('Subnet must have gateway')) - private_network_ports = [p for p in self.neutron_api.list_ports( - network_id=share_network['neutron_net_id'])] - for p in private_network_ports: - fixed_ip = p['fixed_ips'][0] - if fixed_ip['subnet_id'] == private_subnet['id'] and \ - fixed_ip['ip_address'] == private_subnet['gateway_ip']: - private_subnet_gateway_port = p - break - else: - raise exception.ManilaException(_('Subnet gateway is not attached ' - 'the router')) - private_subnet_router = self.neutron_api.show_router( - private_subnet_gateway_port['device_id']) - return private_subnet_router - - def _setup_connectivity_with_service_instances(self): - """Setups connectivity with service instances by creating port - in service network, creating and setting up required network devices. - """ - port = self._setup_service_port() - interface_name = self.vif_driver.get_device_name(port) - self.vif_driver.plug(port['id'], interface_name, port['mac_address']) - ip_cidrs = [] - for fixed_ip in port['fixed_ips']: - subnet = self.neutron_api.get_subnet(fixed_ip['subnet_id']) - net = netaddr.IPNetwork(subnet['cidr']) - ip_cidr = '%s/%s' % (fixed_ip['ip_address'], net.prefixlen) - ip_cidrs.append(ip_cidr) - - self.vif_driver.init_l3(interface_name, ip_cidrs) - - # ensure that interface is first in the list - device = ip_lib.IPDevice(interface_name) - device.route.pullup_route(interface_name) - - # here we are checking for garbage devices from removed service port - self._clean_garbage(device) - - def _clean_garbage(self, device): - """Finds and removes network device, that was associated with deleted - service port. - """ - list_dev = [] - for dev in ip_lib.IPWrapper().get_devices(): - if dev.name != device.name and dev.name[:3] == device.name[:3]: - cidr_set = set() - for a in dev.addr.list(): - if a['ip_version'] == 4: - cidr_set.add(str(netaddr.IPNetwork(a['cidr']).cidr)) - list_dev.append((dev.name, cidr_set)) - device_cidr_set = set(str(netaddr.IPNetwork(a['cidr']).cidr) - for a in device.addr.list() - if a['ip_version'] == 4) - - for dev_name, cidr_set in list_dev: - if device_cidr_set & cidr_set: - self.vif_driver.unplug(dev_name) - - def _setup_service_port(self): - """Find or creates neutron port, that will be used for connectivity - with service instances. - """ - ports = [port for port in self.neutron_api. - list_ports(device_id='manila-share')] - if len(ports) > 1: - raise exception.\ - ManilaException(_('Error. Ambiguous service ports')) - elif not ports: - try: - stdout, stderr = self._execute('hostname') - host = stdout.strip() - except exception.ProcessExecutionError as e: - msg = _('Unable to get host. %s') % e.stderr - raise exception.ManilaException(msg) - port = self.neutron_api.create_port(self.service_tenant_id, - self.service_network_id, - device_id='manila-share', - device_owner='manila:generic_driver', - host_id=host) - else: - port = ports[0] - - network = self.neutron_api.get_network(self.service_network_id) - subnets = set(network['subnets']) - port_fixed_ips = [] - for fixed_ip in port['fixed_ips']: - port_fixed_ips.append({'subnet_id': fixed_ip['subnet_id'], - 'ip_address': fixed_ip['ip_address']}) - if fixed_ip['subnet_id'] in subnets: - subnets.remove(fixed_ip['subnet_id']) - - # If there are subnets here that means that - # we need to add those to the port and call update. - if subnets: - port_fixed_ips.extend([dict(subnet_id=s) for s in subnets]) - port = self.neutron_api.update_port_fixed_ips( - port['id'], {'fixed_ips': port_fixed_ips}) - - return port - - def _get_cidr_for_subnet(self, subnets): - """Returns not used cidr for service subnet creating.""" - used_cidrs = set(subnet['cidr'] for subnet in subnets) - serv_cidr = netaddr.IPNetwork(self.configuration.service_network_cidr) - for subnet in serv_cidr.subnet(29): - cidr = str(subnet.cidr) - if cidr not in used_cidrs: - return cidr - else: - raise exception.ManilaException(_('No available cidrs')) - def _allocate_container(self, context, share, snapshot=None): """Creates cinder volume, associated to share by name.""" volume_snapshot = None @@ -748,8 +291,8 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): if volume: self.volume_api.delete(context, volume['id']) t = time.time() - while time.time() - t < self.configuration.\ - max_time_to_create_volume: + while (time.time() - t < + self.configuration.max_time_to_create_volume): try: volume = self.volume_api.get(context, volume['id']) except exception.VolumeNotFound: @@ -785,15 +328,16 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): data['total_capacity_gb'] = 'infinite' data['free_capacity_gb'] = 'infinite' - data['reserved_percentage'] = \ - self.configuration.reserved_share_percentage + data['reserved_percentage'] = (self.configuration. + reserved_share_percentage) data['QoS_support'] = False self._stats = data def create_share_from_snapshot(self, context, share, snapshot): """Is called to create share from snapshot.""" - server = self._get_service_instance(self.admin_context, share) + server = self.get_service_instance(self.admin_context, + share_network_id=share['share_network_id']) volume = self._allocate_container(context, share, snapshot) volume = self._attach_volume(context, share, server, volume) self._mount_device(context, share, server, volume) @@ -805,8 +349,9 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): """Deletes share.""" if not share['share_network_id']: return - server = self._get_service_instance(self.admin_context, - share, create=False) + server = self.get_service_instance(self.admin_context, + share_network_id=share['share_network_id'], + create=False) if server: self._get_helper(share).remove_export(server, share['name']) self._unmount_device(context, share, server) @@ -816,8 +361,8 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): def create_snapshot(self, context, snapshot): """Creates a snapshot.""" volume = self._get_volume(context, snapshot['share_id']) - volume_snapshot_name = self.configuration.\ - volume_snapshot_name_template % snapshot['id'] + volume_snapshot_name = (self.configuration. + volume_snapshot_name_template % snapshot['id']) volume_snapshot = self.volume_api.create_snapshot_force(context, volume['id'], volume_snapshot_name, @@ -859,7 +404,8 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): def ensure_share(self, context, share): """Ensure that storage are mounted and exported.""" - server = self._get_service_instance(context, share) + server = self.get_service_instance(context, + share_network_id=share['share_network_id']) volume = self._get_volume(context, share['id']) volume = self._attach_volume(context, share, server, volume) self._mount_device(context, share, server, volume) @@ -867,9 +413,9 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): def allow_access(self, context, share, access): """Allow access to the share.""" - server = self._get_service_instance(self.admin_context, - share, - create=False) + server = self.get_service_instance(self.admin_context, + share_network_id=share['share_network_id'], + create=False) if not server: raise exception.ManilaException('Server not found. Try to ' 'restart manila share service') @@ -881,9 +427,9 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): """Deny access to the share.""" if not share['share_network_id']: return - server = self._get_service_instance(self.admin_context, - share, - create=False) + server = self.get_service_instance(self.admin_context, + share_network_id=share['share_network_id'], + create=False) if server: self._get_helper(share).deny_access(server, share['name'], access['access_type'], @@ -1009,8 +555,7 @@ class CIFSHelper(NASHelperBase): local_config = self._create_local_config(server['share_network_id']) config_dir = os.path.dirname(self.config_path) try: - _ssh_exec(server, ['sudo', 'mkdir', - config_dir]) + _ssh_exec(server, ['sudo', 'mkdir', config_dir]) except exception.ProcessExecutionError as e: if 'File exists' not in e.stderr: raise @@ -1068,12 +613,12 @@ class CIFSHelper(NASHelperBase): self._update_config(parser, config) self._write_remote_config(config, server) _ssh_exec(server, ['sudo', 'smbcontrol', 'all', 'close-share', - share_name]) + share_name]) @synchronized def _write_remote_config(self, config, server): with open(config, 'r') as f: - cfg = "'" + f.read() + "'" + cfg = "'%s'" % f.read() _ssh_exec(server, ['echo %s > %s' % (cfg, self.config_path)]) def allow_access(self, server, share_name, access_type, access): @@ -1131,8 +676,7 @@ class CIFSHelper(NASHelperBase): #Check that configuration is correct with open(self.test_config, 'w') as fp: parser.write(fp) - self._execute('testparm', '-s', self.test_config, - check_exit_code=True) + self._execute('testparm', '-s', self.test_config, check_exit_code=True) #save it with open(config, 'w') as fp: parser.write(fp) diff --git a/manila/share/drivers/service_instance.py b/manila/share/drivers/service_instance.py new file mode 100644 index 0000000000..2c08973ca7 --- /dev/null +++ b/manila/share/drivers/service_instance.py @@ -0,0 +1,578 @@ +# Copyright (c) 2014 NetApp, Inc. +# All Rights Reserved. +# +# 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. + +"""Module for managing nova instances for share drivers.""" + +import netaddr +import os +import socket +import threading +import time + +from oslo.config import cfg + +from manila import compute +from manila import context +from manila import exception +from manila.network.linux import ip_lib +from manila.network.neutron import api as neutron +from manila.openstack.common import importutils +from manila.openstack.common import log as logging +from manila import utils + + +LOG = logging.getLogger(__name__) + +server_opts = [ + cfg.StrOpt('service_image_name', + default='manila-service-image', + help="Name of image in glance, that will be used to create " + "service instance."), + cfg.StrOpt('service_instance_name_template', + default='manila_service_instance-%s', + help="Name of service instance."), + cfg.StrOpt('service_instance_user', + help="User in service instance."), + cfg.StrOpt('service_instance_password', + default=None, + help="Password to service instance user."), + cfg.StrOpt('manila_service_keypair_name', + default='manila-service', + help="Name of keypair that will be created and used " + "for service instance."), + cfg.StrOpt('path_to_public_key', + default='~/.ssh/id_rsa.pub', + help="Path to hosts public key."), + cfg.StrOpt('path_to_private_key', + default='~/.ssh/id_rsa', + help="Path to hosts private key."), + cfg.IntOpt('max_time_to_build_instance', + default=300, + help="Maximum time to wait for creating service instance."), + cfg.IntOpt('service_instance_flavor_id', + default=100, + help="ID of flavor, that will be used for service instance " + "creation."), + cfg.StrOpt('service_network_name', + default='manila_service_network', + help="Name of manila service network."), + cfg.StrOpt('service_network_cidr', + default='10.254.0.0/16', + help="CIDR of manila service network."), + cfg.StrOpt('interface_driver', + default='manila.network.linux.interface.OVSInterfaceDriver', + help="Vif driver."), +] + +CONF = cfg.CONF +CONF.register_opts(server_opts) + + +def synchronized(f): + """Decorates function with unique locks for each share network. + + Share network id must be provided either as value/attribute + of one of args or as named argument. + """ + def wrapped_func(self, *args, **kwargs): + share_network_id = kwargs.get('share_network_id', None) + if not share_network_id: + for arg in args: + share_network_id = getattr(arg, 'share_network_id', None) + if isinstance(arg, dict): + share_network_id = arg.get('share_network_id', None) + if share_network_id: + break + else: + raise exception.ServiceInstanceException(_('Could not get ' + 'share network id')) + with self.share_networks_locks.setdefault(share_network_id, + threading.RLock()): + return f(self, *args, **kwargs) + return wrapped_func + + +def _ssh_exec(server, command): + """Executes ssh commands and checks/restores ssh connection.""" + if not server['ssh'].get_transport().is_active(): + server['ssh_pool'].remove(server['ssh']) + server['ssh'] = server['ssh_pool'].create() + return utils.ssh_execute(server['ssh'], ' '.join(command)) + + +class ServiceInstanceManager(object): + """Manages nova instances for various share drivers.""" + + def __init__(self, db, _helpers, *args, **kwargs): + """Do initialization.""" + super(ServiceInstanceManager, self).__init__(*args, **kwargs) + if not CONF.service_instance_user: + raise exception.ServiceInstanceException(_('Service instance user ' + 'is not specified')) + self.admin_context = context.get_admin_context() + self._execute = utils.execute + self.compute_api = compute.API() + self.neutron_api = neutron.API() + self._helpers = _helpers + self.db = db + attempts = 5 + while attempts: + try: + self.service_tenant_id = self.neutron_api.admin_tenant_id + break + except exception.NetworkException: + LOG.debug(_('Connection to neutron failed.')) + attempts -= 1 + time.sleep(3) + else: + raise exception.ServiceInstanceException(_('Can not receive ' + 'service tenant id.')) + self.share_networks_locks = {} + self.share_networks_servers = {} + self.service_network_id = self._get_service_network() + self.vif_driver = importutils.import_class(CONF.interface_driver)() + self._setup_connectivity_with_service_instances() + + def _get_service_network(self): + """Finds existing or creates new service network.""" + service_network_name = CONF.service_network_name + networks = [network for network in self.neutron_api. + get_all_tenant_networks(self.service_tenant_id) + if network['name'] == service_network_name] + if len(networks) > 1: + raise exception.ServiceInstanceException(_('Ambiguous service ' + 'networks.')) + elif not networks: + return self.neutron_api.network_create(self.service_tenant_id, + service_network_name)['id'] + else: + return networks[0]['id'] + + def _get_service_instance_name(self, share_network_id): + """Returns service vms name.""" + return CONF.service_instance_name_template % share_network_id + + def _get_server_ip(self, server): + """Returns service vms ip address.""" + net = server['networks'] + try: + net_ips = net[CONF.service_network_name] + return net_ips[0] + except KeyError: + msg = _('Service vm is not attached to %s network.') + except IndexError: + msg = _('Service vm has no ips on %s network.') + msg = msg % CONF.service_network_name + LOG.error(msg) + raise exception.ServiceInstanceException(msg) + + def _ensure_server(self, context, server, update=False): + """Ensures that server exists and active, otherwise deletes it.""" + if not server: + return False + if update: + try: + server.update(self.compute_api.server_get(context, + server['id'])) + except exception.InstanceNotFound as e: + LOG.debug(e) + return False + if server['status'] == 'ACTIVE': + if self._check_server_availability(server): + return True + + return False + + def _delete_server(self, context, server): + """Deletes the server.""" + if not server: + return + self.compute_api.server_delete(context, server['id']) + t = time.time() + while time.time() - t < CONF.max_time_to_build_instance: + try: + server = self.compute_api.server_get(context, server['id']) + except exception.InstanceNotFound: + LOG.debug(_('Service instance was deleted succesfully.')) + break + time.sleep(1) + else: + raise exception.ServiceInstanceException(_('Instance have not ' + 'been deleted in %ss. Giving up.') % + CONF.max_time_to_build_instance) + + @synchronized + def get_service_instance(self, context, share_network_id, create=True): + """Finds or creates and sets up service vm.""" + server = self.share_networks_servers.get(share_network_id, {}) + old_server_ip = server.get('ip', None) + if self._ensure_server(context, server, update=True): + return server + else: + self._delete_server(context, server) + server = {} + service_instance_name = self._get_service_instance_name( + share_network_id) + search_opts = {'name': service_instance_name} + servers = self.compute_api.server_list(context, search_opts, True) + if len(servers) == 1: + server = servers[0] + server['ip'] = self._get_server_ip(server) + old_server_ip = server['ip'] + if not self._ensure_server(context, server): + self._delete_server(context, server) + server.clear() + elif len(servers) > 1: + raise exception.ServiceInstanceException( + _('Error. Ambiguous service instances.')) + if not server and create: + server = self._create_service_instance(context, + service_instance_name, + share_network_id, + old_server_ip) + if server: + server['share_network_id'] = share_network_id + server['ip'] = self._get_server_ip(server) + server['ssh_pool'] = self._get_ssh_pool(server) + server['ssh'] = server['ssh_pool'].create() + for helper in self._helpers.values(): + helper.init_helper(server) + + self.share_networks_servers[share_network_id] = server + return server + + def _get_ssh_pool(self, server): + """Returns ssh connection pool for service vm.""" + ssh_pool = utils.SSHPool(server['ip'], 22, None, + CONF.service_instance_user, + password=CONF.service_instance_password, + privatekey=CONF.path_to_private_key, + max_size=1) + return ssh_pool + + def _get_key(self, context): + """Returns name of key, that will be injected to service vm.""" + if not CONF.path_to_public_key or not CONF.path_to_private_key: + return + path_to_public_key = os.path.expanduser(CONF.path_to_public_key) + path_to_private_key = os.path.expanduser(CONF.path_to_private_key) + if (not os.path.exists(path_to_public_key) or + not os.path.exists(path_to_private_key)): + return + keypair_name = CONF.manila_service_keypair_name + keypairs = [k for k in self.compute_api.keypair_list(context) + if k.name == keypair_name] + if len(keypairs) > 1: + raise exception.ServiceInstanceException(_('Ambiguous keypairs.')) + + public_key, __ = self._execute('cat', path_to_public_key) + if not keypairs: + keypair = self.compute_api.keypair_import(context, + keypair_name, + public_key) + else: + keypair = keypairs[0] + if keypair.public_key != public_key: + LOG.debug(_('Public key differs from existing keypair. ' + 'Creating new keypair.')) + self.compute_api.keypair_delete(context, keypair.id) + keypair = self.compute_api.keypair_import(context, + keypair_name, + public_key) + return keypair.name + + def _get_service_image(self, context): + """Returns ID of service image for service vm creating.""" + images = [image.id for image in self.compute_api.image_list(context) + if image.name == CONF.service_image_name] + if len(images) == 1: + return images[0] + elif not images: + raise exception.ServiceInstanceException(_('No appropriate ' + 'image was found.')) + else: + raise exception.ServiceInstanceException( + _('Ambiguous image name.')) + + def _create_service_instance(self, context, instance_name, + share_network_id, old_server_ip): + """Creates service vm and sets up networking for it.""" + service_image_id = self._get_service_image(context) + key_name = self._get_key(context) + if not CONF.service_instance_password and not key_name: + raise exception.ServiceInstanceException( + _('Neither service instance password nor key are available.')) + + port = self._setup_network_for_instance(context, + share_network_id, + old_server_ip) + try: + self._setup_connectivity_with_service_instances() + except Exception as e: + LOG.debug(e) + self.neutron_api.delete_port(port['id']) + raise + service_instance = self.compute_api.server_create(context, + instance_name, + service_image_id, + CONF.service_instance_flavor_id, + key_name, + None, + None, + nics=[{'port-id': port['id']}]) + + t = time.time() + while time.time() - t < CONF.max_time_to_build_instance: + if service_instance['status'] == 'ACTIVE': + break + if service_instance['status'] == 'ERROR': + raise exception.ServiceInstanceException( + _('Failed to build service instance.')) + time.sleep(1) + try: + service_instance = self.compute_api.server_get(context, + service_instance['id']) + except exception.InstanceNotFound as e: + LOG.debug(e) + else: + raise exception.ServiceInstanceException( + _('Instance have not been spawned in %ss. Giving up.') % + CONF.max_time_to_build_instance) + + service_instance['ip'] = self._get_server_ip(service_instance) + if not self._check_server_availability(service_instance): + raise exception.ServiceInstanceException( + _('SSH connection have not been ' + 'established in %ss. Giving up.') % + CONF.max_time_to_build_instance) + return service_instance + + def _check_server_availability(self, server): + t = time.time() + while time.time() - t < CONF.max_time_to_build_instance: + LOG.debug(_('Checking service vm availablity.')) + try: + socket.socket().connect((server['ip'], 22)) + LOG.debug(_('Service vm is available via ssh.')) + return True + except socket.error as e: + LOG.debug(e) + LOG.debug(_('Server is not available through ssh. Waiting...')) + time.sleep(5) + return False + + def _setup_network_for_instance(self, context, share_network_id, + old_server_ip): + """Sets up network for service vm.""" + service_subnet = self._get_service_subnet(share_network_id) + if not service_subnet: + service_subnet = self.neutron_api.subnet_create( + self.service_tenant_id, + self.service_network_id, + share_network_id, + self._get_cidr_for_subnet()) + + private_router = self._get_private_router(share_network_id) + try: + self.neutron_api.router_add_interface(private_router['id'], + service_subnet['id']) + except exception.NetworkException as e: + if e.kwargs['code'] != 400: + raise + LOG.debug(_('Subnet %(subnet_id)s is already attached to the ' + 'router %(router_id)s.') % + {'subnet_id': service_subnet['id'], + 'router_id': private_router['id']}) + + return self.neutron_api.create_port(self.service_tenant_id, + self.service_network_id, + subnet_id=service_subnet['id'], + fixed_ip=old_server_ip, + device_owner='manila') + + def _get_private_router(self, share_network_id): + """Returns router attached to private subnet gateway.""" + share_network = self.db.share_network_get(self.admin_context, + share_network_id) + private_subnet = self.neutron_api.get_subnet( + share_network['neutron_subnet_id']) + if not private_subnet['gateway_ip']: + raise exception.ServiceInstanceException( + _('Subnet must have gateway.')) + private_network_ports = [p for p in self.neutron_api.list_ports( + network_id=share_network['neutron_net_id'])] + for p in private_network_ports: + fixed_ip = p['fixed_ips'][0] + if (fixed_ip['subnet_id'] == private_subnet['id'] and + fixed_ip['ip_address'] == private_subnet['gateway_ip']): + private_subnet_gateway_port = p + break + else: + raise exception.ServiceInstanceException( + _('Subnet gateway is not attached the router.')) + private_subnet_router = self.neutron_api.show_router( + private_subnet_gateway_port['device_id']) + return private_subnet_router + + def _setup_connectivity_with_service_instances(self): + """Sets up connectivity with service instances. + + Creates creating port in service network, creating and setting up + required network devices. + """ + port = self._get_service_port() + port = self._add_fixed_ips_to_service_port(port) + interface_name = self.vif_driver.get_device_name(port) + self.vif_driver.plug(port['id'], interface_name, port['mac_address']) + ip_cidrs = [] + for fixed_ip in port['fixed_ips']: + subnet = self.neutron_api.get_subnet(fixed_ip['subnet_id']) + net = netaddr.IPNetwork(subnet['cidr']) + ip_cidr = '%s/%s' % (fixed_ip['ip_address'], net.prefixlen) + ip_cidrs.append(ip_cidr) + + self.vif_driver.init_l3(interface_name, ip_cidrs) + + # ensure that interface is first in the list + device = ip_lib.IPDevice(interface_name) + device.route.pullup_route(interface_name) + + # here we are checking for garbage devices from removed service port + self._remove_outdated_interfaces(device) + + def _remove_outdated_interfaces(self, device): + """Finds and removes unused network device.""" + list_dev = [] + for dev in ip_lib.IPWrapper().get_devices(): + if dev.name != device.name and dev.name[:3] == device.name[:3]: + cidr_set = set() + for a in dev.addr.list(): + if a['ip_version'] == 4: + cidr_set.add(str(netaddr.IPNetwork(a['cidr']).cidr)) + list_dev.append((dev.name, cidr_set)) + device_cidr_set = set(str(netaddr.IPNetwork(a['cidr']).cidr) + for a in device.addr.list() + if a['ip_version'] == 4) + + for dev_name, cidr_set in list_dev: + if device_cidr_set & cidr_set: + self.vif_driver.unplug(dev_name) + + def _get_service_port(self): + """Find or creates service neutron port. + + This port will be used for connectivity with service instances. + """ + ports = [port for port in self.neutron_api. + list_ports(device_id='manila-share')] + if len(ports) > 1: + raise exception.ServiceInstanceException( + _('Error. Ambiguous service ports.')) + elif not ports: + try: + stdout, stderr = self._execute('hostname') + host = stdout.strip() + except exception.ProcessExecutionError as e: + msg = _('Unable to get host. %s') % e.stderr + raise exception.ManilaException(msg) + port = self.neutron_api.create_port(self.service_tenant_id, + self.service_network_id, + device_id='manila-share', + device_owner='manila:share', + host_id=host) + else: + port = ports[0] + return port + + def _add_fixed_ips_to_service_port(self, port): + network = self.neutron_api.get_network(self.service_network_id) + subnets = set(network['subnets']) + port_fixed_ips = [] + for fixed_ip in port['fixed_ips']: + port_fixed_ips.append({'subnet_id': fixed_ip['subnet_id'], + 'ip_address': fixed_ip['ip_address']}) + if fixed_ip['subnet_id'] in subnets: + subnets.remove(fixed_ip['subnet_id']) + + # If there are subnets here that means that + # we need to add those to the port and call update. + if subnets: + port_fixed_ips.extend([dict(subnet_id=s) for s in subnets]) + port = self.neutron_api.update_port_fixed_ips( + port['id'], {'fixed_ips': port_fixed_ips}) + + return port + + def _remove_fixed_ip_from_service_port(self, port, subnet_id): + port_fixed_ips = [] + for fixed_ip in port['fixed_ips']: + if fixed_ip['subnet_id'] == subnet_id: + continue + port_fixed_ips.append({'subnet_id': fixed_ip['subnet_id'], + 'ip_address': fixed_ip['ip_address']}) + + if port_fixed_ips != port['fixed_ips']: + port = self.neutron_api.update_port_fixed_ips( + port['id'], {'fixed_ips': port_fixed_ips}) + + return port + + def _get_cidr_for_subnet(self): + """Returns not used cidr for service subnet creating.""" + subnets = self._get_all_service_subnets() + used_cidrs = set(subnet['cidr'] for subnet in subnets) + serv_cidr = netaddr.IPNetwork(CONF.service_network_cidr) + for subnet in serv_cidr.subnet(29): + cidr = str(subnet.cidr) + if cidr not in used_cidrs: + return cidr + else: + raise exception.ServiceInstanceException(_('No available cidrs.')) + + def delete_share_infrastructure(self, context, share_network_id): + """Removes share infrastructure. + + Deletes service vm and subnet, associated to share network. + """ + server = self.get_service_instance(context, + share_network_id=share_network_id, + create=False) + if server: + self._delete_server(context, server) + subnet_id = self._get_service_subnet(share_network_id) + if subnet_id: + router = self._get_private_router(share_network_id) + port = self._get_service_port() + self.neutron_api.router_remove_interface(router['id'], subnet_id) + self._remove_fixed_ip_from_service_port(port, subnet_id) + self.neutron_api.delete_subnet(subnet_id) + self._setup_connectivity_with_service_instances() + + def _get_all_service_subnets(self): + service_network = self.neutron_api.get_network(self.service_network_id) + return [self.neutron_api.get_subnet(subnet_id) + for subnet_id in service_network['subnets']] + + def _get_service_subnet(self, share_network_id): + all_service_subnets = self._get_all_service_subnets() + service_subnets = [subnet for subnet in all_service_subnets + if subnet['name'] == share_network_id] + if len(service_subnets) == 1: + return service_subnets[0] + elif not service_subnets: + return None + else: + raise exception.ServiceInstanceException(_('Ambiguous service ' + 'subnets.')) diff --git a/manila/tests/conf_fixture.py b/manila/tests/conf_fixture.py index a6cb4bd021..7a9b6bddc7 100644 --- a/manila/tests/conf_fixture.py +++ b/manila/tests/conf_fixture.py @@ -34,5 +34,6 @@ def set_defaults(conf): conf.set_default('sqlite_synchronous', False) conf.set_default('policy_file', 'manila/tests/policy.json') conf.set_default('share_export_ip', '0.0.0.0') + conf.set_default('service_instance_user', 'fake_user') conf.set_default('share_driver', 'manila.tests.fake_driver.FakeShareDriver') diff --git a/manila/tests/test_service_instance.py b/manila/tests/test_service_instance.py new file mode 100644 index 0000000000..28fb1387bb --- /dev/null +++ b/manila/tests/test_service_instance.py @@ -0,0 +1,657 @@ +# Copyright (c) 2014 NetApp, Inc. +# All Rights Reserved. +# +# 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. + +"""Unit tests for the instance module.""" + +import copy +import mock + +from manila import context +from manila import exception +from manila.share.drivers import service_instance +from manila import test +from manila.tests.db import fakes as db_fakes +from manila.tests import fake_compute +from manila.tests import fake_network + +from oslo.config import cfg + +CONF = cfg.CONF + + +def fake_share(**kwargs): + share = { + 'id': 'fakeid', + 'name': 'fakename', + 'size': 1, + 'share_proto': 'NFS', + 'share_network_id': 'fake share network id', + 'export_location': '127.0.0.1:/mnt/nfs/volume-00002', + } + share.update(kwargs) + return db_fakes.FakeModel(share) + + +class ServiceInstanceManagerTestCase(test.TestCase): + """Tests InstanceManager.""" + + def setUp(self): + super(ServiceInstanceManagerTestCase, self).setUp() + self._context = context.get_admin_context() + + self._helper_cifs = mock.Mock() + self._helper_nfs = mock.Mock() + self._db = mock.Mock() + self.stubs.Set(service_instance.neutron, 'API', fake_network.API) + self.stubs.Set(service_instance.compute, 'API', fake_compute.API) + with mock.patch.object(service_instance.ServiceInstanceManager, + '_setup_connectivity_with_service_instances', + mock.Mock()): + self._manager = service_instance.ServiceInstanceManager(self._db, + {}) + self._manager.service_tenant_id = 'service tenant id' + self._manager.service_network_id = 'service network id' + self._manager.admin_context = self._context + self._manager._execute = mock.Mock(return_value=('', '')) + self._manager.vif_driver = mock.Mock() + self.stubs.Set(service_instance, '_ssh_exec', mock.Mock()) + self.stubs.Set(service_instance, 'synchronized', mock.Mock(side_effect= + lambda f: f)) + self.stubs.Set(service_instance.os.path, 'exists', + mock.Mock(return_value=True)) + self._manager._helpers = { + 'CIFS': self._helper_cifs, + 'NFS': self._helper_nfs, + } + self.share = fake_share() + + def test_get_service_network_net_exists(self): + net1 = copy.copy(fake_network.API.network) + net2 = copy.copy(fake_network.API.network) + net1['name'] = CONF.service_network_name + net1['id'] = 'fake service network id' + self.stubs.Set(self._manager.neutron_api, 'get_all_tenant_networks', + mock.Mock(return_value=[net1, net2])) + result = self._manager._get_service_network() + self.assertEqual(result, net1['id']) + + def test_get_service_network_net_does_not_exists(self): + net = fake_network.FakeNetwork() + self.stubs.Set(self._manager.neutron_api, 'get_all_tenant_networks', + mock.Mock(return_value=[])) + self.stubs.Set(self._manager.neutron_api, 'network_create', + mock.Mock(return_value=net)) + result = self._manager._get_service_network() + self.assertEqual(result, net['id']) + + def test_get_service_network_ambiguos(self): + net = fake_network.FakeNetwork(name=CONF.service_network_name) + self.stubs.Set(self._manager.neutron_api, 'get_all_tenant_networks', + mock.Mock(return_value=[net, net])) + self.assertRaises(exception.ManilaException, + self._manager._get_service_network) + + def test_get_service_instance_name(self): + result = self._manager._get_service_instance_name( + 'fake_share_network_id') + self.assertEqual(result, CONF.service_instance_name_template % + 'fake_share_network_id') + + def test_get_server_ip(self): + fake_server = fake_compute.FakeServer(networks= + {CONF.service_network_name: '10.254.0.1'}) + + result = self._manager._get_server_ip(fake_server) + + self.assertEqual(result, + fake_server['networks'][CONF.service_network_name][0]) + + def test_get_server_ip_exception(self): + fake_server = fake_compute.FakeServer(networks={}) + self.assertRaises(exception.ManilaException, + self._manager._get_server_ip, fake_server) + + def test_get_service_instance(self): + fake_server = fake_compute.FakeServer() + self.stubs.Set(self._manager, '_ensure_server', + mock.Mock(return_value=False)) + self.stubs.Set(self._manager, '_get_server_ip', + mock.Mock(return_value='fake_ip')) + self.stubs.Set(self._manager.compute_api, 'server_list', + mock.Mock(return_value=[])) + self.stubs.Set(self._manager, '_create_service_instance', + mock.Mock(return_value=fake_server)) + self.stubs.Set(self._manager, '_get_ssh_pool', + mock.Mock(return_value=mock.Mock())) + + result = self._manager.get_service_instance(self._context, + share_network_id='fake_share_network_id') + + self._manager._ensure_server.assert_called_once() + self._manager._get_ssh_pool.assert_called_once_with(fake_server) + self._manager.compute_api.server_list.assert_called_once() + self._manager._get_server_ip.assert_called_once() + self._manager._create_service_instance.assert_called_once() + self.assertEqual(result, fake_server) + + def test_get_service_instance_existed_in_memory(self): + fake_server = fake_compute.FakeServer() + self._manager.share_networks_servers = {'fake_share_network_id': + fake_server} + self.stubs.Set(self._manager, '_ensure_server', + mock.Mock(return_value=True)) + self.stubs.Set(self._manager.compute_api, 'server_list', + mock.Mock(return_value=[fake_server])) + self.stubs.Set(self._manager, '_get_ssh_pool', + mock.Mock(return_value=mock.Mock())) + self.stubs.Set(self._manager, '_create_service_instance', mock.Mock()) + + result = self._manager.get_service_instance(self._context, + share_network_id='fake_share_network_id') + + self._manager._ensure_server.assert_called_once() + self.assertFalse(self._manager._get_ssh_pool.called) + self.assertFalse(self._manager.compute_api.server_list.called) + self.assertFalse(self._manager._create_service_instance.called) + + self.assertEqual(result, fake_server) + + def test_get_service_instance_existed_in_memory_non_active(self): + old_fake_server = fake_compute.FakeServer(status='ERROR') + new_fake_server = fake_compute.FakeServer() + self._manager.share_networks_servers = {'fake_share_network_id': + old_fake_server} + self.stubs.Set(self._manager, '_ensure_server', + mock.Mock(return_value=False)) + self.stubs.Set(self._manager, '_delete_server', mock.Mock()) + self.stubs.Set(self._manager, '_get_server_ip', + mock.Mock(return_value='fake_ip')) + self.stubs.Set(self._manager.compute_api, 'server_list', + mock.Mock(return_value=[])) + self.stubs.Set(self._manager, '_create_service_instance', + mock.Mock(return_value=new_fake_server)) + self.stubs.Set(self._manager, '_get_ssh_pool', + mock.Mock(return_value=mock.Mock())) + + result = self._manager.get_service_instance(self._context, + share_network_id='fake_share_network_id') + + self._manager._ensure_server.assert_has_calls( + [mock.call(self._context, old_fake_server, update=True)]) + self._manager._get_ssh_pool.assert_called_once_with(new_fake_server) + self._manager.compute_api.server_list.assert_called_once() + self._manager._get_server_ip.assert_called_once() + self._manager._create_service_instance.assert_called_once() + + self.assertEqual(result, new_fake_server) + + def test_get_service_instance_existed(self): + fake_server = fake_compute.FakeServer() + self.stubs.Set(self._manager, '_ensure_server', + mock.Mock(side_effect=[False, True])) + self.stubs.Set(self._manager, '_get_server_ip', + mock.Mock(return_value='fake_ip')) + self.stubs.Set(self._manager.compute_api, 'server_list', + mock.Mock(return_value=[fake_server])) + self.stubs.Set(self._manager, '_create_service_instance', + mock.Mock()) + self.stubs.Set(self._manager, '_get_ssh_pool', + mock.Mock(return_value=mock.Mock())) + + result = self._manager.get_service_instance(self._context, + share_network_id='fake_share_network_id') + + self._manager._ensure_server.assert_called_once() + self._manager._get_ssh_pool.assert_called_once_with(fake_server) + self._manager.compute_api.server_list.assert_called_once() + self._manager._get_server_ip.assert_called_once() + self.assertFalse(self._manager._create_service_instance.called) + self.assertEqual(result, fake_server) + + def test_ensure_server(self): + fake_server = fake_compute.FakeServer() + self.stubs.Set(self._manager, '_check_server_availability', + mock.Mock(return_value=True)) + self.stubs.Set(self._manager.compute_api, 'server_get', + mock.Mock(return_value=fake_server)) + result = self._manager._ensure_server(self._context, + fake_server, + update=True) + self._manager.compute_api.server_get.\ + assert_called_once_with(self._context, fake_server['id']) + self._manager._check_server_availability.\ + assert_called_once_with(fake_server) + self.assertTrue(result) + + def test_ensure_server_not_exists(self): + fake_server = fake_compute.FakeServer() + self.stubs.Set(self._manager, '_check_server_availability', + mock.Mock(return_value=True)) + self.stubs.Set(self._manager.compute_api, 'server_get', + mock.Mock(side_effect=exception.InstanceNotFound( + instance_id=fake_server['id']))) + result = self._manager._ensure_server(self._context, + fake_server, + update=True) + self._manager.compute_api.server_get.\ + assert_called_once_with(self._context, fake_server['id']) + self.assertFalse(self._manager._check_server_availability.called) + self.assertFalse(result) + + def test_ensure_server_exception(self): + fake_server = fake_compute.FakeServer() + self.stubs.Set(self._manager, '_check_server_availability', + mock.Mock(return_value=True)) + self.stubs.Set(self._manager.compute_api, 'server_get', + mock.Mock(side_effect=exception.ManilaException)) + self.assertRaises(exception.ManilaException, + self._manager._ensure_server, + self._context, + fake_server, + update=True) + self._manager.compute_api.server_get.\ + assert_called_once_with(self._context, fake_server['id']) + self.assertFalse(self._manager._check_server_availability.called) + + def test_ensure_server_non_active(self): + fake_server = fake_compute.FakeServer(status='ERROR') + self.stubs.Set(self._manager, '_check_server_availability', + mock.Mock(return_value=True)) + result = self._manager._ensure_server(self._context, fake_server) + self.assertFalse(self._manager._check_server_availability.called) + self.assertFalse(result) + + def test_get_key_create_new(self): + fake_keypair = fake_compute.FakeKeypair(name= + CONF.manila_service_keypair_name) + self.stubs.Set(self._manager.compute_api, 'keypair_list', + mock.Mock(return_value=[])) + self.stubs.Set(self._manager.compute_api, 'keypair_import', + mock.Mock(return_value=fake_keypair)) + + result = self._manager._get_key(self._context) + + self.assertEqual(result, fake_keypair.name) + self._manager.compute_api.keypair_list.assert_called_once() + self._manager.compute_api.keypair_import.assert_called_once() + + def test_get_key_exists(self): + fake_keypair = fake_compute.FakeKeypair( + name=CONF.manila_service_keypair_name, + public_key='fake_public_key') + self.stubs.Set(self._manager.compute_api, 'keypair_list', + mock.Mock(return_value=[fake_keypair])) + self.stubs.Set(self._manager.compute_api, 'keypair_import', + mock.Mock(return_value=fake_keypair)) + self.stubs.Set(self._manager, '_execute', + mock.Mock(return_value=('fake_public_key', ''))) + + result = self._manager._get_key(self._context) + + self._manager.compute_api.keypair_list.assert_called_once() + self.assertFalse(self._manager.compute_api.keypair_import.called) + self.assertEqual(result, fake_keypair.name) + + def test_get_key_exists_recreate(self): + fake_keypair = fake_compute.FakeKeypair( + name=CONF.manila_service_keypair_name, + public_key='fake_public_key1') + self.stubs.Set(self._manager.compute_api, 'keypair_list', + mock.Mock(return_value=[fake_keypair])) + self.stubs.Set(self._manager.compute_api, 'keypair_import', + mock.Mock(return_value=fake_keypair)) + self.stubs.Set(self._manager.compute_api, 'keypair_delete', + mock.Mock()) + self.stubs.Set(self._manager, '_execute', + mock.Mock(return_value=('fake_public_key2', ''))) + + result = self._manager._get_key(self._context) + + self._manager.compute_api.keypair_list.assert_called_once() + self._manager.compute_api.keypair_delete.assert_called_once() + self._manager.compute_api.keypair_import.\ + assert_called_once_with(self._context, fake_keypair.name, + 'fake_public_key2') + self.assertEqual(result, fake_keypair.name) + + def test_get_service_image(self): + fake_image1 = fake_compute.FakeImage(name=CONF.service_image_name) + fake_image2 = fake_compute.FakeImage(name='another-image') + self.stubs.Set(self._manager.compute_api, 'image_list', + mock.Mock(return_value=[fake_image1, fake_image2])) + + result = self._manager._get_service_image(self._context) + + self.assertEqual(result, fake_image1.id) + + def test_get_service_image_not_found(self): + self.stubs.Set(self._manager.compute_api, 'image_list', + mock.Mock(return_value=[])) + + self.assertRaises(exception.ManilaException, + self._manager._get_service_image, + self._context) + + def test_get_service_image_ambiguous(self): + fake_image = fake_compute.FakeImage(name=CONF.service_image_name) + self.stubs.Set(self._manager.compute_api, 'image_list', + mock.Mock(return_value=[fake_image, fake_image])) + + self.assertRaises(exception.ManilaException, + self._manager._get_service_image, + self._context) + + def test_create_service_instance(self): + fake_server = fake_compute.FakeServer() + fake_port = fake_network.FakePort() + self.stubs.Set(self._manager, '_get_service_image', + mock.Mock(return_value='fake_image_id')) + self.stubs.Set(self._manager, '_get_key', + mock.Mock(return_value='fake_key_name')) + self.stubs.Set(self._manager, '_setup_network_for_instance', + mock.Mock(return_value=fake_port)) + self.stubs.Set(self._manager, + '_setup_connectivity_with_service_instances', + mock.Mock()) + self.stubs.Set(self._manager.compute_api, 'server_create', + mock.Mock(return_value=fake_server)) + self.stubs.Set(self._manager, '_get_server_ip', + mock.Mock(return_value='fake_ip')) + self.stubs.Set(service_instance.socket, 'socket', mock.Mock()) + + result = self._manager._create_service_instance(self._context, + 'instance_name', self.share, None) + + self._manager._get_service_image.assert_called_once() + self._manager._get_key.assert_called_once() + self._manager._setup_network_for_instance.assert_called_once() + self._manager._setup_connectivity_with_service_instances.\ + assert_called_once() + self._manager.compute_api.server_create.assert_called_once_with( + self._context, 'instance_name', 'fake_image_id', + CONF.service_instance_flavor_id, 'fake_key_name', None, None, + nics=[{'port-id': fake_port['id']}]) + service_instance.socket.socket.assert_called_once() + self.assertEqual(result, fake_server) + + def test_create_service_instance_error(self): + fake_server = fake_compute.FakeServer(status='ERROR') + fake_port = fake_network.FakePort() + self.stubs.Set(self._manager, '_get_service_image', + mock.Mock(return_value='fake_image_id')) + self.stubs.Set(self._manager, '_get_key', + mock.Mock(return_value='fake_key_name')) + self.stubs.Set(self._manager, '_setup_network_for_instance', + mock.Mock(return_value=fake_port)) + self.stubs.Set(self._manager, + '_setup_connectivity_with_service_instances', + mock.Mock()) + self.stubs.Set(self._manager.compute_api, 'server_create', + mock.Mock(return_value=fake_server)) + self.stubs.Set(self._manager.compute_api, 'server_get', + mock.Mock(return_value=fake_server)) + self.stubs.Set(service_instance.socket, 'socket', mock.Mock()) + + self.assertRaises(exception.ManilaException, + self._manager._create_service_instance, self._context, + 'instance_name', self.share, None) + + self._manager.compute_api.server_create.assert_called_once() + self.assertFalse(self._manager.compute_api.server_get.called) + self.assertFalse(service_instance.socket.socket.called) + + def test_create_service_instance_failed_setup_connectivity(self): + fake_server = fake_compute.FakeServer(status='ERROR') + fake_port = fake_network.FakePort() + self.stubs.Set(self._manager, '_get_service_image', + mock.Mock(return_value='fake_image_id')) + self.stubs.Set(self._manager, '_get_key', + mock.Mock(return_value='fake_key_name')) + self.stubs.Set(self._manager, '_setup_network_for_instance', + mock.Mock(return_value=fake_port)) + self.stubs.Set(self._manager, + '_setup_connectivity_with_service_instances', + mock.Mock(side_effect=exception.ManilaException)) + self.stubs.Set(self._manager.neutron_api, 'delete_port', mock.Mock()) + self.stubs.Set(self._manager.compute_api, 'server_create', + mock.Mock(return_value=fake_server)) + self.stubs.Set(self._manager.compute_api, 'server_get', + mock.Mock(return_value=fake_server)) + self.stubs.Set(service_instance.socket, 'socket', mock.Mock()) + + self.assertRaises(exception.ManilaException, + self._manager._create_service_instance, + self._context, 'instance_name', self.share, None) + + self._manager.neutron_api.delete_port.\ + assert_called_once_with(fake_port['id']) + self.assertFalse(self._manager.compute_api.server_create.called) + self.assertFalse(self._manager.compute_api.server_get.called) + self.assertFalse(service_instance.socket.socket.called) + + def test_create_service_instance_no_key_and_password(self): + self.stubs.Set(self._manager, '_get_service_image', + mock.Mock(return_value='fake_image_id')) + self.stubs.Set(self._manager, '_get_key', + mock.Mock(return_value=None)) + self.assertRaises(exception.ManilaException, + self._manager._create_service_instance, self._context, + 'instance_name', self.share, None) + + def test_setup_network_for_instance(self): + fake_service_net = fake_network.FakeNetwork(subnets=[]) + fake_service_subnet = fake_network.\ + FakeSubnet(name=self.share['share_network_id']) + fake_router = fake_network.FakeRouter() + fake_port = fake_network.FakePort() + self.stubs.Set(self._manager.neutron_api, 'get_network', + mock.Mock(return_value=fake_service_net)) + self.stubs.Set(self._manager.neutron_api, 'subnet_create', + mock.Mock(return_value=fake_service_subnet)) + self.stubs.Set(self._manager.db, 'share_network_get', + mock.Mock(return_value='fake_share_network')) + self.stubs.Set(self._manager, '_get_private_router', + mock.Mock(return_value=fake_router)) + self.stubs.Set(self._manager.neutron_api, 'router_add_interface', + mock.Mock()) + self.stubs.Set(self._manager.neutron_api, 'create_port', + mock.Mock(return_value=fake_port)) + self.stubs.Set(self._manager, '_get_cidr_for_subnet', + mock.Mock(return_value='fake_cidr')) + + result = self._manager._setup_network_for_instance(self._context, + 'fake_share_network', None) + + self._manager.neutron_api.get_network.\ + assert_called_once_with(self._manager.service_network_id) + self._manager._get_private_router.\ + assert_called_once_with('fake_share_network') + self._manager.neutron_api.router_add_interface.\ + assert_called_once_with('fake_router_id', 'fake_subnet_id') + self._manager.neutron_api.subnet_create.assert_called_once_with( + self._manager.service_tenant_id, + self._manager.service_network_id, + 'fake_share_network', + 'fake_cidr') + self._manager.neutron_api.create_port.assert_called_once_with( + self._manager.service_tenant_id, + self._manager.service_network_id, + subnet_id='fake_subnet_id', + fixed_ip=None, + device_owner='manila') + self._manager._get_cidr_for_subnet.assert_called_once() + self.assertEqual(result, fake_port) + + def test_get_private_router(self): + fake_net = fake_network.FakeNetwork() + fake_subnet = fake_network.FakeSubnet(gateway_ip='fake_ip') + fake_share_network = {'neutron_net_id': fake_net['id'], + 'neutron_subnet_id': fake_subnet['id']} + self.stubs.Set(self._manager.db, 'share_network_get', + mock.Mock(return_value=fake_share_network)) + fake_port = fake_network.FakePort(fixed_ips=[ + {'subnet_id': fake_subnet['id'], + 'ip_address': fake_subnet['gateway_ip']}], + device_id='fake_router_id') + fake_router = fake_network.FakeRouter(id='fake_router_id') + self.stubs.Set(self._manager.neutron_api, 'get_subnet', + mock.Mock(return_value=fake_subnet)) + self.stubs.Set(self._manager.neutron_api, 'list_ports', + mock.Mock(return_value=[fake_port])) + self.stubs.Set(self._manager.neutron_api, 'show_router', + mock.Mock(return_value=fake_router)) + + result = self._manager._get_private_router( + {'neutron_subnet_id': fake_subnet['id'], + 'neutron_net_id': fake_net['id']}) + + self._manager.neutron_api.get_subnet.\ + assert_called_once_with(fake_subnet['id']) + self._manager.neutron_api.list_ports.\ + assert_called_once_with(network_id=fake_net['id']) + self._manager.neutron_api.show_router.\ + assert_called_once_with(fake_router['id']) + self.assertEqual(result, fake_router) + + def test_get_private_router_exception(self): + fake_net = fake_network.FakeNetwork() + fake_subnet = fake_network.FakeSubnet(gateway_ip='fake_ip') + fake_share_network = {'neutron_net_id': fake_net['id'], + 'neutron_subnet_id': fake_subnet['id']} + self.stubs.Set(self._manager.db, 'share_network_get', + mock.Mock(return_value=fake_share_network)) + self.stubs.Set(self._manager.neutron_api, 'get_subnet', + mock.Mock(return_value=fake_subnet)) + self.stubs.Set(self._manager.neutron_api, 'list_ports', + mock.Mock(return_value=[])) + + self.assertRaises(exception.ManilaException, + self._manager._get_private_router, + {'neutron_subnet_id': fake_subnet['id'], + 'neutron_net_id': fake_net['id']}) + + def test_setup_connectivity_with_service_instances(self): + fake_subnet = fake_network.FakeSubnet(cidr='10.254.0.1/29') + fake_port = fake_network.FakePort(fixed_ips=[ + {'subnet_id': fake_subnet['id'], 'ip_address': '10.254.0.2'}], + mac_address='fake_mac_address') + + self.stubs.Set(self._manager, '_get_service_port', + mock.Mock(return_value=fake_port)) + self.stubs.Set(self._manager.vif_driver, 'get_device_name', + mock.Mock(return_value='fake_interface_name')) + self.stubs.Set(self._manager.neutron_api, 'get_subnet', + mock.Mock(return_value=fake_subnet)) + self.stubs.Set(self._manager, '_remove_outdated_interfaces', + mock.Mock()) + self.stubs.Set(self._manager.vif_driver, 'plug', mock.Mock()) + device_mock = mock.Mock() + self.stubs.Set(service_instance.ip_lib, 'IPDevice', + mock.Mock(return_value=device_mock)) + + self._manager._setup_connectivity_with_service_instances() + + self._manager._get_service_port.assert_called_once() + self._manager.vif_driver.get_device_name.assert_called_once_with( + fake_port) + self._manager.vif_driver.plug.assert_called_once_with(fake_port['id'], + 'fake_interface_name', fake_port['mac_address']) + self._manager.neutron_api.get_subnet.assert_called_once_with( + fake_subnet['id']) + self._manager.vif_driver.init_l3.assert_called_once() + service_instance.ip_lib.IPDevice.assert_called_once() + device_mock.route.pullup_route.assert_called_once() + self._manager._remove_outdated_interfaces.assert_called_once_with( + device_mock) + + def test_get_service_port(self): + fake_service_port = fake_network.FakePort(device_id='manila-share') + fake_service_net = fake_network.FakeNetwork(subnets=[]) + self.stubs.Set(self._manager.neutron_api, 'list_ports', + mock.Mock(return_value=[])) + self.stubs.Set(self._manager, '_execute', + mock.Mock(return_value=('fake_host', ''))) + self.stubs.Set(self._manager.neutron_api, 'create_port', + mock.Mock(return_value=fake_service_port)) + self.stubs.Set(self._manager.neutron_api, 'get_network', + mock.Mock(return_value=fake_service_net)) + self.stubs.Set(self._manager.neutron_api, 'update_port_fixed_ips', + mock.Mock(return_value=fake_service_port)) + + result = self._manager._get_service_port() + + self._manager.neutron_api.list_ports.\ + assert_called_once_with(device_id='manila-share') + self._manager.db.service_get_all_by_topic.assert_called_once() + self._manager.neutron_api.create_port.assert_called_once_with( + self._manager.service_tenant_id, + self._manager.service_network_id, + device_id='manila-share', + device_owner='manila:share', + host_id='fake_host' + ) + self._manager.neutron_api.get_network.assert_called_once() + self.assertFalse(self._manager.neutron_api. + update_port_fixed_ips.called) + self.assertEqual(result, fake_service_port) + + def test_get_service_port_ambigious_ports(self): + fake_service_port = fake_network.FakePort(device_id='manila-share') + self.stubs.Set(self._manager.neutron_api, 'list_ports', + mock.Mock(return_value=[fake_service_port, fake_service_port])) + self.assertRaises(exception.ManilaException, + self._manager._get_service_port) + + def test_get_service_port_exists(self): + fake_service_port = fake_network.FakePort(device_id='manila-share') + fake_service_net = fake_network.FakeNetwork(subnets=[]) + self.stubs.Set(self._manager.neutron_api, 'list_ports', + mock.Mock(return_value=[fake_service_port])) + self.stubs.Set(self._manager.db, 'service_get_all_by_topic', + mock.Mock(return_value=[{'host': 'fake_host'}])) + self.stubs.Set(self._manager.neutron_api, 'create_port', + mock.Mock(return_value=fake_service_port)) + self.stubs.Set(self._manager.neutron_api, 'get_network', + mock.Mock(return_value=fake_service_net)) + self.stubs.Set(self._manager.neutron_api, 'update_port_fixed_ips', + mock.Mock(return_value=fake_service_port)) + + result = self._manager._get_service_port() + + self._manager.neutron_api.list_ports.assert_called_once_with( + device_id='manila-share') + self.assertFalse(self._manager.db.service_get_all_by_topic.called) + self.assertFalse(self._manager.neutron_api.create_port.called) + self._manager.neutron_api.get_network.assert_called_once() + self.assertFalse(self._manager.neutron_api. + update_port_fixed_ips.called) + self.assertEqual(result, fake_service_port) + + def test_get_cidr_for_subnet(self): + serv_cidr = service_instance.netaddr.IPNetwork( + CONF.service_network_cidr) + cidrs = serv_cidr.subnet(29) + cidr1 = str(cidrs.next()) + cidr2 = str(cidrs.next()) + self.stubs.Set(self._manager, '_get_all_service_subnets', + mock.Mock(return_value=[])) + result = self._manager._get_cidr_for_subnet() + self.assertEqual(result, cidr1) + + fake_subnet = fake_network.FakeSubnet(cidr=cidr1) + self.stubs.Set(self._manager, '_get_all_service_subnets', + mock.Mock(return_value=[fake_subnet])) + result = self._manager._get_cidr_for_subnet() + self.assertEqual(result, cidr2) diff --git a/manila/tests/test_share_generic.py b/manila/tests/test_share_generic.py index 2f39bbd439..109ab4ec95 100644 --- a/manila/tests/test_share_generic.py +++ b/manila/tests/test_share_generic.py @@ -1,4 +1,4 @@ -# Copyright 2014 Mirantis Inc. +# Copyright (c) 2014 NetApp, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -12,29 +12,26 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + """Unit tests for the Generic driver module.""" -import copy import mock import os -from manila import context +from oslo.config import cfg from manila import compute +from manila import context from manila import exception -from manila.network.neutron import api as neutron -from manila import volume - from manila.share.configuration import Configuration from manila.share.drivers import generic from manila import test from manila.tests.db import fakes as db_fakes from manila.tests import fake_compute -from manila.tests import fake_network from manila.tests import fake_utils from manila.tests import fake_volume +from manila import volume -from oslo.config import cfg CONF = cfg.CONF @@ -94,13 +91,13 @@ class GenericShareDriverTestCase(test.TestCase): configuration=self.fake_conf) self._driver.service_tenant_id = 'service tenant id' self._driver.service_network_id = 'service network id' - self._driver.neutron_api = fake_network.API() self._driver.compute_api = fake_compute.API() self._driver.volume_api = fake_volume.API() self._driver.share_networks_locks = {} + self._driver.get_service_instance = mock.Mock() self._driver.share_networks_servers = {} self._driver.admin_context = self._context - self._driver.vif_driver = mock.Mock() + self._driver.instance_manager = mock.Mock() self.stubs.Set(generic, '_ssh_exec', mock.Mock()) self.stubs.Set(generic, 'synchronized', mock.Mock(side_effect= lambda f: f)) @@ -114,62 +111,17 @@ class GenericShareDriverTestCase(test.TestCase): self.snapshot = fake_snapshot() def test_do_setup(self): - self.stubs.Set(neutron, 'API', mock.Mock()) self.stubs.Set(volume, 'API', mock.Mock()) self.stubs.Set(compute, 'API', mock.Mock()) - self.stubs.Set(self._driver, - '_setup_connectivity_with_service_instances', - mock.Mock()) - self.stubs.Set(self._driver, - '_get_service_network', - mock.Mock(return_value='fake network id')) + self.stubs.Set(generic, 'service_instance', mock.Mock()) self.stubs.Set(self._driver, '_setup_helpers', mock.Mock()) self._driver.do_setup(self._context) - neutron.API.assert_called_once() volume.API.assert_called_once() compute.API.assert_called_once() self._driver._setup_helpers.assert_called_once() - self._driver._setup_connectivity_with_service_instances.\ - assert_called_once() - self.assertEqual(self._driver.service_network_id, 'fake network id') - - def test_do_setup_exception(self): - self.stubs.Set(neutron, 'API', mock.Mock()) - neutron.API.return_value = fake_network.API() - self.stubs.Set(volume, 'API', mock.Mock()) - self.stubs.Set(compute, 'API', mock.Mock()) - self.stubs.Set(neutron.API, 'admin_tenant_id', mock.Mock()) - neutron.API.admin_tenant_id.side_effect = Exception - self.assertRaises(exception.ManilaException, - self._driver.do_setup, self._context) - - def test_get_service_network_net_exists(self): - net1 = copy.copy(fake_network.API.network) - net2 = copy.copy(fake_network.API.network) - net1['name'] = CONF.service_network_name - net1['id'] = 'fake service network id' - self.stubs.Set(self._driver.neutron_api, 'get_all_tenant_networks', - mock.Mock(return_value=[net1, net2])) - result = self._driver._get_service_network() - self.assertEqual(result, net1['id']) - - def test_get_service_network_net_does_not_exists(self): - net = fake_network.FakeNetwork() - self.stubs.Set(self._driver.neutron_api, 'get_all_tenant_networks', - mock.Mock(return_value=[])) - self.stubs.Set(self._driver.neutron_api, 'network_create', - mock.Mock(return_value=net)) - result = self._driver._get_service_network() - self.assertEqual(result, net['id']) - - def test_get_service_network_ambiguos(self): - net = fake_network.FakeNetwork(name=CONF.service_network_name) - self.stubs.Set(self._driver.neutron_api, 'get_all_tenant_networks', - mock.Mock(return_value=[net, net])) - self.assertRaises(exception.ManilaException, - self._driver._get_service_network) def test_setup_helpers(self): + self._driver._helpers = {} CONF.set_default('share_helpers', ['NFS=fakenfs']) self.stubs.Set(generic.importutils, 'import_class', mock.Mock(return_value=self._helper_nfs)) @@ -184,7 +136,7 @@ class GenericShareDriverTestCase(test.TestCase): def test_create_share(self): self._helper_nfs.create_export.return_value = 'fakelocation' - methods = ('_get_service_instance', '_allocate_container', + methods = ('get_service_instance', '_allocate_container', '_attach_volume', '_format_device', '_mount_device') for method in methods: self.stubs.Set(self._driver, method, mock.Mock()) @@ -444,541 +396,6 @@ class GenericShareDriverTestCase(test.TestCase): self.assertEqual(result, '/dev/vdc') - def test_get_service_instance_name(self): - result = self._driver._get_service_instance_name(self.share) - self.assertEqual(result, CONF.service_instance_name_template % - self.share['share_network_id']) - - def test_get_server_ip(self): - fake_server = fake_compute.FakeServer(networks= - {CONF.service_network_name: '10.254.0.1'}) - - result = self._driver._get_server_ip(fake_server) - - self.assertEqual(result, - fake_server['networks'][CONF.service_network_name][0]) - - def test_get_server_ip_exception(self): - fake_server = fake_compute.FakeServer(networks={}) - self.assertRaises(exception.ManilaException, - self._driver._get_server_ip, fake_server) - - def test_get_service_instance(self): - fake_server = fake_compute.FakeServer() - self.stubs.Set(self._driver, '_ensure_or_delete_server', - mock.Mock(return_value=True)) - self.stubs.Set(self._driver, '_get_server_ip', - mock.Mock(return_value='fake_ip')) - self.stubs.Set(self._driver.compute_api, 'server_list', - mock.Mock(return_value=[])) - self.stubs.Set(self._driver, '_create_service_instance', - mock.Mock(return_value=fake_server)) - self.stubs.Set(self._driver, '_get_ssh_pool', - mock.Mock(return_value=mock.Mock())) - - result = self._driver._get_service_instance(self._context, self.share) - - self.assertFalse(self._driver._ensure_or_delete_server.called) - self._driver._get_ssh_pool.assert_called_once_with(fake_server) - self._driver.compute_api.server_list.assert_called_once() - self._driver._get_server_ip.assert_called_once() - self._driver._create_service_instance.assert_called_once() - self.assertEqual(result, fake_server) - - def test_get_service_instance_existed_in_memory(self): - fake_server = fake_compute.FakeServer() - self._driver.share_networks_servers = {self.share['share_network_id']: - fake_server} - self.stubs.Set(self._driver, '_ensure_or_delete_server', - mock.Mock(return_value=True)) - self.stubs.Set(self._driver.compute_api, 'server_list', - mock.Mock(return_value=[fake_server])) - self.stubs.Set(self._driver, '_get_ssh_pool', - mock.Mock(return_value=mock.Mock())) - self.stubs.Set(self._driver, '_create_service_instance', - mock.Mock(return_value=fake_server)) - - result = self._driver._get_service_instance(self._context, self.share) - - self._driver._ensure_or_delete_server.assert_called_once() - self.assertFalse(self._driver._get_ssh_pool.called) - self.assertFalse(self._driver.compute_api.server_list.called) - self.assertFalse(self._driver._create_service_instance.called) - - self.assertEqual(result, fake_server) - - def test_get_service_instance_existed_in_memory_non_active(self): - old_fake_server = fake_compute.FakeServer(status='ERROR') - new_fake_server = fake_compute.FakeServer() - self._driver.share_networks_servers = {self.share['share_network_id']: - old_fake_server} - self.stubs.Set(self._driver, '_ensure_or_delete_server', - mock.Mock(return_value=False)) - self.stubs.Set(self._driver, '_get_server_ip', - mock.Mock(return_value='fake_ip')) - self.stubs.Set(self._driver.compute_api, 'server_list', - mock.Mock(return_value=[])) - self.stubs.Set(self._driver, '_create_service_instance', - mock.Mock(return_value=new_fake_server)) - self.stubs.Set(self._driver, '_get_ssh_pool', - mock.Mock(return_value=mock.Mock())) - - result = self._driver._get_service_instance(self._context, self.share) - - self._driver._ensure_or_delete_server.assert_has_calls( - [mock.call(self._context, old_fake_server, update=True)]) - self._driver._get_ssh_pool.assert_called_once_with(new_fake_server) - self._driver.compute_api.server_list.assert_called_once() - self._driver._get_server_ip.assert_called_once() - self._driver._create_service_instance.assert_called_once() - - self.assertEqual(result, new_fake_server) - - def test_get_service_instance_existed(self): - fake_server = fake_compute.FakeServer() - self.stubs.Set(self._driver, '_ensure_or_delete_server', - mock.Mock(return_value=True)) - self.stubs.Set(self._driver, '_get_server_ip', - mock.Mock(return_value='fake_ip')) - self.stubs.Set(self._driver.compute_api, 'server_list', - mock.Mock(return_value=[fake_server])) - self.stubs.Set(self._driver, '_create_service_instance', - mock.Mock()) - self.stubs.Set(self._driver, '_get_ssh_pool', - mock.Mock(return_value=mock.Mock())) - - result = self._driver._get_service_instance(self._context, self.share) - - self._driver._ensure_or_delete_server.assert_called_once() - self._driver._get_ssh_pool.assert_called_once_with(fake_server) - self._driver.compute_api.server_list.assert_called_once() - self._driver._get_server_ip.assert_called_once() - self.assertFalse(self._driver._create_service_instance.called) - self.assertEqual(result, fake_server) - - def test_ensure_or_delete_server(self): - fake_server = fake_compute.FakeServer() - self.stubs.Set(self._driver, '_check_server_availability', - mock.Mock(return_value=True)) - self.stubs.Set(self._driver.compute_api, 'server_get', - mock.Mock(return_value=fake_server)) - result = self._driver._ensure_or_delete_server(self._context, - fake_server, - update=True) - self._driver.compute_api.server_get.\ - assert_called_once_with(self._context, fake_server['id']) - self._driver._check_server_availability.\ - assert_called_once_with(fake_server) - self.assertTrue(result) - - def test_ensure_or_delete_server_not_exists(self): - fake_server = fake_compute.FakeServer() - self.stubs.Set(self._driver, '_check_server_availability', - mock.Mock(return_value=True)) - self.stubs.Set(self._driver.compute_api, 'server_get', - mock.Mock(side_effect=exception.InstanceNotFound( - instance_id=fake_server['id']))) - result = self._driver._ensure_or_delete_server(self._context, - fake_server, - update=True) - self._driver.compute_api.server_get.\ - assert_called_once_with(self._context, fake_server['id']) - self.assertFalse(self._driver._check_server_availability.called) - self.assertFalse(result) - - def test_ensure_or_delete_server_exception(self): - fake_server = fake_compute.FakeServer() - self.stubs.Set(self._driver, '_check_server_availability', - mock.Mock(return_value=True)) - self.stubs.Set(self._driver.compute_api, 'server_get', - mock.Mock(side_effect=exception.ManilaException)) - self.assertRaises(exception.ManilaException, - self._driver._ensure_or_delete_server, - self._context, - fake_server, - update=True) - self._driver.compute_api.server_get.\ - assert_called_once_with(self._context, fake_server['id']) - self.assertFalse(self._driver._check_server_availability.called) - - def test_ensure_or_delete_server_non_active(self): - fake_server = fake_compute.FakeServer(status='ERROR') - self.stubs.Set(self._driver, '_delete_server', mock.Mock()) - self.stubs.Set(self._driver, '_check_server_availability', - mock.Mock(return_value=True)) - result = self._driver._ensure_or_delete_server(self._context, - fake_server) - self.assertFalse(self._driver._check_server_availability.called) - self._driver._delete_server.assert_called_once_with(self._context, - fake_server) - self.assertFalse(result) - - def test_get_key_create_new(self): - fake_keypair = fake_compute.FakeKeypair(name= - CONF.manila_service_keypair_name) - self.stubs.Set(self._driver.compute_api, 'keypair_list', - mock.Mock(return_value=[])) - self.stubs.Set(self._driver.compute_api, 'keypair_import', - mock.Mock(return_value=fake_keypair)) - - result = self._driver._get_key(self._context) - - self.assertEqual(result, fake_keypair.name) - self._driver.compute_api.keypair_list.assert_called_once() - self._driver.compute_api.keypair_import.assert_called_once() - - def test_get_key_exists(self): - fake_keypair = fake_compute.FakeKeypair( - name=CONF.manila_service_keypair_name, - public_key='fake_public_key') - self.stubs.Set(self._driver.compute_api, 'keypair_list', - mock.Mock(return_value=[fake_keypair])) - self.stubs.Set(self._driver.compute_api, 'keypair_import', - mock.Mock(return_value=fake_keypair)) - self.stubs.Set(self._driver, '_execute', - mock.Mock(return_value=('fake_public_key', ''))) - - result = self._driver._get_key(self._context) - - self._driver.compute_api.keypair_list.assert_called_once() - self.assertFalse(self._driver.compute_api.keypair_import.called) - self.assertEqual(result, fake_keypair.name) - - def test_get_key_exists_recreate(self): - fake_keypair = fake_compute.FakeKeypair( - name=CONF.manila_service_keypair_name, - public_key='fake_public_key1') - self.stubs.Set(self._driver.compute_api, 'keypair_list', - mock.Mock(return_value=[fake_keypair])) - self.stubs.Set(self._driver.compute_api, 'keypair_import', - mock.Mock(return_value=fake_keypair)) - self.stubs.Set(self._driver.compute_api, 'keypair_delete', mock.Mock()) - self.stubs.Set(self._driver, '_execute', - mock.Mock(return_value=('fake_public_key2', ''))) - - result = self._driver._get_key(self._context) - - self._driver.compute_api.keypair_list.assert_called_once() - self._driver.compute_api.keypair_delete.assert_called_once() - self._driver.compute_api.keypair_import.\ - assert_called_once_with(self._context, fake_keypair.name, - 'fake_public_key2') - self.assertEqual(result, fake_keypair.name) - - def test_get_service_image(self): - fake_image1 = fake_compute.FakeImage(name=CONF.service_image_name) - fake_image2 = fake_compute.FakeImage(name='another-image') - self.stubs.Set(self._driver.compute_api, 'image_list', - mock.Mock(return_value=[fake_image1, fake_image2])) - - result = self._driver._get_service_image(self._context) - - self.assertEqual(result, fake_image1.id) - - def test_get_service_image_not_found(self): - self.stubs.Set(self._driver.compute_api, 'image_list', - mock.Mock(return_value=[])) - - self.assertRaises(exception.ManilaException, - self._driver._get_service_image, - self._context) - - def test_get_service_image_ambiguous(self): - fake_image = fake_compute.FakeImage(name=CONF.service_image_name) - self.stubs.Set(self._driver.compute_api, 'image_list', - mock.Mock(return_value=[fake_image, fake_image])) - - self.assertRaises(exception.ManilaException, - self._driver._get_service_image, - self._context) - - def test_create_service_instance(self): - fake_server = fake_compute.FakeServer() - fake_port = fake_network.FakePort() - self.stubs.Set(self._driver, '_get_service_image', - mock.Mock(return_value='fake_image_id')) - self.stubs.Set(self._driver, '_get_key', - mock.Mock(return_value='fake_key_name')) - self.stubs.Set(self._driver, '_setup_network_for_instance', - mock.Mock(return_value=fake_port)) - self.stubs.Set(self._driver, - '_setup_connectivity_with_service_instances', - mock.Mock()) - self.stubs.Set(self._driver.compute_api, 'server_create', - mock.Mock(return_value=fake_server)) - self.stubs.Set(self._driver, '_get_server_ip', - mock.Mock(return_value='fake_ip')) - self.stubs.Set(generic.socket, 'socket', mock.Mock()) - - result = self._driver._create_service_instance(self._context, - 'instance_name', self.share, None) - - self._driver._get_service_image.assert_called_once() - self._driver._get_key.assert_called_once() - self._driver._setup_network_for_instance.assert_called_once() - self._driver._setup_connectivity_with_service_instances.\ - assert_called_once() - self._driver.compute_api.server_create.assert_called_once_with( - self._context, 'instance_name', 'fake_image_id', - CONF.service_instance_flavor_id, 'fake_key_name', None, None, - nics=[{'port-id': fake_port['id']}]) - generic.socket.socket.assert_called_once() - self.assertEqual(result, fake_server) - - def test_create_service_instance_error(self): - fake_server = fake_compute.FakeServer(status='ERROR') - fake_port = fake_network.FakePort() - self.stubs.Set(self._driver, '_get_service_image', - mock.Mock(return_value='fake_image_id')) - self.stubs.Set(self._driver, '_get_key', - mock.Mock(return_value='fake_key_name')) - self.stubs.Set(self._driver, '_setup_network_for_instance', - mock.Mock(return_value=fake_port)) - self.stubs.Set(self._driver, - '_setup_connectivity_with_service_instances', - mock.Mock()) - self.stubs.Set(self._driver.compute_api, 'server_create', - mock.Mock(return_value=fake_server)) - self.stubs.Set(self._driver.compute_api, 'server_get', - mock.Mock(return_value=fake_server)) - self.stubs.Set(generic.socket, 'socket', mock.Mock()) - - self.assertRaises(exception.ManilaException, - self._driver._create_service_instance, self._context, - 'instance_name', self.share, None) - - self._driver.compute_api.server_create.assert_called_once() - self.assertFalse(self._driver.compute_api.server_get.called) - self.assertFalse(generic.socket.socket.called) - - def test_create_service_instance_failed_setup_connectivity(self): - fake_server = fake_compute.FakeServer(status='ERROR') - fake_port = fake_network.FakePort() - self.stubs.Set(self._driver, '_get_service_image', - mock.Mock(return_value='fake_image_id')) - self.stubs.Set(self._driver, '_get_key', - mock.Mock(return_value='fake_key_name')) - self.stubs.Set(self._driver, '_setup_network_for_instance', - mock.Mock(return_value=fake_port)) - self.stubs.Set(self._driver, - '_setup_connectivity_with_service_instances', - mock.Mock(side_effect=exception.ManilaException)) - self.stubs.Set(self._driver.neutron_api, 'delete_port', mock.Mock()) - self.stubs.Set(self._driver.compute_api, 'server_create', - mock.Mock(return_value=fake_server)) - self.stubs.Set(self._driver.compute_api, 'server_get', - mock.Mock(return_value=fake_server)) - self.stubs.Set(generic.socket, 'socket', mock.Mock()) - - self.assertRaises(exception.ManilaException, - self._driver._create_service_instance, - self._context, 'instance_name', self.share, None) - - self._driver.neutron_api.delete_port.\ - assert_called_once_with(fake_port['id']) - self.assertFalse(self._driver.compute_api.server_create.called) - self.assertFalse(self._driver.compute_api.server_get.called) - self.assertFalse(generic.socket.socket.called) - - def test_create_service_instance_no_key_and_password(self): - self.stubs.Set(self._driver, '_get_service_image', - mock.Mock(return_value='fake_image_id')) - self.stubs.Set(self._driver, '_get_key', - mock.Mock(return_value=None)) - self.assertRaises(exception.ManilaException, - self._driver._create_service_instance, self._context, - 'instance_name', self.share, None) - - def test_setup_network_for_instance(self): - fake_service_net = fake_network.FakeNetwork(subnets=[]) - fake_service_subnet = fake_network.\ - FakeSubnet(name=self.share['share_network_id']) - fake_router = fake_network.FakeRouter() - fake_port = fake_network.FakePort() - self.stubs.Set(self._driver.neutron_api, 'get_network', - mock.Mock(return_value=fake_service_net)) - self.stubs.Set(self._driver.neutron_api, 'subnet_create', - mock.Mock(return_value=fake_service_subnet)) - self.stubs.Set(self._driver.db, 'share_network_get', - mock.Mock(return_value='fake_share_network')) - self.stubs.Set(self._driver, '_get_private_router', - mock.Mock(return_value=fake_router)) - self.stubs.Set(self._driver.neutron_api, 'router_add_interface', - mock.Mock()) - self.stubs.Set(self._driver.neutron_api, 'create_port', - mock.Mock(return_value=fake_port)) - self.stubs.Set(self._driver, '_get_cidr_for_subnet', - mock.Mock(return_value='fake_cidr')) - - result = self._driver._setup_network_for_instance(self._context, - self.share, None) - - self._driver.neutron_api.get_network.\ - assert_called_once_with(self._driver.service_network_id) - self._driver._get_private_router.\ - assert_called_once_with('fake_share_network') - self._driver.neutron_api.router_add_interface.\ - assert_called_once_with('fake_router_id', 'fake_subnet_id') - self._driver.neutron_api.subnet_create.assert_called_once_with( - self._driver.service_tenant_id, - self._driver.service_network_id, - self.share['share_network_id'], - 'fake_cidr') - self._driver.neutron_api.create_port.assert_called_once_with( - self._driver.service_tenant_id, - self._driver.service_network_id, - subnet_id='fake_subnet_id', - fixed_ip=None, - device_owner='manila') - self._driver._get_cidr_for_subnet.assert_called_once_with([]) - self.assertEqual(result, fake_port) - - def test_get_private_router(self): - fake_net = fake_network.FakeNetwork() - fake_subnet = fake_network.FakeSubnet(gateway_ip='fake_ip') - fake_port = fake_network.FakePort(fixed_ips=[ - {'subnet_id': fake_subnet['id'], - 'ip_address': fake_subnet['gateway_ip']}], - device_id='fake_router_id') - fake_router = fake_network.FakeRouter(id='fake_router_id') - self.stubs.Set(self._driver.neutron_api, 'get_subnet', - mock.Mock(return_value=fake_subnet)) - self.stubs.Set(self._driver.neutron_api, 'list_ports', - mock.Mock(return_value=[fake_port])) - self.stubs.Set(self._driver.neutron_api, 'show_router', - mock.Mock(return_value=fake_router)) - - result = self._driver._get_private_router( - {'neutron_subnet_id': fake_subnet['id'], - 'neutron_net_id': fake_net['id']}) - - self._driver.neutron_api.get_subnet.\ - assert_called_once_with(fake_subnet['id']) - self._driver.neutron_api.list_ports.\ - assert_called_once_with(network_id=fake_net['id']) - self._driver.neutron_api.show_router.\ - assert_called_once_with(fake_router['id']) - self.assertEqual(result, fake_router) - - def test_get_private_router_exception(self): - fake_net = fake_network.FakeNetwork() - fake_subnet = fake_network.FakeSubnet(gateway_ip='fake_ip') - self.stubs.Set(self._driver.neutron_api, 'get_subnet', - mock.Mock(return_value=fake_subnet)) - self.stubs.Set(self._driver.neutron_api, 'list_ports', - mock.Mock(return_value=[])) - - self.assertRaises(exception.ManilaException, - self._driver._get_private_router, - {'neutron_subnet_id': fake_subnet['id'], - 'neutron_net_id': fake_net['id']}) - - def test_setup_connectivity_with_service_instances(self): - fake_subnet = fake_network.FakeSubnet(cidr='10.254.0.1/29') - fake_port = fake_network.FakePort(fixed_ips=[ - {'subnet_id': fake_subnet['id'], 'ip_address': '10.254.0.2'}], - mac_address='fake_mac_address') - - self.stubs.Set(self._driver, '_setup_service_port', - mock.Mock(return_value=fake_port)) - self.stubs.Set(self._driver.vif_driver, 'get_device_name', - mock.Mock(return_value='fake_interface_name')) - self.stubs.Set(self._driver.neutron_api, 'get_subnet', - mock.Mock(return_value=fake_subnet)) - self.stubs.Set(self._driver, '_clean_garbage', mock.Mock()) - self.stubs.Set(self._driver.vif_driver, 'plug', mock.Mock()) - device_mock = mock.Mock() - self.stubs.Set(generic.ip_lib, 'IPDevice', - mock.Mock(return_value=device_mock)) - - self._driver._setup_connectivity_with_service_instances() - - self._driver._setup_service_port.assert_called_once() - self._driver.vif_driver.get_device_name.\ - assert_called_once_with(fake_port) - self._driver.vif_driver.plug.assert_called_once_with(fake_port['id'], - 'fake_interface_name', fake_port['mac_address']) - self._driver.neutron_api.get_subnet.\ - assert_called_once_with(fake_subnet['id']) - self._driver.vif_driver.init_l3.assert_called_once() - generic.ip_lib.IPDevice.assert_called_once() - device_mock.route.pullup_route.assert_called_once() - self._driver._clean_garbage.assert_called_once_with(device_mock) - - def test_setup_service_port(self): - fake_service_port = fake_network.FakePort(device_id='manila-share') - fake_service_net = fake_network.FakeNetwork(subnets=[]) - self.stubs.Set(self._driver.neutron_api, 'list_ports', - mock.Mock(return_value=[])) - self.stubs.Set(self._driver, '_execute', - mock.Mock(return_value=('fake_host', ''))) - self.stubs.Set(self._driver.neutron_api, 'create_port', - mock.Mock(return_value=fake_service_port)) - self.stubs.Set(self._driver.neutron_api, 'get_network', - mock.Mock(return_value=fake_service_net)) - self.stubs.Set(self._driver.neutron_api, 'update_port_fixed_ips', - mock.Mock(return_value=fake_service_port)) - - result = self._driver._setup_service_port() - - self._driver.neutron_api.list_ports.\ - assert_called_once_with(device_id='manila-share') - self._driver.db.service_get_all_by_topic.assert_called_once() - self._driver.neutron_api.create_port.assert_called_once_with( - self._driver.service_tenant_id, - self._driver.service_network_id, - device_id='manila-share', - device_owner='manila:generic_driver', - host_id='fake_host' - ) - self._driver.neutron_api.get_network.assert_called_once() - self.assertFalse(self._driver.neutron_api.update_port_fixed_ips.called) - self.assertEqual(result, fake_service_port) - - def test_setup_service_port_ambigious_ports(self): - fake_service_port = fake_network.FakePort(device_id='manila-share') - self.stubs.Set(self._driver.neutron_api, 'list_ports', - mock.Mock(return_value=[fake_service_port, fake_service_port])) - self.assertRaises(exception.ManilaException, - self._driver._setup_service_port) - - def test_setup_service_port_exists(self): - fake_service_port = fake_network.FakePort(device_id='manila-share') - fake_service_net = fake_network.FakeNetwork(subnets=[]) - self.stubs.Set(self._driver.neutron_api, 'list_ports', - mock.Mock(return_value=[fake_service_port])) - self.stubs.Set(self._driver.db, 'service_get_all_by_topic', - mock.Mock(return_value=[{'host': 'fake_host'}])) - self.stubs.Set(self._driver.neutron_api, 'create_port', - mock.Mock(return_value=fake_service_port)) - self.stubs.Set(self._driver.neutron_api, 'get_network', - mock.Mock(return_value=fake_service_net)) - self.stubs.Set(self._driver.neutron_api, 'update_port_fixed_ips', - mock.Mock(return_value=fake_service_port)) - - result = self._driver._setup_service_port() - - self._driver.neutron_api.list_ports.\ - assert_called_once_with(device_id='manila-share') - self.assertFalse(self._driver.db.service_get_all_by_topic.called) - self.assertFalse(self._driver.neutron_api.create_port.called) - self._driver.neutron_api.get_network.assert_called_once() - self.assertFalse(self._driver.neutron_api.update_port_fixed_ips.called) - self.assertEqual(result, fake_service_port) - - def test_get_cidr_for_subnet(self): - serv_cidr = generic.netaddr.IPNetwork(CONF.service_network_cidr) - cidrs = serv_cidr.subnet(29) - cidr1 = str(cidrs.next()) - cidr2 = str(cidrs.next()) - - result = self._driver._get_cidr_for_subnet([]) - self.assertEqual(result, cidr1) - - fake_subnet = fake_network.FakeSubnet(cidr=cidr1) - result = self._driver._get_cidr_for_subnet([fake_subnet]) - self.assertEqual(result, cidr2) - def test_allocate_container(self): fake_vol = fake_volume.FakeVolume() self.stubs.Set(self._driver.volume_api, 'create', @@ -1036,7 +453,7 @@ class GenericShareDriverTestCase(test.TestCase): def test_create_share_from_snapshot(self): self._helper_nfs.create_export.return_value = 'fakelocation' - methods = ('_get_service_instance', '_allocate_container', + methods = ('get_service_instance', '_allocate_container', '_attach_volume', '_mount_device') for method in methods: self.stubs.Set(self._driver, method, mock.Mock()) @@ -1049,7 +466,7 @@ class GenericShareDriverTestCase(test.TestCase): def test_delete_share(self): fake_server = fake_compute.FakeServer() - self.stubs.Set(self._driver, '_get_service_instance', + self.stubs.Set(self._driver, 'get_service_instance', mock.Mock(return_value=fake_server)) self.stubs.Set(self._driver, '_unmount_device', mock.Mock()) self.stubs.Set(self._driver, '_detach_volume', mock.Mock()) @@ -1057,7 +474,7 @@ class GenericShareDriverTestCase(test.TestCase): self._driver.delete_share(self._context, self.share) - self._driver._get_service_instance.assert_called_once() + self._driver.get_service_instance.assert_called_once() self._driver._unmount_device.assert_called_once() self._driver._detach_volume.assert_called_once() self._driver._deallocate_container.assert_called_once() @@ -1097,7 +514,7 @@ class GenericShareDriverTestCase(test.TestCase): def test_ensure_share(self): self._helper_nfs.create_export.return_value = 'fakelocation' - methods = ('_get_service_instance', '_get_volume', + methods = ('get_service_instance', '_get_volume', '_attach_volume', '_mount_device') for method in methods: self.stubs.Set(self._driver, method, mock.Mock()) @@ -1108,11 +525,11 @@ class GenericShareDriverTestCase(test.TestCase): def test_allow_access(self): fake_server = fake_compute.FakeServer() access = {'access_type': 'ip', 'access_to': 'fake_dest'} - self.stubs.Set(self._driver, '_get_service_instance', + self.stubs.Set(self._driver, 'get_service_instance', mock.Mock(return_value=fake_server)) self._driver.allow_access(self._context, self.share, access) - self._driver._get_service_instance.assert_called_once() + self._driver.get_service_instance.assert_called_once() self._driver._helpers[self.share['share_proto']].\ allow_access.assert_called_once_with(fake_server, self.share['name'], @@ -1122,11 +539,11 @@ class GenericShareDriverTestCase(test.TestCase): def test_deny_access(self): fake_server = fake_compute.FakeServer() access = {'access_type': 'ip', 'access_to': 'fake_dest'} - self.stubs.Set(self._driver, '_get_service_instance', + self.stubs.Set(self._driver, 'get_service_instance', mock.Mock(return_value=fake_server)) self._driver.deny_access(self._context, self.share, access) - self._driver._get_service_instance.assert_called_once() + self._driver.get_service_instance.assert_called_once() self._driver._helpers[self.share['share_proto']].\ deny_access.assert_called_once_with(fake_server, self.share['name'],