Share Migration support in generic driver

Added minimal Share Migration support to generic driver,
so the Generic Migration procedure can work.

The approach to support is to add a field to Share Server
backend_details named service_ip with the port IP address that
allows the controller to access the Share Servers (same IP address
which they SSH through), this IP is used to return the access rule
required for the generic migration procedure.

For "driver_handles_share_servers=False" mode, configuration falls
back to common implementation in Core code and its parameters.

Optimized migration for Generic Driver will be included
as a separate patch.

Change-Id: Ie7d19a44c9ab05c5a6a79c30478be23b2e8a3ba2
Implements: blueprint share-migration
This commit is contained in:
Rodrigo Barbieri 2015-09-06 22:52:56 -03:00
parent 0524ab8fc2
commit f5add61a8d
5 changed files with 115 additions and 13 deletions

View File

@ -232,6 +232,10 @@ class ShareMigrationFailed(ManilaException):
message = _("Share migration failed: %(reason)s")
class ServiceIPNotFound(ManilaException):
message = _("Share migration failed: %(reason)s")
class ShareServerNotCreated(ManilaException):
message = _("Share server %(share_server_id)s failed on creation.")

View File

@ -196,6 +196,16 @@ class GenericShareDriver(driver.ExecuteMixin, driver.ShareDriver):
"No protocol helpers selected for Generic Driver. "
"Please specify using config option 'share_helpers'.")
def _get_access_rule_for_data_copy(self, context, share, share_server):
if not self.driver_handles_share_servers:
service_ip = self.configuration.safe_get(
'migration_data_copy_node_ip')
else:
service_ip = share_server['backend_details']['service_ip']
return {'access_type': 'ip',
'access_level': 'rw',
'access_to': service_ip}
@ensure_server
def create_share(self, context, share, share_server=None):
"""Creates share."""

View File

@ -419,6 +419,7 @@ class ServiceInstanceManager(object):
'password': self.get_config_option('service_instance_password'),
'username': self.get_config_option('service_instance_user'),
'public_address': server['public_address'],
'service_ip': server['service_ip'],
}
if server.get('router_id'):
instance_details['router_id'] = server['router_id']
@ -562,6 +563,8 @@ class ServiceInstanceManager(object):
if pair[0] in network_data and 'id' in network_data[pair[0]]:
service_instance[pair[1]] = network_data[pair[0]]['id']
service_instance['service_ip'] = network_data.get('service_ip')
return service_instance
def _get_service_instance_create_kwargs(self):
@ -829,7 +832,9 @@ class NeutronNetworkHelper(BaseNetworkhelper):
network_data['ports'].append(network_data['public_port'])
try:
self.setup_connectivity_with_service_instances()
port = self.setup_connectivity_with_service_instances()
service_ip = self._get_service_ip(
port, network_data['service_subnet']['id'])
except Exception as e:
for port in network_data['ports']:
self.neutron_api.delete_port(port['id'])
@ -840,9 +845,17 @@ class NeutronNetworkHelper(BaseNetworkhelper):
public_ip = network_data.get(
'public_port', network_data['service_port'])
network_data['ip_address'] = public_ip['fixed_ips'][0]['ip_address']
network_data['service_ip'] = service_ip
return network_data
def _get_service_ip(self, port, subnet_id):
for fixed_ips in port['fixed_ips']:
if subnet_id == fixed_ips['subnet_id']:
return fixed_ips['ip_address']
msg = _("Service IP not found for Share Server.")
raise exception.ServiceIPNotFound(reason=msg)
def _get_cidr_for_subnet(self):
"""Returns not used cidr for service subnet creating."""
subnets = self._get_all_service_subnets()
@ -883,6 +896,8 @@ class NeutronNetworkHelper(BaseNetworkhelper):
# here we are checking for garbage devices from removed service port
self._remove_outdated_interfaces(device)
return port
@utils.synchronized(
"service_instance_remove_outdated_interfaces", external=True)
def _remove_outdated_interfaces(self, device):
@ -1016,6 +1031,7 @@ class NovaNetworkHelper(BaseNetworkhelper):
def setup_network(self, network_info):
net = self._get_nova_network(network_info['nova_net_id'])
network_info['nics'] = [{'net-id': net['id']}]
network_info['service_ip'] = net['gateway']
return network_info
def get_network_name(self, network_info):

View File

@ -41,6 +41,7 @@ from manila.tests import fake_volume
from manila import utils
from manila import volume
CONF = cfg.CONF
@ -120,7 +121,8 @@ class GenericShareDriverTestCase(test.TestCase):
'pk_path': 'fake_pk_path',
'backend_details': {
'ip': '1.2.3.4',
'instance_id': 'fake'
'instance_id': 'fake',
'service_ip': 'fake_ip',
},
'availability_zone': 'fake_az',
}
@ -159,6 +161,29 @@ class GenericShareDriverTestCase(test.TestCase):
self.assertRaises(exception.ManilaException,
self._driver._setup_helpers)
def test__get_access_rule_for_data_copy_dhss_true(self):
get_access_return = {
'access_level': 'rw',
'access_to': 'fake_ip',
'access_type': 'ip'
}
result = self._driver._get_access_rule_for_data_copy(
self._context, self.share, self.server)
self.assertEqual(get_access_return, result)
def test__get_access_rule_for_data_copy_dhss_false(self):
get_access_return = {
'access_level': 'rw',
'access_to': 'fake_ip',
'access_type': 'ip'
}
CONF.set_default('driver_handles_share_servers', False)
CONF.set_default('migration_data_copy_node_ip', 'fake_ip')
result = self._driver._get_access_rule_for_data_copy(
self._context, self.share, self.server)
self.assertEqual(get_access_return, result)
def test_create_share(self):
volume = 'fake_volume'
volume2 = 'fake_volume2'

View File

@ -493,11 +493,13 @@ class ServiceInstanceManagerTestCase(test.TestCase):
public_port_id='fake_public_port_id'),
)
def test_set_up_service_instance(self, update_data):
fake_network_info = dict(foo='bar', server_id='fake_server_id')
fake_network_info = dict(foo='bar', server_id='fake_server_id',
service_ip='fake_ip')
fake_server = dict(
id='fake', ip='1.2.3.4', public_address='1.2.3.4', pk_path=None,
subnet_id='fake-subnet-id', router_id='fake-router-id',
username=self._manager.get_config_option('service_instance_user'))
username=self._manager.get_config_option('service_instance_user'),
service_ip='fake_ip')
fake_server.update(update_data)
expected_details = fake_server.copy()
expected_details.pop('pk_path')
@ -517,11 +519,13 @@ class ServiceInstanceManagerTestCase(test.TestCase):
self.assertEqual(expected_details, result)
def test_set_up_service_instance_not_available(self):
fake_network_info = dict(foo='bar', server_id='fake_server_id')
fake_network_info = dict(foo='bar', server_id='fake_server_id',
service_ip='fake_ip')
fake_server = dict(
id='fake', ip='1.2.3.4', public_address='1.2.3.4', pk_path=None,
subnet_id='fake-subnet-id', router_id='fake-router-id',
username=self._manager.get_config_option('service_instance_user'))
username=self._manager.get_config_option('service_instance_user'),
service_ip='fake_ip')
expected_details = fake_server.copy()
expected_details.pop('pk_path')
expected_details['instance_id'] = expected_details.pop('id')
@ -948,7 +952,8 @@ class ServiceInstanceManagerTestCase(test.TestCase):
key_data = 'fake_key_name', 'fake_key_path'
instance_name = 'fake_instance_name'
network_info = dict()
network_data = dict(nics=['fake_nic1', 'fake_nic2'])
network_data = dict(nics=['fake_nic1', 'fake_nic2'],
service_ip='fake_ip')
if helper_type == service_instance.NEUTRON_NAME:
network_data['router'] = dict(id='fake_router_id')
server_get = dict(
@ -987,7 +992,8 @@ class ServiceInstanceManagerTestCase(test.TestCase):
subnet_id=network_data.get('subnet_id'),
instance_id=server_get['id'],
ip=ip_address,
networks=server_get['networks'])
networks=server_get['networks'],
service_ip='fake_ip')
if helper_type == service_instance.NEUTRON_NAME:
expected['router_id'] = network_data['router']['id']
expected['public_port_id'] = 'fake_public_port'
@ -1640,9 +1646,11 @@ class NeutronNetworkHelperTestCase(test.TestCase):
instance, '_get_cidr_for_subnet', mock.Mock(return_value=cidr))
self.mock_object(
instance, '_get_service_subnet', mock.Mock(return_value=None))
self.mock_object(
instance, '_get_service_ip', mock.Mock(return_value='fake_ip'))
expected = dict(
ip_address=self.public_port['fixed_ips'][0]['ip_address'],
public_port=self.public_port,
public_port=self.public_port, service_ip='fake_ip',
service_port=self.service_port, service_subnet=service_subnet,
ports=[self.service_port, self.public_port],
nics=[{'port-id': self.service_port['id']},
@ -1657,6 +1665,7 @@ class NeutronNetworkHelperTestCase(test.TestCase):
instance._get_cidr_for_subnet.assert_called_once_with()
self.assertTrue(service_instance.neutron.API.subnet_create.called)
self.assertTrue(service_instance.neutron.API.create_port.called)
self.assertTrue(instance._get_service_ip.called)
@ddt.data(None, exception.NetworkException(code=400))
def test_setup_network_using_router_success(self, return_obj):
@ -1693,10 +1702,12 @@ class NeutronNetworkHelperTestCase(test.TestCase):
instance, '_get_cidr_for_subnet', mock.Mock(return_value=cidr))
self.mock_object(
instance, '_get_service_subnet', mock.Mock(return_value=None))
self.mock_object(
instance, '_get_service_ip', mock.Mock(return_value='fake_ip'))
expected = dict(
ip_address=self.service_port['fixed_ips'][0]['ip_address'],
service_port=self.service_port, service_subnet=service_subnet,
ports=[self.service_port], router=router,
ports=[self.service_port], router=router, service_ip='fake_ip',
nics=[{'port-id': self.service_port['id']}])
result = instance.setup_network(network_info)
@ -1712,6 +1723,7 @@ class NeutronNetworkHelperTestCase(test.TestCase):
network_info['neutron_net_id'], network_info['neutron_subnet_id'])
service_instance.neutron.API.router_add_interface.\
assert_called_once_with(router['id'], service_subnet['id'])
self.assertTrue(instance._get_service_ip.called)
def test_setup_network_using_router_addon_of_interface_failed(self):
network_info = dict(
@ -1795,6 +1807,36 @@ class NeutronNetworkHelperTestCase(test.TestCase):
service_instance.neutron.API.delete_port.assert_has_calls([
mock.call(self.service_port['id'])])
def test__get_service_ip(self):
fake_division_mask = fake_get_config_option(
'service_network_division_mask')
fake_subnet = fake_network.FakeSubnet(
cidr='10.254.0.0/%s' % fake_division_mask)
fake_port = fake_network.FakePort(fixed_ips=[
{'subnet_id': fake_subnet['id'], 'ip_address': '10.254.0.2'}],
mac_address='fake_mac_address')
instance = self._init_neutron_network_plugin()
result = instance._get_service_ip(fake_port, fake_subnet['id'])
# result should be equal to fake_port.fixed_ips[0]['ip_address']
self.assertEqual(fake_port.fixed_ips[0]['ip_address'], result)
def test__get_service_ip_exception(self):
fake_division_mask = fake_get_config_option(
'service_network_division_mask')
fake_subnet = fake_network.FakeSubnet(
cidr='10.254.0.0/%s' % fake_division_mask)
fake_port = fake_network.FakePort(fixed_ips=[
{'subnet_id': 'another_fake_id', 'ip_address': '10.254.0.2'}],
mac_address='fake_mac_address')
instance = self._init_neutron_network_plugin()
self.assertRaises(
exception.ServiceIPNotFound,
instance._get_service_ip, fake_port, fake_subnet['id'])
def test__get_cidr_for_subnet_success(self):
expected = (
fake_get_config_option('service_network_cidr').split('/')[0] +
@ -1852,7 +1894,7 @@ class NeutronNetworkHelperTestCase(test.TestCase):
self.mock_object(service_instance.ip_lib, 'IPDevice',
mock.Mock(return_value=device_mock))
instance.setup_connectivity_with_service_instances()
result = instance.setup_connectivity_with_service_instances()
instance._get_service_port.assert_called_once_with()
instance.vif_driver.get_device_name.assert_called_once_with(fake_port)
@ -1868,6 +1910,9 @@ class NeutronNetworkHelperTestCase(test.TestCase):
instance._remove_outdated_interfaces.assert_called_once_with(
device_mock)
# result should be equal to fake_port
self.assertEqual(fake_port, result)
def test__get_set_of_device_cidrs(self):
device = fake_network.FakeDevice('foo')
expected = set(('1.0.0.0/27', '2.0.0.0/27'))
@ -2136,13 +2181,15 @@ class NovaNetworkHelperTestCase(test.TestCase):
def test_setup_network(self):
network_info = dict(nova_net_id='fake_nova_net_id')
network = dict(label='fake_network', id='fake_network_id')
network = dict(label='fake_network', id='fake_network_id',
gateway='fake_gateway_ip')
instance = service_instance.NovaNetworkHelper(self.fake_manager)
self.mock_object(instance.compute_api, 'network_get',
mock.Mock(return_value=network))
expected = dict(
nova_net_id=network_info['nova_net_id'],
nics=[{'net-id': network['id']}])
nics=[{'net-id': network['id']}],
service_ip='fake_gateway_ip')
result = instance.setup_network(network_info)