From 53b40423f48bf33b9168d1ed9a80bd5c2c6e701d Mon Sep 17 00:00:00 2001 From: Valeriy Ponomaryov Date: Thu, 22 Jan 2015 15:40:28 +0200 Subject: [PATCH] Implement additional driver mode for Generic driver Add possibility to run Generic driver without handling of share servers. It allows us to create shares, without share-networks defined, on one specific Nova VM. Added three (3) new config options that are used only with new mode: - 'service_instance_name_or_id' expects string with name or id of Nova VM. - 'service_net_name_or_ip' expects name of network that Nova VM is switched to or IP itself. Used for handle operation of share. - 'tenant_net_name_or_ip' expects name of network that Nova VM is switched to or IP itself. Used for exporting share to end users. All three new config options are required if new mode is enabled. They will be skipped if driver mode with handling of share servers is enabled. Implements bp single-svm-mode-for-generic-driver Change-Id: I48864b002e4174728136d093b1a17dad2edec605 --- manila/compute/nova.py | 11 ++ manila/opts.py | 4 +- manila/share/drivers/generic.py | 39 ++-- manila/share/drivers/service_instance.py | 169 ++++++++++++---- manila/tests/compute/test_nova.py | 13 +- manila/tests/fake_compute.py | 3 + manila/tests/share/drivers/test_generic.py | 118 +++++++++-- .../share/drivers/test_service_instance.py | 184 +++++++++++++++--- 8 files changed, 446 insertions(+), 95 deletions(-) diff --git a/manila/compute/nova.py b/manila/compute/nova.py index 30b9316c..52126b19 100644 --- a/manila/compute/nova.py +++ b/manila/compute/nova.py @@ -20,6 +20,7 @@ import sys from novaclient import exceptions as nova_exception from novaclient import service_catalog +from novaclient import utils from novaclient.v1_1 import client as nova_client from novaclient.v1_1.contrib import assisted_volume_snapshots from novaclient.v1_1 import servers as nova_servers @@ -27,6 +28,7 @@ from oslo_config import cfg from manila.db import base from manila import exception +from manila.i18n import _ from manila.openstack.common import log as logging @@ -181,6 +183,15 @@ class API(base.Base): novaclient(context).servers.get(instance_id) ) + def server_get_by_name_or_id(self, context, instance_name_or_id): + try: + server = utils.find_resource( + novaclient(context).servers, instance_name_or_id) + except nova_exception.CommandError as e: + msg = _("Failed to get Nova VM. %s") % e + raise exception.ManilaException(msg) + return _untranslate_server_summary_view(server) + def server_list(self, context, search_opts=None, all_tenants=False): if search_opts is None: search_opts = {} diff --git a/manila/opts.py b/manila/opts.py index ecc4169b..eebbdf58 100644 --- a/manila/opts.py +++ b/manila/opts.py @@ -109,7 +109,9 @@ _global_opt_lists = [ manila.share.drivers.huawei.huawei_nas.huawei_opts, manila.share.drivers.ibm.gpfs.gpfs_share_opts, manila.share.drivers.netapp.cluster_mode.NETAPP_NAS_OPTS, - manila.share.drivers.service_instance.server_opts, + manila.share.drivers.service_instance.common_opts, + manila.share.drivers.service_instance.no_share_servers_handling_mode_opts, + manila.share.drivers.service_instance.share_servers_handling_mode_opts, manila.share.drivers.zfssa.zfssashare.ZFSSA_OPTS, manila.share.manager.share_manager_opts, manila.volume._volume_opts, diff --git a/manila/share/drivers/generic.py b/manila/share/drivers/generic.py index df2bc0a9..5558fac1 100644 --- a/manila/share/drivers/generic.py +++ b/manila/share/drivers/generic.py @@ -85,22 +85,35 @@ CONF.register_opts(share_opts) def ensure_server(f): + def wrap(self, *args, **kwargs): - server = kwargs.get('share_server') context = args[0] - if not server: - # For now generic driver does not support flat networking. - # When we implement flat networking in generic driver - # we will not need share server to be passed and - # will change this logic. - raise exception.ManilaException(_('Share server not found.')) + server = kwargs.get('share_server') + + if not self.driver_handles_share_servers: + if not server: + server = self.service_instance_manager.get_common_server() + kwargs['share_server'] = server + else: + raise exception.ManilaException( + _("Share server handling is not available. " + "But 'share_server' was provided. '%s'. " + "Share network should not be used.") % server['id']) + elif not server: + raise exception.ManilaException( + _("Share server handling is enabled. But 'share_server' " + "is not provided. Make sure you used 'share_network'.")) + if not server.get('backend_details'): - raise exception.ManilaException(_('Share server backend ' - 'details missing.')) + raise exception.ManilaException( + _("Share server '%s' does not have backend details.") % + server['id']) if not self.service_instance_manager.ensure_service_instance( context, server['backend_details']): raise exception.ServiceInstanceUnavailable() + return f(self, *args, **kwargs) + return wrap @@ -109,11 +122,11 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): def __init__(self, db, *args, **kwargs): """Do initialization.""" - super(GenericShareDriver, self).__init__(True, *args, **kwargs) + super(GenericShareDriver, self).__init__( + [False, True], *args, **kwargs) self.admin_context = context.get_admin_context() self.db = db self.configuration.append_config_values(share_opts) - self.configuration.append_config_values(service_instance.server_opts) self._helpers = {} self.backend_name = self.configuration.safe_get( 'share_backend_name') or "Cinder_Volumes" @@ -479,6 +492,8 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): def delete_share(self, context, share, share_server=None): """Deletes share.""" + if not self.driver_handles_share_servers: + share_server = self.service_instance_manager.get_common_server() if self._is_share_server_active(context, share_server): self._get_helper(share).remove_export( share_server['backend_details'], share['name']) @@ -490,7 +505,6 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): # with any reason that caused absence of Nova instances. self._deallocate_container(self.admin_context, share) - @ensure_server def create_snapshot(self, context, snapshot, share_server=None): """Creates a snapshot.""" volume = self._get_volume(self.admin_context, snapshot['share_id']) @@ -515,7 +529,6 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver): 'created in %ss. Giving up') % self.configuration.max_time_to_create_volume) - @ensure_server def delete_snapshot(self, context, snapshot, share_server=None): """Deletes a snapshot.""" volume_snapshot = self._get_volume_snapshot(self.admin_context, diff --git a/manila/share/drivers/service_instance.py b/manila/share/drivers/service_instance.py index 95011ca8..e23f4513 100644 --- a/manila/share/drivers/service_instance.py +++ b/manila/share/drivers/service_instance.py @@ -1,4 +1,5 @@ # Copyright (c) 2014 NetApp, Inc. +# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -25,7 +26,7 @@ from oslo_config import cfg from oslo_utils import importutils import six -from manila.common import constants +from manila.common import constants as const from manila import compute from manila import context from manila import exception @@ -39,7 +40,7 @@ from manila import utils LOG = logging.getLogger(__name__) -server_opts = [ +share_servers_handling_mode_opts = [ cfg.StrOpt('service_image_name', default='manila-service-image', help="Name of image in glance, that will be used to create " @@ -47,11 +48,6 @@ server_opts = [ 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 " @@ -59,12 +55,6 @@ server_opts = [ 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.StrOpt('service_instance_security_group', default="manila-service", help="Name of security group, that will be used for " @@ -93,8 +83,48 @@ server_opts = [ help='Attach share server directly to share network.'), ] +no_share_servers_handling_mode_opts = [ + cfg.StrOpt( + "service_instance_name_or_id", + help="Name or ID of service instance in Nova to use for share " + "exports. Used only when share servers handling is disabled."), + cfg.StrOpt( + "service_net_name_or_ip", + help="Can be either name of network that is used by service " + "instance within Nova to get IP address or IP address itself " + "for managing shares there. " + "Used only when share servers handling is disabled."), + cfg.StrOpt( + "tenant_net_name_or_ip", + help="Can be either name of network that is used by service " + "instance within Nova to get IP address or IP address itself " + "for exporting shares. " + "Used only when share servers handling is disabled."), +] + +common_opts = [ + cfg.StrOpt( + "service_instance_user", + help="User in service instance that will be used for authentication."), + cfg.StrOpt( + "service_instance_password", + default=None, + secret=True, + help="Password for service instance user."), + cfg.StrOpt( + "path_to_private_key", + default="~/.ssh/id_rsa", + help="Path to host's private key."), + cfg.IntOpt( + "max_time_to_build_instance", + default=300, + help="Maximum time in seconds to wait for creating service instance."), +] + CONF = cfg.CONF -CONF.register_opts(server_opts) +CONF.register_opts(common_opts) +CONF.register_opts(no_share_servers_handling_mode_opts) +CONF.register_opts(share_servers_handling_mode_opts) lock = threading.Lock() @@ -126,20 +156,45 @@ class ServiceInstanceManager(object): value = CONF.get(key) return value - def __init__(self, db, *args, **kwargs): + def __init__(self, db, driver_config): """Do initialization.""" super(ServiceInstanceManager, self).__init__() - self.driver_config = None - if "driver_config" in kwargs: - self.driver_config = kwargs["driver_config"] + self.db = db + self.driver_config = driver_config + + self.driver_config.append_config_values(common_opts) + if self.get_config_option("driver_handles_share_servers"): + self.driver_config.append_config_values( + share_servers_handling_mode_opts) + else: + self.driver_config.append_config_values( + no_share_servers_handling_mode_opts) + if not self.get_config_option("service_instance_user"): - raise exception.ServiceInstanceException(_('Service instance user ' - 'is not specified')) + 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.db = db + self.path_to_private_key = self.get_config_option( + "path_to_private_key") + self.max_time_to_build_instance = self.get_config_option( + "max_time_to_build_instance") + + if self.get_config_option("driver_handles_share_servers"): + self.neutron_api = neutron.API() + self.path_to_public_key = self.get_config_option( + "path_to_public_key") + self.connect_share_server_to_tenant_network = ( + self.get_config_option( + 'connect_share_server_to_tenant_network')) + self._get_service_tenant_id() + self.service_network_id = self._get_service_network() + self.vif_driver = importutils.import_class( + self.get_config_option("interface_driver"))() + self._setup_connectivity_with_service_instances() + + def _get_service_tenant_id(self): attempts = 5 while attempts: try: @@ -152,17 +207,57 @@ class ServiceInstanceManager(object): else: raise exception.ServiceInstanceException(_('Can not receive ' 'service tenant id.')) - self.service_network_id = self._get_service_network() - self.vif_driver = importutils.import_class( - self.get_config_option("interface_driver"))() - self._setup_connectivity_with_service_instances() - self.max_time_to_build_instance = self.get_config_option( - "max_time_to_build_instance") - self.path_to_private_key = self.get_config_option( - "path_to_private_key") - self.path_to_public_key = self.get_config_option("path_to_public_key") - self.connect_share_server_to_tenant_network = self.get_config_option( - 'connect_share_server_to_tenant_network') + + def get_common_server(self): + data = { + 'public_address': None, + 'private_address': None, + 'service_net_name_or_ip': self.get_config_option( + 'service_net_name_or_ip'), + 'tenant_net_name_or_ip': self.get_config_option( + 'tenant_net_name_or_ip'), + } + + data['instance'] = self.compute_api.server_get_by_name_or_id( + self.admin_context, + self.get_config_option('service_instance_name_or_id')) + + if netaddr.valid_ipv4(data['service_net_name_or_ip']): + data['private_address'] = [data['service_net_name_or_ip']] + else: + data['private_address'] = self._get_addresses_by_network_name( + data['service_net_name_or_ip'], data['instance']) + + if netaddr.valid_ipv4(data['tenant_net_name_or_ip']): + data['public_address'] = [data['tenant_net_name_or_ip']] + else: + data['public_address'] = self._get_addresses_by_network_name( + data['tenant_net_name_or_ip'], data['instance']) + + if not (data['public_address'] and data['private_address']): + raise exception.ManilaException( + "Can not find one of net addresses for service instance. " + "Instance: %(instance)s, " + "private_address: %(private_address)s, " + "public_address: %(public_address)s." % data) + + share_server = { + 'username': self.get_config_option('service_instance_user'), + 'password': self.get_config_option('service_instance_password'), + 'pk_path': self.path_to_private_key, + 'ip': data['private_address'][0], # for handling + 'public_address': data['public_address'][0], # for exports + 'instance_id': data['instance']['id'], + } + return {'backend_details': share_server} + + def _get_addresses_by_network_name(self, net_name, server): + net_ips = [] + if 'networks' in server and net_name in server['networks']: + net_ips = server['networks'][net_name] + elif 'addresses' in server and net_name in server['addresses']: + net_ips = [addr['addr'] for addr in server['addresses'][net_name]] + return net_ips @utils.synchronized("service_instance_get_service_network", external=True) def _get_service_network(self): @@ -193,11 +288,7 @@ class ServiceInstanceManager(object): def _get_server_ip(self, server): """Returns service vms ip address.""" net_name = self.get_config_option("service_network_name") - net_ips = [] - if 'networks' in server and net_name in server['networks']: - net_ips = server['networks'][net_name] - elif 'addresses' in server and net_name in server['addresses']: - net_ips = [addr['addr'] for addr in server['addresses'][net_name]] + net_ips = self._get_addresses_by_network_name(net_name, server) if not net_ips: msg = _("Failed to get service instance ip address. " "Service network name is '%(net_name)s' " @@ -234,7 +325,7 @@ class ServiceInstanceManager(object): LOG.debug("Creating security group with name '%s'.", name) sg = self.compute_api.security_group_create( context, name, description) - for protocol, ports in constants.SERVICE_INSTANCE_SECGROUP_DATA: + for protocol, ports in const.SERVICE_INSTANCE_SECGROUP_DATA: self.compute_api.security_group_rule_create( context, parent_group_id=sg.id, diff --git a/manila/tests/compute/test_nova.py b/manila/tests/compute/test_nova.py index 2fe030ad..80034c6d 100644 --- a/manila/tests/compute/test_nova.py +++ b/manila/tests/compute/test_nova.py @@ -14,6 +14,7 @@ import mock from novaclient import exceptions as nova_exception +from novaclient import utils from novaclient.v1_1 import servers as nova_servers from manila.compute import nova @@ -87,7 +88,17 @@ class NovaApiTestCase(test.TestCase): def test_server_get(self): instance_id = 'instance_id1' result = self.api.server_get(self.ctx, instance_id) - self.assertEqual(result['id'], instance_id) + self.assertEqual(instance_id, result['id']) + + def test_server_get_by_name_or_id(self): + instance_id = 'instance_id1' + server = {'id': instance_id, 'fake_key': 'fake_value'} + self.stubs.Set(utils, 'find_resource', mock.Mock(return_value=server)) + + result = self.api.server_get_by_name_or_id(self.ctx, instance_id) + + self.assertEqual(instance_id, result['id']) + utils.find_resource.assert_called_once_with(mock.ANY, instance_id) def test_server_get_failed(self): nova.novaclient.side_effect = nova_exception.NotFound(404) diff --git a/manila/tests/fake_compute.py b/manila/tests/fake_compute.py index d5ce2682..b4f99313 100644 --- a/manila/tests/fake_compute.py +++ b/manila/tests/fake_compute.py @@ -89,6 +89,9 @@ class API(object): def server_get(self, *args, **kwargs): pass + def server_get_by_name_or_id(self, *args, **kwargs): + pass + def keypair_list(self, *args, **kwargs): pass diff --git a/manila/tests/share/drivers/test_generic.py b/manila/tests/share/drivers/test_generic.py index 486d2247..481d4d1b 100644 --- a/manila/tests/share/drivers/test_generic.py +++ b/manila/tests/share/drivers/test_generic.py @@ -1,4 +1,5 @@ # Copyright (c) 2014 NetApp, Inc. +# Copyright (c) 2015 Mirantis, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -624,6 +625,27 @@ class GenericShareDriverTestCase(test.TestCase): self._helper_nfs.create_export.assert_called_once_with( self.server['backend_details'], self.share['name']) + def test_delete_share_no_share_servers_handling(self): + self.stubs.Set(self._driver, '_deallocate_container', mock.Mock()) + self.stubs.Set( + self._driver.service_instance_manager, + 'get_common_server', mock.Mock(return_value=self.server)) + self.stubs.Set( + self._driver.service_instance_manager, + 'ensure_service_instance', mock.Mock(return_value=False)) + + CONF.set_default('driver_handles_share_servers', False) + + self._driver.delete_share(self._context, self.share) + + self._driver.service_instance_manager.get_common_server.\ + assert_called_once_with() + self._driver._deallocate_container.assert_called_once_with( + self._driver.admin_context, self.share) + self._driver.service_instance_manager.ensure_service_instance.\ + assert_called_once_with( + self._context, self.server['backend_details']) + def test_delete_share(self): self.stubs.Set(self._driver, '_unmount_device', mock.Mock()) self.stubs.Set(self._driver, '_detach_volume', mock.Mock()) @@ -641,6 +663,9 @@ class GenericShareDriverTestCase(test.TestCase): self.server['backend_details']) self._driver._deallocate_container.assert_called_once_with( self._driver.admin_context, self.share) + self._driver.service_instance_manager.ensure_service_instance.\ + assert_called_once_with( + self._context, self.server['backend_details']) def test_delete_share_without_share_server(self): self.stubs.Set(self._driver, '_unmount_device', mock.Mock()) @@ -684,17 +709,20 @@ class GenericShareDriverTestCase(test.TestCase): self.stubs.Set(self._driver, '_detach_volume', mock.Mock()) self.stubs.Set(self._driver, '_deallocate_container', mock.Mock()) - with mock.patch.object(self._driver.service_instance_manager, - 'ensure_service_instance', - mock.Mock(return_value=False)): - self._driver.delete_share( - self._context, self.share, share_server=self.server) + self.stubs.Set( + self._driver.service_instance_manager, + 'ensure_service_instance', mock.Mock(return_value=False)) + self._driver.delete_share( + self._context, self.share, share_server=self.server) - self.assertFalse(self._helper_nfs.remove_export.called) - self.assertFalse(self._driver._unmount_device.called) - self.assertFalse(self._driver._detach_volume.called) - self._driver._deallocate_container.assert_called_once_with( - self._driver.admin_context, self.share) + self.assertFalse(self._helper_nfs.remove_export.called) + self.assertFalse(self._driver._unmount_device.called) + self.assertFalse(self._driver._detach_volume.called) + self._driver._deallocate_container.assert_called_once_with( + self._driver.admin_context, self.share) + self._driver.service_instance_manager.ensure_service_instance.\ + assert_called_once_with( + self._context, self.server['backend_details']) def test_create_snapshot(self): fake_vol = fake_volume.FakeVolume() @@ -787,7 +815,7 @@ class GenericShareDriverTestCase(test.TestCase): self.assertRaises(exception.InvalidShare, self._driver._get_helper, share) - def test_setup_network(self): + def test__setup_server(self): sim = self._driver.instance_manager net_info = {'server_id': 'fake', 'neutron_net_id': 'fake-net-id', @@ -799,7 +827,7 @@ class GenericShareDriverTestCase(test.TestCase): 'fake-net-id', 'fake-subnet-id') - def test_setup_network_revert(self): + def test__setup_server_revert(self): def raise_exception(*args, **kwargs): raise exception.ServiceInstanceException @@ -814,7 +842,7 @@ class GenericShareDriverTestCase(test.TestCase): self._driver.setup_server, net_info) - def test_teardown_network(self): + def test__teardown_server(self): server_details = { 'instance_id': 'fake_instance_id', 'subnet_id': 'fake_subnet_id', @@ -929,6 +957,70 @@ class GenericShareDriverTestCase(test.TestCase): self.assertEqual('Open Source', result['vendor_name']) +@generic.ensure_server +def fake(driver_instance, context, share_server=None): + return share_server + + +@ddt.ddt +class GenericDriverEnsureServerTestCase(test.TestCase): + + def setUp(self): + super(GenericDriverEnsureServerTestCase, self).setUp() + self._context = context.get_admin_context() + self.server = {'id': 'fake_id', 'backend_details': {'foo': 'bar'}} + self.dhss_false = type( + 'Fake', (object,), {'driver_handles_share_servers': False}) + self.dhss_true = type( + 'Fake', (object,), {'driver_handles_share_servers': True}) + + def test_share_servers_are_not_handled_server_not_provided(self): + self.dhss_false.service_instance_manager = mock.Mock() + self.dhss_false.service_instance_manager.get_common_server = ( + mock.Mock(return_value=self.server)) + self.dhss_false.service_instance_manager.ensure_service_instance = ( + mock.Mock(return_value=True)) + + actual = fake(self.dhss_false, self._context) + + self.assertEqual(self.server, actual) + self.dhss_false.service_instance_manager.\ + get_common_server.assert_called_once_with() + self.dhss_false.service_instance_manager.ensure_service_instance.\ + assert_called_once_with( + self._context, self.server['backend_details']) + + @ddt.data({'id': 'without_details'}, + {'id': 'with_details', 'backend_details': {'foo': 'bar'}}) + def test_share_servers_are_not_handled_server_provided(self, server): + self.assertRaises( + exception.ManilaException, + fake, self.dhss_false, self._context, share_server=server) + + def test_share_servers_are_handled_server_provided(self): + self.dhss_true.service_instance_manager = mock.Mock() + self.dhss_true.service_instance_manager.ensure_service_instance = ( + mock.Mock(return_value=True)) + + actual = fake(self.dhss_true, self._context, share_server=self.server) + + self.assertEqual(self.server, actual) + self.dhss_true.service_instance_manager.ensure_service_instance.\ + assert_called_once_with( + self._context, self.server['backend_details']) + + def test_share_servers_are_handled_invalid_server_provided(self): + server = {'id': 'without_details'} + + self.assertRaises( + exception.ManilaException, + fake, self.dhss_true, self._context, share_server=server) + + def test_share_servers_are_handled_server_not_provided(self): + self.assertRaises( + exception.ManilaException, fake, self.dhss_true, self._context) + + class NFSHelperTestCase(test.TestCase): """Test case for NFS helper of generic driver.""" diff --git a/manila/tests/share/drivers/test_service_instance.py b/manila/tests/share/drivers/test_service_instance.py index f9c39774..0aafa4a9 100644 --- a/manila/tests/share/drivers/test_service_instance.py +++ b/manila/tests/share/drivers/test_service_instance.py @@ -18,12 +18,15 @@ import copy import os +import ddt import mock from oslo_config import cfg +from oslo_utils import importutils import six from manila import context from manila import exception +from manila.share import configuration from manila.share.drivers import service_instance from manila import test from manila.tests import fake_compute @@ -34,23 +37,60 @@ from manila import utils CONF = cfg.CONF +def fake_get_config_option(key): + if key == 'driver_handles_share_servers': + return True + elif key == 'service_instance_password': + return None + elif key == 'service_instance_user': + return 'fake_user' + elif key == 'service_network_name': + return 'fake_service_network_name' + elif key == 'service_instance_flavor_id': + return 100 + elif key == 'service_instance_name_template': + return 'fake_manila_service_instance_%s' + elif key == 'service_image_name': + return 'fake_service_image_name' + elif key == 'manila_service_keypair_name': + return 'fake_manila_service_keypair_name' + elif key == 'path_to_private_key': + return 'fake_path_to_private_key' + elif key == 'path_to_public_key': + return 'fake_path_to_public_key' + elif key == 'max_time_to_build_instance': + return 500 + elif key == 'connect_share_server_to_tenant_network': + return False + elif key == 'service_network_cidr': + return '99.254.0.0/16' + elif key == 'service_network_division_mask': + return 25 + else: + return mock.Mock() + + +@ddt.ddt class ServiceInstanceManagerTestCase(test.TestCase): """Tests InstanceManager.""" def setUp(self): super(ServiceInstanceManagerTestCase, self).setUp() self._context = context.get_admin_context() + self.config = configuration.Configuration(None) + self.config.safe_get = mock.Mock(side_effect=fake_get_config_option) 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) + self.stubs.Set(importutils, 'import_class', mock.Mock()) with mock.patch.object(service_instance.ServiceInstanceManager, '_setup_connectivity_with_service_instances', mock.Mock()): - self._manager = service_instance.ServiceInstanceManager(self._db, - {}) + self._manager = service_instance.ServiceInstanceManager( + self._db, self.config) self._manager.service_tenant_id = 'service tenant id' self._manager.service_network_id = 'service network id' self._manager.admin_context = self._context @@ -70,12 +110,12 @@ class ServiceInstanceManagerTestCase(test.TestCase): 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['name'] = self._manager.get_config_option('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']) + self.assertEqual(net1['id'], result) def test_get_service_network_net_does_not_exists(self): net = fake_network.FakeNetwork() @@ -84,10 +124,11 @@ class ServiceInstanceManagerTestCase(test.TestCase): 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']) + self.assertEqual(net['id'], result) def test_get_service_network_ambiguos(self): - net = fake_network.FakeNetwork(name=CONF.service_network_name) + net = fake_network.FakeNetwork( + name=self._manager.get_config_option('service_network_name')) self.stubs.Set(self._manager.neutron_api, 'get_all_tenant_networks', mock.Mock(return_value=[net, net])) self.assertRaises(exception.ManilaException, @@ -96,14 +137,14 @@ class ServiceInstanceManagerTestCase(test.TestCase): 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') + self.assertEqual( + 'fake_manila_service_instance_None_fake_share_network_id', result) def test_get_server_ip_found_in_networks_section(self): ip = '10.0.0.1' fake_server = { 'networks': { - CONF.service_network_name: [ip], + self._manager.get_config_option('service_network_name'): [ip], } } result = self._manager._get_server_ip(fake_server) @@ -113,7 +154,7 @@ class ServiceInstanceManagerTestCase(test.TestCase): ip = '10.0.0.1' fake_server = { 'addresses': { - CONF.service_network_name: [ + self._manager.get_config_option('service_network_name'): [ {'addr': ip, 'version': 4, } ], } @@ -311,8 +352,9 @@ class ServiceInstanceManagerTestCase(test.TestCase): self.assertFalse(result) def test_get_key_create_new(self): - fake_keypair = fake_compute.FakeKeypair( - name=CONF.manila_service_keypair_name) + keypair_name = self._manager.get_config_option( + 'manila_service_keypair_name') + fake_keypair = fake_compute.FakeKeypair(name=keypair_name) self.stubs.Set(self._manager.compute_api, 'keypair_list', mock.Mock(return_value=[])) self.stubs.Set(self._manager.compute_api, 'keypair_import', @@ -320,17 +362,20 @@ class ServiceInstanceManagerTestCase(test.TestCase): result = self._manager._get_key(self._context) - self.assertEqual(result, - (fake_keypair.name, - os.path.expanduser(CONF.path_to_private_key))) + self.assertEqual( + (fake_keypair.name, + os.path.expanduser(self._manager.get_config_option( + 'path_to_private_key'))), + result) self._manager.compute_api.keypair_list.assert_called_once_with( self._context) self._manager.compute_api.keypair_import.assert_called_once_with( - self._context, 'manila-service', '') + self._context, keypair_name, '') def test_get_key_exists(self): fake_keypair = fake_compute.FakeKeypair( - name=CONF.manila_service_keypair_name, + name=self._manager.get_config_option( + 'manila_service_keypair_name'), public_key='fake_public_key') self.stubs.Set(self._manager.compute_api, 'keypair_list', mock.Mock(return_value=[fake_keypair])) @@ -344,13 +389,16 @@ class ServiceInstanceManagerTestCase(test.TestCase): self._manager.compute_api.keypair_list.assert_called_once_with( self._context) self.assertFalse(self._manager.compute_api.keypair_import.called) - self.assertEqual(result, - (fake_keypair.name, - os.path.expanduser(CONF.path_to_private_key))) + self.assertEqual( + (fake_keypair.name, + os.path.expanduser(self._manager.get_config_option( + 'path_to_private_key'))), + result) def test_get_key_exists_recreate(self): fake_keypair = fake_compute.FakeKeypair( - name=CONF.manila_service_keypair_name, + name=self._manager.get_config_option( + 'manila_service_keypair_name'), public_key='fake_public_key1') self.stubs.Set(self._manager.compute_api, 'keypair_list', mock.Mock(return_value=[fake_keypair])) @@ -369,9 +417,11 @@ class ServiceInstanceManagerTestCase(test.TestCase): self._context, fake_keypair.id) self._manager.compute_api.keypair_import.assert_called_once_with( self._context, fake_keypair.name, 'fake_public_key2') - self.assertEqual(result, - (fake_keypair.name, - os.path.expanduser(CONF.path_to_private_key))) + self.assertEqual( + (fake_keypair.name, + os.path.expanduser(self._manager.get_config_option( + 'path_to_private_key'))), + result) def test_get_key_keypath_to_public_not_set(self): self._manager.path_to_public_key = None @@ -414,14 +464,15 @@ class ServiceInstanceManagerTestCase(test.TestCase): self.assertEqual(result, (None, None)) def test_get_service_image(self): - fake_image1 = fake_compute.FakeImage(name=CONF.service_image_name) + fake_image1 = fake_compute.FakeImage( + name=self._manager.get_config_option('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) + self.assertEqual(fake_image1.id, result) def test_get_service_image_not_found(self): self.stubs.Set(self._manager.compute_api, 'image_list', @@ -834,8 +885,9 @@ class ServiceInstanceManagerTestCase(test.TestCase): def test_get_cidr_for_subnet(self): serv_cidr = service_instance.netaddr.IPNetwork( - CONF.service_network_cidr) - fake_division_mask = CONF.service_network_division_mask + self._manager.get_config_option('service_network_cidr')) + fake_division_mask = self._manager.get_config_option( + 'service_network_division_mask') cidrs = serv_cidr.subnet(fake_division_mask) cidr1 = six.text_type(next(cidrs)) cidr2 = six.text_type(next(cidrs)) @@ -936,3 +988,79 @@ class ServiceInstanceManagerTestCase(test.TestCase): self._manager.neutron_api.update_subnet.assert_has_calls([ mock.call(subnet_id, ''), ]) + + @ddt.data( + {'s': 'fake_net_s', 't': 'fake_net_t'}, + {'s': 'fake_net_s', 't': '12.34.56.78'}, + {'s': '98.76.54.123', 't': 'fake_net_t'}, + {'s': '98.76.54.123', 't': '12.34.56.78'}) + @ddt.unpack + def test_get_common_server_valid_cases(self, s, t): + self._get_common_server(s, t, True) + + @ddt.data( + {'s': 'fake_net_s', 't': 'fake'}, + {'s': 'fake', 't': 'fake_net_t'}, + {'s': 'fake', 't': 'fake'}, + {'s': '98.76.54.123', 't': '12.12.12.1212'}, + {'s': '12.12.12.1212', 't': '12.34.56.78'}, + {'s': '12.12.12.1212', 't': '12.12.12.1212'}) + @ddt.unpack + def test_get_common_server_invalid_cases(self, s, t): + self._get_common_server(s, t, False) + + def _get_common_server(self, s, t, is_valid=True): + fake_instance_id = 'fake_instance_id' + fake_user = 'fake_user' + fake_pass = 'fake_pass' + fake_net_s = 'fake_net_s' + fake_addr_s = '98.76.54.123' + fake_net_t = 'fake_net_t' + fake_addr_t = '12.34.56.78' + fake_server = { + 'id': fake_instance_id, + 'networks': {fake_net_s: [fake_addr_s], fake_net_t: [fake_addr_t]}, + 'addresses': {fake_net_s: {'addr': fake_addr_s}, + fake_net_t: {'addr': fake_addr_t}}, + } + expected = { + 'backend_details': { + 'username': fake_user, + 'password': fake_pass, + 'pk_path': self._manager.path_to_private_key, + 'ip': fake_addr_s, + 'public_address': fake_addr_t, + 'instance_id': fake_instance_id, + } + } + + def fake_get_config_option(attr): + if attr == 'service_net_name_or_ip': + return s + elif attr == 'tenant_net_name_or_ip': + return t + elif attr == 'service_instance_name_or_id': + return fake_instance_id + elif attr == 'service_instance_user': + return fake_user + elif attr == 'service_instance_password': + return fake_pass + else: + raise exception.ManilaException("Wrong test data provided.") + + self.mock_object( + self._manager.compute_api, 'server_get_by_name_or_id', + mock.Mock(return_value=fake_server)) + self.mock_object( + self._manager, 'get_config_option', + mock.Mock(side_effect=fake_get_config_option)) + + if is_valid: + actual = self._manager.get_common_server() + self.assertEqual(expected, actual) + else: + self.assertRaises( + exception.ManilaException, + self._manager.get_common_server) + self.assertTrue( + self._manager.compute_api.server_get_by_name_or_id.called)