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:
parent
0524ab8fc2
commit
f5add61a8d
|
@ -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.")
|
||||
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue