diff --git a/nova/virt/zvm/driver.py b/nova/virt/zvm/driver.py index 3461f7d..989dd10 100644 --- a/nova/virt/zvm/driver.py +++ b/nova/virt/zvm/driver.py @@ -27,5 +27,4 @@ import nova_zvm.virt.zvm.driver as real_drv LOG = real_drv.LOG CONF = real_drv.CONF -ZVMInstance = real_drv.ZVMInstance ZVMDriver = real_drv.ZVMDriver diff --git a/nova_zvm/tests/unit/virt/zvm/test_driver.py b/nova_zvm/tests/unit/virt/zvm/test_driver.py new file mode 100644 index 0000000..d4d7b99 --- /dev/null +++ b/nova_zvm/tests/unit/virt/zvm/test_driver.py @@ -0,0 +1,512 @@ +# Copyright 2017,2018 IBM Corp. +# +# 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. + +import copy +import mock +import os +import six + +from nova import conf +from nova import context +from nova import exception +from nova.network import model as network_model +from nova import objects +from nova import test +from nova.tests.unit import fake_instance +from nova.tests import uuidsentinel +from nova.virt import fake +from nova_zvm.virt.zvm import driver as zvmdriver +from nova_zvm.virt.zvm import exception as zvm_exception + + +CONF = conf.CONF + + +class TestZVMDriver(test.NoDBTestCase): + + def setUp(self): + super(TestZVMDriver, self).setUp() + self.flags(my_ip='192.168.1.1', + instance_name_template='abc%05d') + self.flags(cloud_connector_url='https://1.1.1.1:1111', group='zvm') + + with mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') as mcall, \ + mock.patch('pwd.getpwuid', return_value=mock.Mock(pw_name='test')): + mcall.return_value = {'hypervisor_hostname': 'TESTHOST', + 'ipl_time': 'IPL at 11/14/17 10:47:44 EST'} + self._driver = zvmdriver.ZVMDriver(fake.FakeVirtAPI()) + self._hypervisor = self._driver._hypervisor + + self._context = context.RequestContext('fake_user', 'fake_project') + self._image_id = uuidsentinel.imag_id + + self._instance_values = { + 'display_name': 'test', + 'uuid': uuidsentinel.inst_id, + 'vcpus': 1, + 'memory_mb': 1024, + 'image_ref': self._image_id, + 'root_gb': 0, + } + self._instance = fake_instance.fake_instance_obj( + self._context, **self._instance_values) + self._instance.flavor = objects.Flavor(name='testflavor', + vcpus=1, root_gb=3, ephemeral_gb=10, + swap=0, memory_mb=512, extra_specs={}) + + self._eph_disks = [{'guest_format': u'ext3', + 'device_name': u'/dev/sdb', + 'disk_bus': None, + 'device_type': None, + 'size': 1}, + {'guest_format': u'ext4', + 'device_name': u'/dev/sdc', + 'disk_bus': None, + 'device_type': None, + 'size': 2}] + self._block_device_info = {'swap': None, + 'root_device_name': u'/dev/sda', + 'ephemerals': self._eph_disks, + 'block_device_mapping': []} + fake_image_meta = {'status': 'active', + 'properties': {'os_distro': 'rhel7.2'}, + 'name': 'rhel72eckdimage', + 'deleted': False, + 'container_format': 'bare', + 'disk_format': 'raw', + 'id': self._image_id, + 'owner': 'cfc26f9d6af948018621ab00a1675310', + 'checksum': 'b026cd083ef8e9610a29eaf71459cc', + 'min_disk': 0, + 'is_public': False, + 'deleted_at': None, + 'min_ram': 0, + 'size': 465448142} + self._image_meta = objects.ImageMeta.from_dict(fake_image_meta) + subnet_4 = network_model.Subnet(cidr='192.168.0.1/24', + dns=[network_model.IP('192.168.0.1')], + gateway= + network_model.IP('192.168.0.1'), + ips=[ + network_model.IP('192.168.0.100')], + routes=None) + network = network_model.Network(id=0, + bridge='fa0', + label='fake', + subnets=[subnet_4], + vlan=None, + bridge_interface=None, + injected=True) + self._network_values = { + 'id': None, + 'address': 'DE:AD:BE:EF:00:00', + 'network': network, + 'type': network_model.VIF_TYPE_OVS, + 'devname': None, + 'ovs_interfaceid': None, + 'rxtx_cap': 3 + } + self._network_info = network_model.NetworkInfo([ + network_model.VIF(**self._network_values) + ]) + + self.mock_update_task_state = mock.Mock() + + def test_driver_init_no_url(self): + self.flags(cloud_connector_url=None, group='zvm') + self.assertRaises(zvm_exception.ZVMDriverException, + zvmdriver.ZVMDriver, 'virtapi') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_available_resource_err_case(self, call): + res = {'overallRC': 1, 'errmsg': 'err', 'rc': 0, 'rs': 0} + call.side_effect = zvm_exception.ZVMConnectorError(res) + results = self._driver.get_available_resource() + self.assertEqual(0, results['vcpus']) + self.assertEqual(0, results['memory_mb_used']) + self.assertEqual(0, results['disk_available_least']) + self.assertEqual('TESTHOST', results['hypervisor_hostname']) + + def test_driver_template_validation(self): + self.flags(instance_name_template='abc%6d') + self.assertRaises(zvm_exception.ZVMDriverException, + self._driver._validate_options) + + @mock.patch('nova_zvm.virt.zvm.guest.Guest.get_info') + def test_get_info(self, mock_get): + self._driver.get_info(self._instance) + mock_get.assert_called_once_with() + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_private_get_image_info_err(self, call): + res = {'overallRC': 500, 'errmsg': 'err', 'rc': 0, 'rs': 0} + call.side_effect = zvm_exception.ZVMConnectorError(res) + self.assertRaises(zvm_exception.ZVMConnectorError, + self._driver._get_image_info, + 'context', 'image_meta_id', 'os_distro') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._import_spawn_image') + def test_private_get_image_info(self, image_import, call): + res = {'overallRC': 404, 'errmsg': 'err', 'rc': 0, 'rs': 0} + + call_response = [] + call_response.append(zvm_exception.ZVMConnectorError(results=res)) + call_response.append([{'imagename': 'image-info'}]) + call.side_effect = call_response + self._driver._get_image_info('context', 'image_meta_id', 'os_distro') + image_import.assert_called_once_with('context', 'image_meta_id', + 'os_distro') + call.assert_has_calls( + [mock.call('image_query', imagename='image_meta_id')] * 2 + ) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_private_get_image_info_exist(self, call): + call.return_value = [{'imagename': 'image-info'}] + res = self._driver._get_image_info('context', 'image_meta_id', + 'os_distro') + call.assert_called_once_with('image_query', imagename='image_meta_id') + self.assertEqual('image-info', res) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def _test_set_disk_list(self, call, has_get_root_units=False, + has_eph_disks=False): + disk_list = [{'is_boot_disk': True, 'size': '3g'}] + eph_disk_list = [{'format': u'ext3', 'size': '1g'}, + {'format': u'ext3', 'size': '2g'}] + _inst = copy.deepcopy(self._instance) + _bdi = copy.deepcopy(self._block_device_info) + + if has_get_root_units: + # overwrite + disk_list = [{'is_boot_disk': True, 'size': '3338'}] + call.return_value = '3338' + _inst['root_gb'] = 0 + else: + _inst['root_gb'] = 3 + + if has_eph_disks: + disk_list += eph_disk_list + else: + _bdi['ephemerals'] = [] + eph_disk_list = [] + + res1, res2 = self._driver._set_disk_list(_inst, self._image_meta.id, + _bdi) + + if has_get_root_units: + call.assert_called_once_with('image_get_root_disk_size', + self._image_meta.id) + self.assertEqual(disk_list, res1) + self.assertEqual(eph_disk_list, res2) + + def test_private_set_disk_list_simple(self): + self._test_set_disk_list() + + def test_private_set_disk_list_with_eph_disks(self): + self._test_set_disk_list(has_eph_disks=True) + + def test_private_set_disk_list_with_get_root_units(self): + self._test_set_disk_list(has_get_root_units=True) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_private_setup_network(self, call): + inst_nets = [] + _net = {'ip_addr': '192.168.0.100', + 'gateway_addr': '192.168.0.1', + 'cidr': '192.168.0.1/24', + 'mac_addr': 'DE:AD:BE:EF:00:00', + 'nic_id': None} + inst_nets.append(_net) + self._driver._setup_network('vm_name', 'os_distro', + self._network_info, + self._instance) + call.assert_called_once_with('guest_create_network_interface', + 'vm_name', 'os_distro', inst_nets) + + @mock.patch('nova.virt.images.fetch') + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_private_import_spawn_image(self, call, fetch): + + image_name = CONF.zvm.image_tmp_path + '/image_name' + image_url = "file://" + image_name + image_meta = {'os_version': 'os_version'} + with mock.patch('os.path.exists', side_effect=[False]): + self._driver._import_spawn_image(self._context, 'image_name', + 'os_version') + fetch.assert_called_once_with(self._context, 'image_name', + image_name) + call.assert_called_once_with('image_import', 'image_name', image_url, + image_meta, remote_host='test@192.168.1.1') + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_exists') + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_destroy(self, call, guest_exists): + guest_exists.return_value = True + self._driver.destroy(self._context, self._instance, + network_info=self._network_info) + call.assert_called_once_with('guest_delete', self._instance['name']) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_exists') + @mock.patch('nova.compute.manager.ComputeVirtAPI.wait_for_instance_event') + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._setup_network') + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._set_disk_list') + @mock.patch('nova_zvm.virt.zvm.utils.generate_configdrive') + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_image_info') + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_spawn(self, call, get_image_info, gen_conf_file, set_disk_list, + setup_network, mock_wait, mock_exists): + _bdi = copy.copy(self._block_device_info) + get_image_info.return_value = 'image_name' + gen_conf_file.return_value = 'transportfiles' + set_disk_list.return_value = 'disk_list', 'eph_list' + mock_exists.return_value = False + self._driver.spawn(self._context, self._instance, self._image_meta, + injected_files=None, admin_password=None, + allocations=None, network_info=self._network_info, + block_device_info=_bdi) + gen_conf_file.assert_called_once_with(self._context, self._instance, + None, self._network_info, None) + get_image_info.assert_called_once_with(self._context, + self._image_meta.id, + self._image_meta.properties.os_distro) + set_disk_list.assert_called_once_with(self._instance, 'image_name', + _bdi) + setup_network.assert_called_once_with(self._instance.name, + self._image_meta.properties.os_distro, + self._network_info, self._instance) + + call.assert_has_calls([ + mock.call('guest_create', self._instance.name, + 1, 1024, disk_list='disk_list'), + mock.call('guest_deploy', self._instance.name, 'image_name', + transportfiles='transportfiles', + remotehost='test@192.168.1.1'), + mock.call('guest_config_minidisks', self._instance.name, + 'eph_list'), + mock.call('guest_start', self._instance.name) + ]) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_exists') + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_image_info') + def test_spawn_image_no_distro_empty(self, get_image_info, mock_exists): + meta = {'status': 'active', + 'deleted': False, + 'properties': {'os_distro': ''}, + 'id': self._image_id, + 'size': 465448142} + self._image_meta = objects.ImageMeta.from_dict(meta) + mock_exists.return_value = False + self.assertRaises(exception.InvalidInput, self._driver.spawn, + self._context, self._instance, self._image_meta, + injected_files=None, admin_password=None, + allocations=None, network_info=self._network_info, + block_device_info=None) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_exists') + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_image_info') + def test_spawn_image_no_distro_none(self, get_image_info, mock_exists): + meta = {'status': 'active', + 'deleted': False, + 'id': self._image_id, + 'size': 465448142} + self._image_meta = objects.ImageMeta.from_dict(meta) + mock_exists.return_value = False + self.assertRaises(exception.InvalidInput, self._driver.spawn, + self._context, self._instance, self._image_meta, + injected_files=None, admin_password=None, + allocations=None, network_info=self._network_info, + block_device_info=None) + + @mock.patch.object(six.moves.builtins, 'open') + @mock.patch('nova.image.glance.get_remote_image_service') + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_snapshot(self, call, get_image_service, mock_open): + image_service = mock.Mock() + image_id = 'e9ee1562-3ea1-4cb1-9f4c-f2033000eab1' + get_image_service.return_value = (image_service, image_id) + call_resp = ['', {"os_version": "rhel7.2", + "dest_url": "file:///path/to/target"}, ''] + call.side_effect = call_resp + new_image_meta = { + 'is_public': False, + 'status': 'active', + 'properties': { + 'image_location': 'snapshot', + 'image_state': 'available', + 'owner_id': self._instance['project_id'], + 'os_distro': call_resp[1]['os_version'], + 'architecture': 's390x', + 'hypervisor_type': 'zvm' + }, + 'disk_format': 'raw', + 'container_format': 'bare', + } + image_path = os.path.join(os.path.normpath( + CONF.zvm.image_tmp_path), image_id) + dest_path = "file://" + image_path + + self._driver.snapshot(self._context, self._instance, image_id, + self.mock_update_task_state) + get_image_service.assert_called_with(self._context, image_id) + + mock_open.assert_called_once_with(image_path, 'r') + ret_file = mock_open.return_value.__enter__.return_value + image_service.update.assert_called_once_with(self._context, + image_id, + new_image_meta, + ret_file, + purge_props=False) + self.mock_update_task_state.assert_has_calls([ + mock.call(task_state='image_pending_upload'), + mock.call(expected_state='image_pending_upload', + task_state='image_uploading') + ]) + call.assert_has_calls([ + mock.call('guest_capture', self._instance.name, image_id), + mock.call('image_export', image_id, dest_path, + remote_host=mock.ANY), + mock.call('image_delete', image_id) + ]) + + @mock.patch('nova.image.glance.get_remote_image_service') + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_capture') + def test_snapshot_capture_fail(self, mock_capture, get_image_service): + image_service = mock.Mock() + image_id = 'e9ee1562-3ea1-4cb1-9f4c-f2033000eab1' + get_image_service.return_value = (image_service, image_id) + mock_capture.side_effect = zvm_exception.ZVMDriverException( + error='error') + + self.assertRaises(zvm_exception.ZVMDriverException, + self._driver.snapshot, + self._context, self._instance, image_id, + self.mock_update_task_state) + + self.mock_update_task_state.assert_called_once_with( + task_state='image_pending_upload') + image_service.delete.assert_called_once_with(self._context, image_id) + + @mock.patch('nova.image.glance.get_remote_image_service') + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.image_delete') + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.image_export') + def test_snapshot_import_fail(self, mock_import, mock_delete, + call, get_image_service): + image_service = mock.Mock() + image_id = 'e9ee1562-3ea1-4cb1-9f4c-f2033000eab1' + get_image_service.return_value = (image_service, image_id) + + mock_import.side_effect = zvm_exception.ZVMDriverException( + error='error') + + self.assertRaises(zvm_exception.ZVMDriverException, + self._driver.snapshot, + self._context, self._instance, image_id, + self.mock_update_task_state) + + self.mock_update_task_state.assert_called_once_with( + task_state='image_pending_upload') + get_image_service.assert_called_with(self._context, image_id) + call.assert_called_once_with('guest_capture', + self._instance.name, image_id) + mock_delete.assert_called_once_with(image_id) + image_service.delete.assert_called_once_with(self._context, image_id) + + @mock.patch.object(six.moves.builtins, 'open') + @mock.patch('nova.image.glance.get_remote_image_service') + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.image_delete') + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.image_export') + def test_snapshot_update_fail(self, mock_import, mock_delete, call, + get_image_service, mock_open): + image_service = mock.Mock() + image_id = 'e9ee1562-3ea1-4cb1-9f4c-f2033000eab1' + get_image_service.return_value = (image_service, image_id) + image_service.update.side_effect = exception.ImageNotAuthorized( + image_id='dummy') + image_path = os.path.join(os.path.normpath( + CONF.zvm.image_tmp_path), image_id) + + self.assertRaises(exception.ImageNotAuthorized, self._driver.snapshot, + self._context, self._instance, image_id, + self.mock_update_task_state) + + mock_open.assert_called_once_with(image_path, 'r') + + get_image_service.assert_called_with(self._context, image_id) + mock_delete.assert_called_once_with(image_id) + image_service.delete.assert_called_once_with(self._context, image_id) + + self.mock_update_task_state.assert_has_calls([ + mock.call(task_state='image_pending_upload'), + mock.call(expected_state='image_pending_upload', + task_state='image_uploading') + ]) + + call.assert_called_once_with('guest_capture', self._instance.name, + image_id) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_start') + def test_guest_start(self, call): + self._driver.power_on(self._context, self._instance, None) + call.assert_called_once_with(self._instance.name) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_softstop') + def test_power_off(self, ipa): + self._driver.power_off(self._instance) + ipa.assert_called_once_with(self._instance.name) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_softstop') + def test_power_off_with_timeout_interval(self, ipa): + self._driver.power_off(self._instance, 60, 10) + ipa.assert_called_once_with(self._instance.name, + timeout=60, retry_interval=10) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_pause') + def test_pause(self, ipa): + self._driver.pause(self._instance) + ipa.assert_called_once_with(self._instance.name) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_unpause') + def test_unpause(self, ipa): + self._driver.unpause(self._instance) + ipa.assert_called_once_with(self._instance.name) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_reboot') + def test_reboot_soft(self, ipa): + self._driver.reboot(None, self._instance, None, 'SOFT') + ipa.assert_called_once_with(self._instance.name) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.guest_reset') + def test_reboot_hard(self, ipa): + self._driver.reboot(None, self._instance, None, 'HARD') + ipa.assert_called_once_with(self._instance.name) + + @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver.list_instances') + def test_instance_exists(self, mock_list): + mock_list.return_value = [self._instance.name.upper()] + # Create a new server which not in list_instances's output + another_instance = fake_instance.fake_instance_obj(self._context, + id=10) + self.assertTrue(self._driver.instance_exists(self._instance)) + self.assertFalse(self._driver.instance_exists(another_instance)) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_console_output(self, call): + call.return_value = 'console output' + outputs = self._driver.get_console_output(None, self._instance) + call.assert_called_once_with('guest_get_console_output', 'abc00001') + self.assertEqual('console output', outputs) diff --git a/nova_zvm/tests/unit/virt/zvm/test_guest.py b/nova_zvm/tests/unit/virt/zvm/test_guest.py new file mode 100644 index 0000000..badb322 --- /dev/null +++ b/nova_zvm/tests/unit/virt/zvm/test_guest.py @@ -0,0 +1,79 @@ +# Copyright 2017,2018 IBM Corp. +# +# 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. + +import mock + +from nova.compute import power_state as compute_power_state +from nova import context +from nova import exception +from nova import test +from nova.tests.unit import fake_instance +from nova.virt import fake +from nova_zvm.virt.zvm import driver +from nova_zvm.virt.zvm import exception as zvm_exception +from nova_zvm.virt.zvm import guest + + +class TestZVMGuestOp(test.NoDBTestCase): + def setUp(self): + super(TestZVMGuestOp, self).setUp() + self.flags(cloud_connector_url='https://1.1.1.1:1111', + image_tmp_path='/test/image', + reachable_timeout=300, group='zvm') + self.flags(my_ip='192.168.1.1', + instance_name_template='test%04x') + with test.nested( + mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call'), + mock.patch('pwd.getpwuid'), + ) as (mcall, getpwuid): + getpwuid.return_value = mock.Mock(pw_name='test') + mcall.return_value = {'hypervisor_hostname': 'TESTHOST', + 'ipl_time': 'TESTTIME'} + self._driver = driver.ZVMDriver(fake.FakeVirtAPI()) + self._hypervisor = self._driver._hypervisor + + self._context = context.RequestContext('fake_user', 'fake_project') + self._instance = fake_instance.fake_instance_obj( + self._context) + self._guest = guest.Guest(self._hypervisor, self._instance, + self._driver.virtapi) + + def test_private_mapping_power_state(self): + status = self._guest._mapping_power_state('on') + self.assertEqual(compute_power_state.RUNNING, status) + status = self._guest._mapping_power_state('off') + self.assertEqual(compute_power_state.SHUTDOWN, status) + status = self._guest._mapping_power_state('bad') + self.assertEqual(compute_power_state.NOSTATE, status) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_info_err_instance_not_found(self, call): + res = {'overallRC': 404, 'errmsg': 'err', 'rc': 0, 'rs': 0} + call.side_effect = zvm_exception.ZVMConnectorError(results=res) + self.assertRaises(exception.InstanceNotFound, self._guest.get_info) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_info_err_general(self, call): + res = {'overallRC': 500, 'errmsg': 'err', 'rc': 0, 'rs': 0} + call.side_effect = zvm_exception.ZVMConnectorError(res) + self.assertRaises(zvm_exception.ZVMConnectorError, + self._guest.get_info) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_info(self, call): + call.return_value = 'on' + info = self._guest.get_info() + call.assert_called_once_with('guest_get_power_state', + self._instance['name']) + self.assertEqual(info.state, compute_power_state.RUNNING) diff --git a/nova_zvm/tests/unit/virt/zvm/test_hypervisor.py b/nova_zvm/tests/unit/virt/zvm/test_hypervisor.py new file mode 100644 index 0000000..849e89a --- /dev/null +++ b/nova_zvm/tests/unit/virt/zvm/test_hypervisor.py @@ -0,0 +1,146 @@ +# Copyright 2017,2018 IBM Corp. +# +# 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. + +import mock + +from nova import context +from nova import test +from nova.tests.unit import fake_instance +from nova_zvm.virt.zvm import driver as zvmdriver +from nova_zvm.virt.zvm import exception as zvm_exception + + +class TestZVMHypervisor(test.NoDBTestCase): + + def setUp(self): + super(TestZVMHypervisor, self).setUp() + self.flags(instance_name_template='abc%5d') + self.flags(cloud_connector_url='https://1.1.1.1:1111', group='zvm') + with mock.patch('nova_zvm.virt.zvm.utils.' + 'ConnectorClient.call') as mcall: + mcall.return_value = {'hypervisor_hostname': 'TESTHOST', + 'ipl_time': 'IPL at 11/14/17 10:47:44 EST'} + driver = zvmdriver.ZVMDriver('virtapi') + self._hypervisor = driver._hypervisor + + self._context = context.RequestContext('fake_user', 'fake_project') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_available_resource(self, call): + host_info = {'disk_available': 1144, + 'ipl_time': 'IPL at 11/14/17 10:47:44 EST', + 'vcpus_used': 4, + 'hypervisor_type': 'zvm', + 'disk_total': 2000, + 'zvm_host': 'TESTHOST', + 'memory_mb': 78192.0, + 'cpu_info': {'cec_model': '2827', + 'architecture': 's390x'}, + 'vcpus': 84, + 'hypervisor_hostname': 'TESTHOST', + 'hypervisor_version': 640, + 'disk_used': 856, + 'memory_mb_used': 8192.0} + call.return_value = host_info + results = self._hypervisor.get_available_resource() + self.assertEqual(host_info, results) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_available_resource_err_case(self, call): + res = {'overallRC': 1, 'errmsg': 'err', 'rc': 0, 'rs': 0} + call.side_effect = zvm_exception.ZVMConnectorError(res) + results = self._hypervisor.get_available_resource() + # Should return an empty dict + self.assertFalse(results) + + def test_get_available_nodes(self): + nodes = self._hypervisor.get_available_nodes() + self.assertEqual(['TESTHOST'], nodes) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_list_names(self, call): + call.return_value = ['vm1', 'vm2'] + inst_list = self._hypervisor.list_names() + self.assertEqual(['vm1', 'vm2'], inst_list) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_get_host_uptime(self, call): + host_info = {'disk_available': 1144, + 'ipl_time': 'IPL at 11/14/17 10:47:44 EST', + 'memory_mb_used': 8192.0} + call.return_value = host_info + + time = self._hypervisor.get_host_uptime() + self.assertEqual('IPL at 11/14/17 10:47:44 EST', time) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.list_names') + def test_private_guest_exists_true(self, list_names): + instance = fake_instance.fake_instance_obj(self._context) + list_names.return_value = [instance.name.upper(), 'TEST0002'] + res = self._hypervisor.guest_exists(instance) + self.assertTrue(res) + + @mock.patch('nova_zvm.virt.zvm.hypervisor.Hypervisor.list_names') + def test_private_guest_exists_false(self, list_names): + list_names.return_value = ['dummy1', 'dummy2'] + instance = fake_instance.fake_instance_obj(self._context) + res = self._hypervisor.guest_exists(instance) + self.assertFalse(res) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_capture(self, mcall): + self._hypervisor.guest_capture('n1', 'image-id') + mcall.assert_called_once_with('guest_capture', 'n1', 'image-id') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_image_export(self, mcall): + self._hypervisor.image_export('image-id', 'path') + mcall.assert_called_once_with('image_export', 'image-id', 'path', + remote_host=self._hypervisor._rhost) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_image_delete(self, mcall): + self._hypervisor.image_delete('image-id') + mcall.assert_called_once_with('image_delete', 'image-id') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_softstop(self, mcall): + self._hypervisor.guest_softstop('guest') + mcall.assert_called_once_with('guest_softstop', 'guest', + poll_interval=0, timeout=0) + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_pause(self, mcall): + self._hypervisor.guest_pause('guest') + mcall.assert_called_once_with('guest_pause', 'guest') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_unpause(self, mcall): + self._hypervisor.guest_unpause('guest') + mcall.assert_called_once_with('guest_unpause', 'guest') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_reboot(self, mcall): + self._hypervisor.guest_reboot('guest') + mcall.assert_called_once_with('guest_reboot', 'guest') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_reset(self, mcall): + self._hypervisor.guest_reset('guest') + mcall.assert_called_once_with('guest_reset', 'guest') + + @mock.patch('nova_zvm.virt.zvm.utils.ConnectorClient.call') + def test_guest_get_console_output(self, mcall): + self._hypervisor.guest_get_console_output('guest') + mcall.assert_called_once_with('guest_get_console_output', 'guest') diff --git a/nova_zvm/tests/unit/virt/zvm/test_utils.py b/nova_zvm/tests/unit/virt/zvm/test_utils.py new file mode 100644 index 0000000..1358538 --- /dev/null +++ b/nova_zvm/tests/unit/virt/zvm/test_utils.py @@ -0,0 +1,139 @@ +# Copyright 2017,2018 IBM Corp. +# +# 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. + +import mock + +from zvmconnector import connector + +from nova import context +from nova import exception +from nova import test +from nova.tests.unit import fake_instance +from nova_zvm.virt.zvm import exception as zvm_exception +from nova_zvm.virt.zvm import utils as zvmutils + + +class TestZVMUtils(test.NoDBTestCase): + + def setUp(self): + super(TestZVMUtils, self).setUp() + self.flags(cloud_connector_url='http://127.0.0.1', group='zvm') + self._url = 'http://127.0.0.1' + + def test_connector_request_handler_invalid_url(self): + rh = zvmutils.ConnectorClient('http://invalid') + self.assertRaises(zvm_exception.ZVMDriverException, rh.call, + 'guest_list') + + @mock.patch('zvmconnector.connector.ZVMConnector.__init__', + return_value=None) + def test_connector_request_handler_https(self, mock_init): + rh = zvmutils.ConnectorClient('https://127.0.0.1:80', + ca_file='/tmp/file') + mock_init.assert_called_once_with('127.0.0.1', 80, ssl_enabled=True, + verify='/tmp/file') + self.assertIsInstance(rh._conn, connector.ZVMConnector) + + @mock.patch('zvmconnector.connector.ZVMConnector.__init__', + return_value=None) + def test_connector_request_handler_https_noca(self, mock_init): + rh = zvmutils.ConnectorClient('https://127.0.0.1:80') + mock_init.assert_called_once_with('127.0.0.1', 80, ssl_enabled=True, + verify=False) + self.assertIsInstance(rh._conn, connector.ZVMConnector) + + @mock.patch('zvmconnector.connector.ZVMConnector.__init__', + return_value=None) + def test_connector_request_handler_http(self, mock_init): + rh = zvmutils.ConnectorClient('http://127.0.0.1:80') + mock_init.assert_called_once_with('127.0.0.1', 80, ssl_enabled=False, + verify=False) + self.assertIsInstance(rh._conn, connector.ZVMConnector) + + @mock.patch('zvmconnector.connector.ZVMConnector.send_request') + def test_connector_request_handler(self, mock_send): + mock_send.return_value = {'overallRC': 0, 'output': 'data', + 'rc': 0, 'rs': 0} + rh = zvmutils.ConnectorClient(self._url) + res = rh.call('guest_list') + self.assertEqual('data', res) + + @mock.patch('zvmconnector.connector.ZVMConnector.send_request') + def test_connector_request_handler_error(self, mock_send): + expected = {'overallRC': 1, 'errmsg': 'err', 'rc': 0, 'rs': 0} + mock_send.return_value = expected + + rh = zvmutils.ConnectorClient(self._url) + exc = self.assertRaises(zvm_exception.ZVMConnectorError, rh.call, + 'guest_list') + self.assertIn('zVM Cloud Connector request failed', + exc.format_message()) + self.assertEqual(expected['overallRC'], exc.overallRC) + self.assertEqual(expected['rc'], exc.rc) + self.assertEqual(expected['rs'], exc.rs) + self.assertEqual(expected['errmsg'], exc.errmsg) + + @mock.patch('nova.virt.configdrive.required_by') + @mock.patch('nova_zvm.virt.zvm.utils._create_config_drive') + @mock.patch('nova_zvm.virt.zvm.utils._get_instance_path') + def test_generate_configdrive(self, get, create, required): + get.return_value = '/test/tmp/fake_uuid' + create.return_value = '/test/cfgdrive.tgz' + required.return_value = True + + ctxt = context.RequestContext('fake_user', 'fake_project') + instance = fake_instance.fake_instance_obj(ctxt) + + file = zvmutils.generate_configdrive('context', instance, + 'injected_files', + 'network_info', + 'admin_password') + required.assert_called_once_with(instance) + create.assert_called_once_with('context', '/test/tmp/fake_uuid', + instance, 'injected_files', + 'network_info', 'admin_password') + self.assertEqual('/test/cfgdrive.tgz', file) + + @mock.patch('nova.api.metadata.base.InstanceMetadata') + @mock.patch('nova.virt.configdrive.ConfigDriveBuilder.make_drive') + def test_create_config_drive(self, make_drive, mock_instance_metadata): + + class FakeInstanceMetadata(object): + def __init__(self): + self.network_metadata = None + + def metadata_for_config_drive(self): + return [] + + mock_instance_metadata.return_value = FakeInstanceMetadata() + + self.flags(config_drive_format='iso9660') + extra_md = {'admin_pass': 'admin_password'} + zvmutils._create_config_drive('context', '/instance_path', + 'instance', 'injected_files', + 'network_info', 'admin_password') + mock_instance_metadata.assert_called_once_with('instance', + content='injected_files', + extra_md=extra_md, + network_info='network_info', + request_context='context') + make_drive.assert_called_once_with('/instance_path/cfgdrive.iso') + + def test_create_config_drive_invalid_format(self): + + self.flags(config_drive_format='vfat') + self.assertRaises(exception.ConfigDriveUnsupportedFormat, + zvmutils._create_config_drive, 'context', + '/instance_path', 'instance', 'injected_files', + 'network_info', 'admin_password') diff --git a/nova_zvm/tests/unit/virt/zvm/test_zvm.py b/nova_zvm/tests/unit/virt/zvm/test_zvm.py deleted file mode 100644 index 4d6e488..0000000 --- a/nova_zvm/tests/unit/virt/zvm/test_zvm.py +++ /dev/null @@ -1,4604 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. -"""Test suite for ZVMDriver.""" - -import os -import six -from six.moves import http_client as httplib -import socket -import time - -import mock -from mox3 import mox -from nova.compute import power_state -from nova.compute import vm_states -from nova import context -from nova import exception as nova_exception -from nova.image import glance -from nova.network import model -from nova import objects -from nova import test -from nova.tests.unit import fake_instance -from nova.virt import configdrive as virt_configdrive -from nova.virt import fake -from nova.virt import hardware -from oslo_concurrency import processutils -from oslo_config import cfg -from oslo_serialization import jsonutils -from oslo_utils import fileutils - -from nova_zvm.virt.zvm import configdrive -from nova_zvm.virt.zvm import dist -from nova_zvm.virt.zvm import driver -from nova_zvm.virt.zvm import exception -from nova_zvm.virt.zvm import imageop -from nova_zvm.virt.zvm import instance -from nova_zvm.virt.zvm import networkop -from nova_zvm.virt.zvm import utils as zvmutils -from nova_zvm.virt.zvm import volumeop - - -CONF = cfg.CONF - - -class FakeXCATConn(object): - - def __init__(self): - pass - - def request(self, one, two, three=None, four=None): - four = four or {} - pass - - -class FakeHTTPResponse(object): - - def __init__(self, status=None, reason=None, data=None): - self.status = status - self.reason = reason - self.data = data - - def read(self): - return self.data - - -class FakeImageService(object): - - def __init__(self, values): - self.values = values - - def show(self, *args): - return self.values - - def delete(self, *args): - pass - - def update(self, *args, **kwargs): - pass - - -class FakeInstMeta(object): - - def metadata_for_config_drive(self): - return [('openstack', 'data1'), ('ec2', 'data2')] - - -class ZVMTestCase(test.TestCase): - """Base testcase class of zvm driver and zvm instance.""" - - def setUp(self): - super(ZVMTestCase, self).setUp() - self.context = context.get_admin_context() - self.instance = fake_instance.fake_instance_obj(self.context, - **{'user_id': 'fake', - 'project_id': 'fake', - 'instance_type_id': 1, - 'memory_mb': 1024, - 'vcpus': 2, - 'root_gb': 10, - 'ephemeral_gb': 0, - 'image_ref': '0000-1111', - 'host': 'fakenode'}) - self.instance2 = fake_instance.fake_instance_obj(self.context, - **{'user_id': 'fake', - 'project_id': 'fake', - 'instance_type_id': 1, - 'memory_mb': 1024, - 'vcpus': 2, - 'root_gb': 10, - 'ephemeral_gb': 1, - 'image_ref': '0000-1111', - 'host': 'fakenode'}) - self.flags(host='fakehost', - my_ip='10.1.1.10', - zvm_xcat_server='10.10.10.10', - zvm_xcat_username='fake', - zvm_xcat_password='fake', - zvm_host='fakenode', - zvm_diskpool='fakedp', - zvm_diskpool_type='FBA', - instance_name_template='os%06x', - zvm_xcat_master='fakemn', - zvm_scsi_pool='fakesp', - zvm_image_default_password='pass', - zvm_fcp_list="1FB0-1FB3", - zvm_zhcp_fcp_list="1FAF", - config_drive_format='iso9660', - zvm_image_compression_level='0') - - def tearDown(self): - self.addCleanup(self.stubs.UnsetAll) - self.mox.UnsetStubs() - super(ZVMTestCase, self).tearDown() - - def _set_fake_xcat_responses(self, fake_resp_list): - self.stubs.Set(zvmutils, "XCATConnection", FakeXCATConn) - self.mox.StubOutWithMock(zvmutils.XCATConnection, 'request') - for res_data in fake_resp_list: - res = {'message': jsonutils.dumps(res_data)} - zvmutils.XCATConnection.request(mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg(), mox.IgnoreArg()).AndReturn((0, res)) - self.mox.ReplayAll() - - def _set_fake_xcat_resp(self, fake_resp_list): - """fake_resq_list: - [(method, url, body, response_data), - (method, url, body, response_data), - ..., - (method, url, body, response_data)] - """ - self.mox.StubOutWithMock(zvmutils.XCATConnection, 'request') - for res in fake_resp_list: - method = res[0] or mox.IgnoreArg() - url = res[1] or mox.IgnoreArg() - body = res[2] or mox.IgnoreArg() - res = {'message': jsonutils.dumps(res[3])} - zvmutils.XCATConnection.request(method, url, body, - mox.IgnoreArg()).AndReturn((0, res)) - self.mox.ReplayAll() - - def _gen_resp(self, **kwargs): - data = [] - for (k, v) in six.iteritems(kwargs): - if v not in (None, []): - data.append({k: v}) - - return {'data': data} - - def _generate_xcat_resp(self, info): - return {'data': [{'info': info}]} - - def _fake_fun(self, value=None): - return lambda *args, **kwargs: value - - def _app_auth(self, url): - return ''.join([url, '?userName=fake&password=fake&format=json']) - - -class ZVMDriverTestCases(ZVMTestCase): - """Unit tests for z/VM driver methods.""" - - fake_image_meta = { - 'checksum': '1a2bbbdbcc9c536a2688fc6278685dfb', - 'container_format': 'bare', - 'disk_format': 'raw', - 'id': 'bef39792-1ae2-46f5-b44c-0641bfcb3b98', - 'is_public': False, - 'name': 'fakeimg', - 'properties': {'architecture': 's390x', - 'image_file_name': 'abc', - 'image_type': 'linux', - 'os_name': 'Linux', - 'os_version': 'rhel6.2', - 'provisioning_method': 'netboot', - 'image_type_xcat': 'linux'}, - 'size': 578181045, - 'status': 'active'} - - keys = ('image_ref', 'uuid', 'user_id', 'project_id', - 'power_state', 'system_metadata', 'memory_mb', 'vcpus', - 'root_gb', 'ephemeral_gb') - _fake_inst = {'name': 'os000001'} - _old_inst = {'name': 'rszos000001'} - - for k in keys: - _fake_inst[k] = '' - _old_inst[k] = '' - - def _fake_host_rinv_info(self): - fake_host_rinv_info = ["fakenode: z/VM Host: FAKENODE\n" - "fakenode: zHCP: fakehcp.fake.com\n" - "fakenode: CEC Vendor: FAKE\n" - "fakenode: CEC Model: 2097\n" - "fakenode: Hypervisor OS: z/VM 6.1.0\n" - "fakenode: Hypervisor Name: fakenode\n" - "fakenode: Architecture: s390x\n" - "fakenode: LPAR CPU Total: 10\n" - "fakenode: LPAR CPU Used: 10\n" - "fakenode: LPAR Memory Total: 16G\n" - "fakenode: LPAR Memory Offline: 0\n" - "fakenode: LPAR Memory Used: 16.0G\n" - "fakenode: IPL Time:" - "IPL at 03/13/14 21:43:12 EDT\n"] - return self._generate_xcat_resp(fake_host_rinv_info) - - def _fake_disk_info(self): - fake_disk_info = ["fakenode: FAKEDP Total: 406105.3 G\n" - "fakenode: FAKEDP Used: 367262.6 G\n" - "fakenode: FAKEDP Free: 38842.7 G\n"] - return self._generate_xcat_resp(fake_disk_info) - - def _setup_fake_inst_obj(self): - keys = ('image_ref', 'uuid', 'user_id', 'project_id', - 'power_state', 'system_metadata', 'memory_mb', 'vcpus', - 'root_gb', 'ephemeral_gb') - self._fake_inst = {'name': 'os000001'} - self._old_inst = {'name': 'rszos000001'} - for k in keys: - self._fake_inst[k] = '' - self._old_inst[k] = '' - - @mock.patch.object(driver.ZVMDriver, 'update_host_status') - @mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_xcat_version') - def setUp(self, mock_version, mock_update): - super(ZVMDriverTestCases, self).setUp() - self._setup_fake_inst_obj() - mock_update.return_value = [{ - 'host': 'fakehost', - 'allowed_vm_type': 'zLinux', - 'vcpus': 10, - 'vcpus_used': 10, - 'cpu_info': {'Architecture': 's390x', 'CEC model': '2097'}, - 'disk_total': 406105, - 'disk_used': 367263, - 'disk_available': 38842, - 'host_memory_total': 16384, - 'host_memory_free': 8096, - 'hypervisor_type': 'zvm', - 'hypervisor_version': '630', - 'hypervisor_hostname': 'fakenode', - 'supported_instances': [('s390x', 'zvm', 'hvm')], - 'zhcp': {'hostname': 'fakehcp.fake.com', - 'nodename': 'fakehcp', 'userid': 'fakehcp'}, - 'ipl_time': 'IPL at 03/13/14 21:43:12 EDT', - }] - mock_version.return_value = '100.100.100.100' - self.driver = driver.ZVMDriver(fake.FakeVirtAPI()) - self.mox.UnsetStubs() - - def test_init_driver(self): - self.assertIsInstance(self.driver._xcat_url, zvmutils.XCATUrl) - self.assertIsInstance(self.driver._zvm_images, imageop.ZVMImages) - self.assertIsInstance(self.driver._pathutils, zvmutils.PathUtils) - self.assertIsInstance(self.driver._networkop, - networkop.NetworkOperator) - - def test_update_host_info(self): - self._set_fake_xcat_responses([self._fake_host_rinv_info(), - self._fake_disk_info()]) - host_info = self.driver.update_host_status()[0] - self.mox.VerifyAll() - self.assertEqual(host_info['hypervisor_hostname'], 'fakenode') - self.assertEqual(host_info['host_memory_total'], 16 * 1024) - - def _fake_instance_list_data(self): - return {'data': [{'data': self._fake_instances_list()}]} - - def _fake_instances_list(self): - inst_list = ['#node,hcp,userid,nodetype,parent,comments,disable', - '"fakehcp","fakehcp.fake.com","HCP","vm","fakenode"', - '"fakenode","fakehcp.fake.com",,,,,', - '"os000001","fakehcp.fake.com","OS000001",,,,'] - return inst_list - - def test_list_instances(self): - self._set_fake_xcat_responses([self._fake_instance_list_data()]) - inst_list = self.driver.list_instances() - self.mox.VerifyAll() - self.assertIn("os000001", inst_list) - - def test_list_instances_exclude_xcat_master(self): - self.flags(zvm_xcat_master='xcat') - fake_inst_list = self._fake_instances_list() - fake_inst_list.append('"xcat","fakexcat.fake.com",,,,,') - self._set_fake_xcat_responses([ - {'data': [{'data': fake_inst_list}]}]) - inst_list = self.driver.list_instances() - self.mox.VerifyAll() - self.assertIn("os000001", inst_list) - self.assertNotIn("xcat", inst_list) - - def test_get_available_resource(self): - self._set_fake_xcat_responses([self._fake_host_rinv_info(), - self._fake_disk_info()]) - res = self.driver.get_available_resource('fakenode') - self.mox.VerifyAll() - self.assertEqual(res['vcpus'], 10) - self.assertEqual(res['memory_mb_used'], 16 * 1024) - self.assertEqual(res['disk_available_least'], 38843) - - def _fake_instance_info(self): - inst_inv_info = [ - "os000001: Uptime: 4 days 20 hr 00 min\n" - "os000001: CPU Used Time: 330528353\n" - "os000001: Total Memory: 128M\n" - "os000001: Max Memory: 2G\n" - "os000001: \n" - "os000001: Processors: \n" - "os000001: CPU 03 ID FF00EBBE20978000 CP CPUAFF ON\n" - "os000001: CPU 00 ID FF00EBBE20978000 (BASE) CP CPUAFF ON\n" - "os000001: CPU 01 ID FF00EBBE20978000 CP CPUAFF ON\n" - "os000001: CPU 02 ID FF00EBBE20978000 CP CPUAFF ON\n" - "os000001: \n" - ] - return self._generate_xcat_resp(inst_inv_info) - - def _fake_reachable_data(self, stat): - # return {"data": [{"node": [{"name": ["os000001"], "data": [stat]}]}]} - return self._set_reachable(stat) - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.get_info') - def test_get_info(self, mk_get_info): - _fake_inst_obj = hardware.InstanceInfo(state=0x01, mem_kb=131072, - num_cpu=4, cpu_time_ns=330528353, - max_mem_kb=1048576) - mk_get_info.return_value = _fake_inst_obj - - inst_info = self.driver.get_info(self.instance) - self.assertEqual(0x01, inst_info.state) - self.assertEqual(131072, inst_info.mem_kb) - self.assertEqual(4, inst_info.num_cpu) - self.assertEqual(330528353, inst_info.cpu_time_ns) - self.assertEqual(1048576, inst_info.max_mem_kb) - - @mock.patch('nova_zvm.virt.zvm.exception.ZVMXCATRequestFailed.' - 'format_message') - @mock.patch('nova_zvm.virt.zvm.exception.ZVMXCATRequestFailed.msg_fmt') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.get_info') - def test_get_info_not_exist(self, mk_get_info, mk_msg_fmt, mk_fm): - mk_get_info.side_effect = exception.ZVMXCATRequestFailed - _emsg = "Forbidden Invalid nodes and/or groups" - mk_msg_fmt.return_value = _emsg - mk_fm.return_value = _emsg - - self.assertRaises(nova_exception.InstanceNotFound, - self.driver.get_info, self.instance) - - @mock.patch('nova_zvm.virt.zvm.exception.ZVMXCATRequestFailed.msg_fmt') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.get_info') - def test_get_info_other_err(self, mk_get_info, mk_msg_fmt): - mk_get_info.side_effect = exception.ZVMXCATRequestFailed - mk_msg_fmt.return_value = "Other error" - - self.assertRaises(exception.ZVMXCATRequestFailed, - self.driver.get_info, self.instance) - - def test_destroy(self): - rmvm_info = ["os000001: Deleting virtual server OS000001... Done"] - fake_resp_list = [ - ("GET", None, None, self._fake_instance_list_data()), - # ("GET", None, None, self._fake_reachable_data('sshd')), - ("PUT", None, None, self._fake_reachable_data(': reachable')), - ("DELETE", None, None, self._gen_resp(info=rmvm_info))] - self._set_fake_xcat_resp(fake_resp_list) - self.driver.destroy({}, self.instance, {}, {}) - self.mox.VerifyAll() - - def test_destroy_failed(self): - rmvm_info = ["os000001: Deleting virtual server OS000001... Failed"] - det_res = self._gen_resp(info=rmvm_info, error=['error']) - fake_resp_list = [ - ("GET", None, None, self._fake_instance_list_data()), - # ("GET", None, None, self._fake_reachable_data('sshd')), - ("PUT", None, None, self._fake_reachable_data(': reachable')), - ("DELETE", None, None, det_res)] - self._set_fake_xcat_resp(fake_resp_list) - # Will not reraise xcat error anymore because it could interrupt the - # process of removing resources from the failed instance. Instead there - # will be a warning message in the log. - self.driver.destroy({}, self.instance, {}, {}) - self.mox.VerifyAll() - - def test_destroy_non_exist(self): - self.stubs.Set(self.driver, '_instance_exists', self._fake_fun(False)) - self.driver.destroy({}, self.instance2, {}, {}) - self.mox.VerifyAll() - - def fake_image_get(self, context, id): - return self._fake_image_meta() - - def fake_imgmeta_obj(self): - return objects.ImageMeta.from_dict({"id": "image_id"}) - - def _fake_image_meta(self): - return {'checksum': '1a2bbbdbcc9c536a2688fc6278685dfb', - 'container_format': 'bare', - 'disk_format': 'raw', - 'id': 'bef39792-1ae2-46f5-b44c-0641bfcb3b98', - 'is_public': False, - 'name': 'fakeimg', - 'properties': {'architecture': 's390x', - 'image_file_name': 'abc', - 'image_type_xcat': 'linux', - 'os_name': 'Linux', - 'os_version': 'rhel6.2', - 'provisioning_method': 'netboot', - 'root_disk_units': '578181045:BLK', - }, - 'size': 578181045, - 'status': 'active', - 'min_disk': 3, - } - - def _fake_network_info(self): - info = [ - ( - {'ovs_interfaceid': '6ef5433c-f29b-4bcc-b8c5-f5159d7e05ba', - 'network': ( - {'bridge': 'br-int', - 'subnets': [({ - 'ips': [({'meta': {}, - 'version': 4, - 'type': 'fixed', - 'floating_ips': [], - 'address': '10.1.11.51' - })], - 'version': 4, - 'meta': {}, - 'dns': [], - 'routes': [], - 'cidr': '10.1.0.0/16', - 'gateway': ({'meta': {}, - 'version': 4, - 'type': 'gateway', - 'address': u'10.1.0.1' - }) - })], - 'meta': {'injected': False, - 'tenant_id': '0be9e98fdf6d4f599632226154c6c86c'}, - 'id': '01921c21-0373-4ccf-934e-9c6b2ccd7bdc', - 'label': u'xcat_management' - } - ), - 'devname': 'tap6ef5433c-f2', - 'qbh_params': None, - 'meta': {}, - 'address': '02:00:00:ee:ae:51', - 'type': 'ovs', - 'id': '6ef5433c-f29b-4bcc-b8c5-f5159d7e05ba', - 'qbg_params': None - } - ) - ] - return info - - def test_spawn(self): - self.stubs.Set(self.instance, 'save', self._fake_fun()) - self.instance['config_drive'] = True - self.stubs.Set(self.driver._pathutils, 'get_instance_path', - self._fake_fun('/temp/os000001')) - self.stubs.Set(virt_configdrive, 'required_by', self._fake_fun(True)) - self.stubs.Set(self.driver, '_create_config_drive', - self._fake_fun('/temp/os000001/configdrive.tgz')) - self.stubs.Set(instance.ZVMInstance, 'create_xcat_node', - self._fake_fun()) - self.stubs.Set(self.driver, '_preset_instance_network', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'image_exist_xcat', - self._fake_fun(False)) - self.stubs.Set(self.driver, '_import_image_to_xcat', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'create_userid', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'update_node_info', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'get_imgname_xcat', - self._fake_fun('fakeimg')) - self.stubs.Set(dist.LinuxDist, - "create_network_configuration_files", - self._fake_fun((['fakefile', 'fakecmd']))) - self.stubs.Set(instance.ZVMInstance, 'deploy_node', self._fake_fun()) - self.stubs.Set(self.driver._pathutils, 'clean_temp_folder', - self._fake_fun()) - self.stubs.Set(self.driver, '_add_nic_to_table', self._fake_fun()) - self.stubs.Set(zvmutils, 'punch_adminpass_file', self._fake_fun()) - self.stubs.Set(zvmutils, 'punch_iucv_file', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'power_on', self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'update_last_use_date', - self._fake_fun()) - self.stubs.Set(self.driver, '_wait_and_get_nic_direct', - self._fake_fun()) - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.driver.spawn({}, self.instance, self.fake_imgmeta_obj(), ['fake'], - 'fakepass', self._fake_network_info(), {}) - - @mock.patch.object(six.moves.builtins, 'open') - def test_spawn_with_eph(self, mock_open): - self.instance['config_drive'] = True - self.stubs.Set(self.driver._pathutils, 'get_instance_path', - self._fake_fun('/temp/os000001')) - self.stubs.Set(self.driver, '_create_config_drive', - self._fake_fun('/temp/os000001/configdrive.tgz')) - self.stubs.Set(virt_configdrive, 'required_by', self._fake_fun(True)) - self.stubs.Set(instance.ZVMInstance, 'create_xcat_node', - self._fake_fun()) - self.stubs.Set(self.driver, '_preset_instance_network', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'image_exist_xcat', - self._fake_fun(False)) - self.stubs.Set(self.driver, '_import_image_to_nova', self._fake_fun()) - self.stubs.Set(self.driver, '_import_image_to_xcat', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'create_userid', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'update_node_info', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'get_imgname_xcat', - self._fake_fun('fakeimg')) - self.stubs.Set(dist.LinuxDist, - "create_network_configuration_files", - self._fake_fun((['fakefile', 'fakecmd']))) - self.stubs.Set(instance.ZVMInstance, 'deploy_node', self._fake_fun()) - self.stubs.Set(self.driver._pathutils, 'clean_temp_folder', - self._fake_fun()) - self.stubs.Set(self.driver, '_add_nic_to_table', self._fake_fun()) - self.stubs.Set(zvmutils, 'punch_adminpass_file', self._fake_fun()) - self.stubs.Set(zvmutils, 'punch_iucv_file', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'power_on', self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'update_last_use_date', - self._fake_fun()) - self.stubs.Set(self.driver, '_wait_and_get_nic_direct', - self._fake_fun()) - self.stubs.Set(os, 'remove', self._fake_fun()) - self.stubs.Set(zvmutils, 'get_host', self._fake_fun("fake@10.1.1.10")) - self._set_fake_xcat_resp([ - ("PUT", None, None, self._gen_resp(info='done')), - ]) - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.stubs.Set(self.instance2, 'save', self._fake_fun()) - self.driver.spawn({}, self.instance2, self.fake_imgmeta_obj(), - ['fake'], 'fakepass', self._fake_network_info(), {}) - self.mox.VerifyAll() - - def test_spawn_with_eph_opts(self): - self.stubs.Set(self.instance, 'save', self._fake_fun()) - self.instance['config_drive'] = True - self.instance['ephemeral_gb'] = 2 - network_info = self._fake_network_info() - image_meta = self._fake_image_meta() - fake_bdi = {'ephemerals': [ - {'device_name': '/dev/sdb', - 'device_type': None, - 'disk_bus': None, - 'guest_format': u'ext4', - 'size': 2}, - {'device_name': '/dev/sdc', - 'device_type': None, - 'disk_bus': None, - 'guest_format': u'ext3', - 'size': 1}]} - - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.StubOutWithMock(self.driver._pathutils, 'get_instance_path') - self.mox.StubOutWithMock(dist.LinuxDist, - "create_network_configuration_files") - self.stubs.Set(virt_configdrive, 'required_by', self._fake_fun(True)) - self.mox.StubOutWithMock(self.driver, '_create_config_drive') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_xcat_node') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(self.driver._zvm_images, 'image_exist_xcat') - self.mox.StubOutWithMock(self.driver._zvm_images, 'get_imgname_xcat') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_userid') - self.mox.StubOutWithMock(instance.ZVMInstance, 'update_node_info') - self.stubs.Set(self.driver, '_add_nic_to_table', self._fake_fun()) - self.mox.StubOutWithMock(instance.ZVMInstance, 'deploy_node') - self.mox.StubOutWithMock(self.driver._pathutils, 'clean_temp_folder') - self.mox.StubOutWithMock(zvmutils, 'punch_adminpass_file') - self.mox.StubOutWithMock(zvmutils, 'punch_iucv_file') - self.mox.StubOutWithMock(zvmutils, 'process_eph_disk') - self.stubs.Set(self.driver, '_wait_and_get_nic_direct', - self._fake_fun()) - self.mox.StubOutWithMock(instance.ZVMInstance, 'power_on') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'update_last_use_date') - - self.driver._pathutils.get_instance_path('fakenode', - 'os000001').AndReturn('/temp/os000001') - dist.LinuxDist.create_network_configuration_files( - mox.IgnoreArg(), network_info, mox.IgnoreArg() - ).AndReturn(('/tmp/fakefile', 'fakecmd')) - self.driver._create_config_drive(self.context, - '/temp/os000001', self.instance, - mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn('/temp/os000001/configdrive.tgz') - - self.driver._zvm_images.image_exist_xcat(mox.IgnoreArg()).AndReturn( - True) - self.driver._zvm_images.get_imgname_xcat(mox.IgnoreArg()).AndReturn( - 'fakeimg') - instance.ZVMInstance.create_xcat_node('fakehcp.fake.com') - instance.ZVMInstance.create_userid(fake_bdi, - image_meta, mox.IgnoreArg(), 'fakeimg') - self.driver._preset_instance_network('os000001', network_info) - self.driver._add_nic_to_table('os000001', 'fake') - instance.ZVMInstance.update_node_info(image_meta) - instance.ZVMInstance.deploy_node('fakeimg', - '/temp/os000001/configdrive.tgz') - zvmutils.punch_adminpass_file(mox.IgnoreArg(), 'os000001', 'pass', - mox.IgnoreArg()) - zvmutils.punch_iucv_file('rhel6.2', 'fakehcp.fake.com', - 'fakehcp', 'os000001', '/temp/os000001') - zvmutils.process_eph_disk('os000001', mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg()) - zvmutils.process_eph_disk('os000001', mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg()) - self.driver._wait_and_get_nic_direct('os000001') - instance.ZVMInstance.power_on() - self.driver._pathutils.clean_temp_folder(mox.IgnoreArg()) - self.driver._zvm_images.update_last_use_date(mox.IgnoreArg()) - self.mox.ReplayAll() - - self.driver.spawn(self.context, self.instance, self.fake_imgmeta_obj(), - ['fake'], 'fakepass', self._fake_network_info(), fake_bdi) - self.mox.VerifyAll() - - def test_spawn_image_error(self): - self.stubs.Set(self.instance, 'save', self._fake_fun()) - self.stubs.Set(self.driver._pathutils, 'get_instance_path', - self._fake_fun('/temp/os000001')) - self.stubs.Set(virt_configdrive, 'required_by', self._fake_fun(True)) - self.stubs.Set(self.driver, '_create_config_drive', - self._fake_fun('/temp/os000001/configdrive.tgz')) - self.stubs.Set(instance.ZVMInstance, 'create_xcat_node', - self._fake_fun()) - self.stubs.Set(self.driver, '_preset_instance_network', - self._fake_fun()) - self.stubs.Set(dist.LinuxDist, - "create_network_configuration_files", - self._fake_fun(('/tmp/fakefile', 'fakecmd'))) - self.mox.StubOutWithMock(self.driver._zvm_images, 'image_exist_xcat') - self.driver._zvm_images.image_exist_xcat(mox.IgnoreArg() - ).AndRaise(exception.ZVMImageError(msg='fake')) - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.ReplayAll() - self.stubs.Set(instance.ZVMInstance, 'delete_xcat_node', - self._fake_fun()) - self.assertRaises(exception.ZVMImageError, self.driver.spawn, {}, - self.instance, self.fake_imgmeta_obj(), [], - 'fakepass', self._fake_network_info(), {}) - self.mox.VerifyAll() - - def test_spawn_deploy_failed(self): - self.stubs.Set(self.instance, 'save', self._fake_fun()) - self.stubs.Set(self.driver._pathutils, 'get_instance_path', - self._fake_fun('/temp/os000001')) - self.stubs.Set(virt_configdrive, 'required_by', self._fake_fun(True)) - self.stubs.Set(self.driver, '_create_config_drive', - self._fake_fun('/temp/os000001/configdrive.tgz')) - self.stubs.Set(instance.ZVMInstance, 'create_xcat_node', - self._fake_fun()) - self.stubs.Set(self.driver, '_preset_instance_network', - self._fake_fun()) - self.stubs.Set(self.driver, '_add_nic_to_table', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'image_exist_xcat', - self._fake_fun(False)) - self.stubs.Set(dist.LinuxDist, - "create_network_configuration_files", - self._fake_fun(("/tmp/fakefile", "fakecmd"))) - self.stubs.Set(self.driver, '_import_image_to_xcat', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'create_userid', self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'update_node_info', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'get_imgname_xcat', - self._fake_fun('fakeimg')) - self.mox.StubOutWithMock(instance.ZVMInstance, 'deploy_node') - instance.ZVMInstance.deploy_node(mox.IgnoreArg(), mox.IgnoreArg() - ).AndRaise(exception.ZVMXCATDeployNodeFailed( - node='os000001', msg='fake')) - self.mox.ReplayAll() - self.stubs.Set(self.driver._pathutils, 'clean_temp_folder', - self._fake_fun()) - self.stubs.Set(self.driver, 'destroy', self._fake_fun()) - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.assertRaises(exception.ZVMXCATDeployNodeFailed, self.driver.spawn, - {}, self.instance, self.fake_imgmeta_obj(), [], - 'fakepass', self._fake_network_info(), {}) - self.mox.VerifyAll() - - def _set_reachable(self, stat): - return {"data": [{"info": ["os000001: reachable"]}]} - - def test_power_on(self): - info = ["os000001: Activating OS000001... Done\n"] - self._set_fake_xcat_responses([self._generate_xcat_resp(info), - self._set_reachable('reachable')]) - self.driver.power_on({}, self.instance, {}) - - def _fake_manifest(self): - return {'imagename': 'fakeimg', - 'imagetype': 'raw', - 'osarch': 's390x', - 'osname': 'fakeos', - 'osvers': 'fakev', - 'profile': 'fakeprof', - 'provmethod': 'netboot'} - - @mock.patch.object(six.moves.builtins, 'open') - def test_snapshot(self, mock_open): - if not os.path.exists("/tmp/fakeimg"): - os.mknod("/tmp/fakeimg") - self.instance['power_state'] = 0x01 - self.stubs.Set(glance, 'get_remote_image_service', - self._fake_fun((FakeImageService(self.fake_image_meta), 0))) - url_fspace = ''.join([self._app_auth("/xcatws/nodes/fakemn/inventory"), - "&field=--freerepospace"]) - res_fspace = self._gen_resp(info=["gpok164: Free Image " - "Repository: 13.9G"]) - self._set_fake_xcat_resp([ - ("GET", url_fspace, None, res_fspace), - ]) - - self.stubs.Set(self.driver, '_get_user_directory', - self._fake_fun(None)) - self.stubs.Set(self.driver._zvm_images, 'get_imgcapture_needed', - self._fake_fun(3.0)) - self.stubs.Set(self.driver._zvm_images, 'create_zvm_image', - self._fake_fun('')) - self.stubs.Set(self.driver, 'power_on', self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'get_image_from_xcat', - self._fake_fun(('/tmp', 'fakeimg.tgz'))) - self.stubs.Set(self.driver._zvm_images, 'delete_image_from_xcat', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'update_last_use_date', - self._fake_fun()) - self.stubs.Set(self.driver, '_is_shared_image_repo', - self._fake_fun(False)) - self.stubs.Set(self.driver._zvm_images, 'untar_image_bundle', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'parse_manifest_xml', - self._fake_fun(self._fake_manifest())) - self.stubs.Set(self.driver._zvm_images, 'get_image_file_name', - self._fake_fun('fakeimg')) - self.stubs.Set(self.driver._zvm_images, 'clean_up_snapshot_time_path', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, 'get_root_disk_units', - self._fake_fun(1111)) - self.stubs.Set(os, 'makedirs', self._fake_fun()) - - self.driver.snapshot({}, self.instance, '0000-1111', self._fake_fun()) - self.mox.VerifyAll() - - def test_snapshot_capture_failed(self): - self.instance['power_state'] = 0x04 - self.stubs.Set(self.driver, 'power_on', self._fake_fun()) - self.stubs.Set(glance, 'get_remote_image_service', - self._fake_fun((FakeImageService(self._fake_image_meta()), 0))) - self.stubs.Set(self.driver._zvm_images, 'get_free_space_xcat', - self._fake_fun(20.0)) - self.stubs.Set(self.driver, '_get_user_directory', - self._fake_fun(None)) - self.stubs.Set(self.driver._zvm_images, 'get_imgcapture_needed', - self._fake_fun(10.0)) - self.mox.StubOutWithMock(self.driver._zvm_images, 'create_zvm_image') - self.driver._zvm_images.create_zvm_image(mox.IgnoreArg(), - mox.IgnoreArg(), mox.IgnoreArg()).AndRaise( - exception.ZVMImageError(msg='fake')) - self.mox.ReplayAll() - self.stubs.Set(self.driver._zvm_images, 'delete_image_glance', - self._fake_fun()) - self.assertRaises(exception.ZVMImageError, self.driver.snapshot, - {}, self.instance, '0000-1111', self._fake_fun()) - self.mox.VerifyAll() - - @mock.patch.object(six.moves.builtins, 'open') - def test_snapshot_all_in_one_mode(self, mock_open): - fake_menifest = {'imagetype': 'linux', - 'osarch': 's390x', - 'imagename': 'fakeimg-uuid', - 'osname': 'Linux', - 'osvers': 'rhel65', - 'profile': 'fakeimg-uuid', - 'provmethod': 'netboot'} - self.instance['power_state'] = 0x01 - - self.stubs.Set(glance, 'get_remote_image_service', - self._fake_fun((FakeImageService(self.fake_image_meta), 0))) - - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_free_space_xcat') - self.mox.StubOutWithMock(self.driver, '_get_user_directory') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_imgcapture_needed') - self.mox.StubOutWithMock(self.driver._zvm_images, 'create_zvm_image') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'update_last_use_date') - self.mox.StubOutWithMock(self.driver, '_reset_power_state') - self.mox.StubOutWithMock(self.driver, '_is_shared_image_repo') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_image_file_path_from_image_name') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_image_file_name') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_root_disk_units') - self.mox.StubOutWithMock(self.driver._zvm_images, 'get_image_menifest') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'delete_image_from_xcat') - self.driver._zvm_images.get_free_space_xcat( - CONF.xcat_free_space_threshold, - CONF.zvm_xcat_master).AndReturn(10.0) - fake_dict = [ - 'os000001: MDISK 0100 3390 n 100 n MR\n'] - self.driver._get_user_directory('os000001').AndReturn(fake_dict) - self.driver._zvm_images.get_imgcapture_needed( - self.instance, fake_dict).AndReturn(5.0) - self.driver._zvm_images.create_zvm_image(self.instance, 'fakeimg', - 'uuid').AndReturn('fakeimg-uuid') - self.driver._zvm_images.update_last_use_date('fakeimg-uuid') - self.driver._reset_power_state(0x01, self.instance) - self.driver._is_shared_image_repo('fakeimg-uuid').AndReturn(True) - self.driver._zvm_images.get_image_file_path_from_image_name( - 'fakeimg-uuid').AndReturn('/install/netboot/rh/s390/fakeimg-uuid') - self.driver._zvm_images.get_image_file_name( - '/install/netboot/rh/s390/fakeimg-uuid').AndReturn('fakeimg.img') - self.driver._zvm_images.get_root_disk_units( - '/install/netboot/rh/s390/fakeimg-uuid/fakeimg.img').AndReturn(999) - self.driver._zvm_images.get_image_menifest('fakeimg-uuid').AndReturn( - fake_menifest) - self.driver._zvm_images.delete_image_from_xcat(mox.IgnoreArg()) - self.mox.ReplayAll() - - self.driver.snapshot(self.context, self.instance, 'uuid', - self._fake_fun()) - self.mox.VerifyAll() - - def test_reboot_hard(self): - info1 = ["os000001: Shutting down... Done"] - info2 = ["os000001: Activating... Done"] - data = {"data": [{"info": info1}, {"info": info2}]} - self._set_fake_xcat_responses([data, self._set_reachable('reachable')]) - self.driver.reboot(self.context, self.instance, {}, "HARD") - self.mox.VerifyAll() - - def test_reboot_hard_from_down(self): - info1 = ["os000001: Shutting down... Failed"] - info2 = ["os000001: Activating... Done"] - data = {"data": [{"info": info1}, {"info": info2}]} - self._set_fake_xcat_responses([data, self._set_reachable('reachable')]) - self.driver.reboot(self.context, self.instance, {}, "HARD") - self.mox.VerifyAll() - - def test_reboot_hard_failed(self): - info1 = ["os000001: Shutting down... Failed"] - info2 = ["os000001: Activating... Failed"] - self._set_fake_xcat_responses([self._gen_resp(info=[info1, info2], - error=['(error)'])]) - self.assertRaises(exception.ZVMXCATInternalError, - self.driver.reboot, - self.context, self.instance, {}, "HARD") - - def test_reboot_soft(self): - info1 = ["os000001: Shutting down... Done"] - info2 = ["os000001: Activating... Done"] - data = {"data": [{"info": info1}, {"info": info2}]} - self._set_fake_xcat_responses([data, self._set_reachable('reachable')]) - self.driver.reboot(self.context, self.instance, {}, "SOFT") - self.mox.VerifyAll() - - def test_reboot_soft_failed(self): - info1 = ["os000001: Shutting down... Failed\n"] - info2 = ["os000001: Activating... Failed\n"] - self._set_fake_xcat_responses([self._gen_resp(info=[info1, info2], - error=['(error)'])]) - self.assertRaises(exception.ZVMXCATInternalError, - self.driver.reboot, - self.context, self.instance, {}, "SOFT") - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.power_off') - def test_power_off(self, poff): - self.driver.power_off(self.instance, 60, 10) - poff.assert_called_once_with(60, 10) - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._power_state') - def test_power_off_inactive(self, pst): - pst.side_effect = exception.ZVMXCATInternalError( - "Return Code: 200 Reason Code: 12") - self.driver.power_off(self.instance, 60, 10) - pst.assert_called_once_with("PUT", "softoff") - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._check_power_stat') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._power_state') - def test_power_off_retry(self, pst, get_pst): - get_pst.side_effect = [0x00, 0x04] - self.driver.power_off(self.instance, 60, 10) - pst.assert_called_once_with("PUT", "softoff") - self.assertTrue(get_pst.called) - - def test_power_off_failed(self): - info = ["os000001: Stopping OS000001... Failed\n"] - self._set_fake_xcat_responses([self._gen_resp(info=info, - error=['error'])]) - self.assertRaises(nova_exception.InstancePowerOffFailure, - self.driver.power_off, self.instance) - - def _fake_connection_info(self): - conn_info = { - 'driver_volume_type': 'fibre_channel', - 'data': {'volume_name': 'fakev', - 'volume_id': 1, - 'volume_type': '', - 'volume_status': 'online', - 'volume_size': 10, - 'provider_location': '500507630B40C038:401340C900000000', - 'zvm_fcp': 'F000', - 'target_lun': '442', - 'target_wwn': '401340C900000000', - 'host': 'fakehost', - } - } - return conn_info - - def test_attach_volume(self): - self.instance.vm_state = vm_states.ACTIVE - self.mox.StubOutWithMock(self.driver, '_format_mountpoint') - self.mox.StubOutWithMock(self.driver, '_instance_exists') - self.mox.StubOutWithMock(instance.ZVMInstance, 'is_reachable') - self.mox.StubOutWithMock(instance.ZVMInstance, 'attach_volume') - self.driver._format_mountpoint('/dev/sdd').AndReturn('/dev/vdd') - self.driver._instance_exists('os000001').AndReturn(True) - instance.ZVMInstance.is_reachable().AndReturn(True) - instance.ZVMInstance.attach_volume(self.driver._volumeop, {}, - self._fake_connection_info(), - self.instance, '/dev/vdd', True) - self.mox.ReplayAll() - - self.driver.attach_volume({}, self._fake_connection_info(), - self.instance, '/dev/sdd') - self.mox.VerifyAll() - - def test_attach_volume_invalid_vm_states(self): - self.instance.vm_state = vm_states.PAUSED - self.assertRaises(exception.ZVMDriverError, - self.driver.attach_volume, - {}, None, self.instance, None) - self.instance.vm_state = vm_states.ACTIVE - - def test_attach_volume_no_mountpoint(self): - self.instance.vm_state = vm_states.ACTIVE - self.mox.StubOutWithMock(self.driver, '_instance_exists') - self.mox.StubOutWithMock(instance.ZVMInstance, 'is_reachable') - self.mox.StubOutWithMock(instance.ZVMInstance, 'attach_volume') - self.driver._instance_exists('os000001').AndReturn(True) - instance.ZVMInstance.is_reachable().AndReturn(True) - instance.ZVMInstance.attach_volume(self.driver._volumeop, {}, - self._fake_connection_info(), - self.instance, None, True) - self.mox.ReplayAll() - - self.driver.attach_volume({}, self._fake_connection_info(), - self.instance, None) - self.mox.VerifyAll() - - def test_live_migration_same_mn(self): - dest = 'fhost2' - migrate_data = {'dest_host': dest, - 'pre_live_migration_result': - {'same_xcat_mn': True, - 'dest_diff_mn_key': None}} - info = ["os000001: VMRELOCATE action=move against OS000001... Done\n"] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self.driver.live_migration(self.context, self.instance, dest, - self._fake_fun(), self._fake_fun(), None, - migrate_data) - self.mox.VerifyAll() - - def test_live_migration_diff_mn(self): - dest = 'fhost2' - migrate_data = {'dest_host': dest, - 'pre_live_migration_result': - {'same_xcat_mn': False, - 'dest_diff_mn_key': 'sshkey'}} - info = ["os000001: VMRELOCATE action=move against OS000001... Done\n"] - self.stubs.Set(zvmutils, "xdsh", self._fake_fun()) - self.stubs.Set(self.driver._networkop, 'clean_mac_switch_host', - self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'delete_xcat_node', - self._fake_fun()) - self._set_fake_xcat_resp([("PUT", None, None, - self._gen_resp(info=info))]) - self.driver.live_migration(self.context, self.instance, dest, - self._fake_fun(), self._fake_fun(), None, - migrate_data) - self.mox.VerifyAll() - - def test_live_migration_failed(self): - dest = 'fhost2' - migrate_data = {'dest_host': dest, - 'pre_live_migration_result': - {'same_xcat_mn': True, - 'dest_diff_mn_key': None}} - info = ["os000001: VMRELOCATE MOVE error\n"] - self._set_fake_xcat_responses([self._gen_resp(info=info, - error=['error'])]) - self.stubs.Set(zvmutils, "xdsh", self._fake_fun()) - self.assertRaises(nova_exception.MigrationError, - self.driver.live_migration, self.context, self.instance, dest, - self._fake_fun(), self._fake_fun(), None, migrate_data) - self.mox.VerifyAll() - - def test_check_can_live_migrate_destination(self): - dest = 'fhost2' - dest_info = {'hypervisor_hostname': dest} - res_dict = self.driver.check_can_live_migrate_destination(self.context, - self.instance, {}, dest_info, {}, {}) - exp_dict = {'dest_host': dest, - 'is_shared_storage': True} - self.assertEqual(res_dict.get('migrate_data', None), exp_dict) - - def test_check_can_live_migrate_source(self): - dest = 'fhost2' - migrate_data = {'dest_host': dest, - 'is_shared_storage': True} - dest_check_data = {'migrate_data': migrate_data} - info = ["os000001: VMRELOCATE action=test against OS000001... Done\n"] - self._set_fake_xcat_resp([ - ("GET", None, None, self._gen_resp(info=["userid=os000001"])), - ("PUT", None, None, self._gen_resp(info=info))]) - res_dict = self.driver.check_can_live_migrate_source( - self.context, self.instance, dest_check_data) - self.assertNotEqual(res_dict.get('dest_host', None), None) - self.mox.ReplayAll() - - def test_check_can_live_migrate_source_failed(self): - dest = 'fhost2' - migrate_data = {'dest_host': dest} - dest_check_data = {'migrate_data': migrate_data} - error = ["VMRELOCATE move failed"] - - self.mox.StubOutWithMock(zvmutils, "get_userid") - self.mox.StubOutWithMock(self.driver, "_vmrelocate") - zvmutils.get_userid('os000001').AndReturn("os000001") - self.driver._vmrelocate('fhost2', 'os000001', 'test').AndRaise( - nova_exception.MigrationError(reason=error)) - self.mox.ReplayAll() - - self.assertRaises(nova_exception.MigrationError, - self.driver.check_can_live_migrate_source, self.context, - self.instance, dest_check_data) - self.mox.VerifyAll() - - def test_check_can_live_migrate_source_force(self): - self.flags(zvm_vmrelocate_force='domain') - dest = 'fhost2' - migrate_data = {'dest_host': dest} - dest_check_data = {'migrate_data': migrate_data} - - self.mox.StubOutWithMock(zvmutils, "get_userid") - self.mox.StubOutWithMock(self.driver, "_vmrelocate") - zvmutils.get_userid('os000001').AndReturn("os000001") - self.driver._vmrelocate('fhost2', 'os000001', 'test').AndRaise( - nova_exception.MigrationError(reason="1944")) - self.mox.ReplayAll() - - self.driver.check_can_live_migrate_source(self.context, self.instance, - dest_check_data) - self.mox.VerifyAll() - - def test_migrate_disk_and_power_off(self): - - inst = fake_instance.fake_instance_obj(self.context) - - self.stubs.Set(zvmutils, 'get_host', self._fake_fun("root@10.1.1.10")) - self.mox.StubOutWithMock(zvmutils, 'get_userid') - self.mox.StubOutWithMock(self.driver, '_get_eph_disk_info') - self.mox.StubOutWithMock(self.driver, '_detach_volume_from_instance') - self.mox.StubOutWithMock(self.driver, '_capture_disk_for_instance') - self.mox.StubOutWithMock(self.driver, '_is_shared_image_repo') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_snapshot_time_path') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'get_image_from_xcat') - - zvmutils.get_userid('os000001').AndReturn('os000001') - self.driver._get_eph_disk_info('os000001').AndReturn([]) - self.driver._detach_volume_from_instance(mox.IgnoreArg(), - mox.IgnoreArg()) - self.driver._capture_disk_for_instance({}, mox.IgnoreArg()).AndReturn( - ('fakeimagename')) - self.driver._is_shared_image_repo('fakeimagename').AndReturn(False) - self.driver._zvm_images.get_snapshot_time_path().AndReturn('/tmp') - self.driver._zvm_images.get_image_from_xcat('fakeimagename', - 'fakeimagename', '/tmp').AndReturn('/tmp/fakeimg') - self.mox.ReplayAll() - - class FakeInstanceType(object): - def __init__(self): - self.root_gb = 10 - self.ephemeral_gb = 10 - - fake_instance_type = FakeInstanceType() - disk_info = self.driver.migrate_disk_and_power_off({}, inst, - '10.1.1.11', fake_instance_type, [({}, {})]) - self.mox.VerifyAll() - - exp = { - "eph_disk_info": [], - "disk_image_name": "fakeimagename", - "disk_owner": "os000001", - "disk_eph_size_old": 0, - "shared_image_repo": False, - "disk_eph_size_new": 10, - "disk_type": "FBA", - "disk_source_image": - "root@10.1.1.10:/tmp/fakeimg", - "disk_source_mn": "10.10.10.10" - } - - self.assertEqual(exp, jsonutils.loads(disk_info)) - - def test_capture_disk_for_instance_all_in_one_mode(self): - self.mox.StubOutWithMock(instance.ZVMInstance, 'get_provmethod') - self.mox.StubOutWithMock(self.driver._zvm_images, 'create_zvm_image') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'update_last_use_date') - instance.ZVMInstance.get_provmethod().AndReturn('sysclone') - self.driver._zvm_images.create_zvm_image(self.instance, 'rszos000001', - mox.IgnoreArg()).AndReturn('image-name-xcat') - self.driver._zvm_images.update_last_use_date('image-name-xcat') - self.mox.ReplayAll() - - imgn = self.driver._capture_disk_for_instance(self.context, - self.instance) - self.mox.VerifyAll() - self.assertEqual(imgn, 'image-name-xcat') - - def test_migrate_disk_and_power_off_not_support(self): - - inst = fake_instance.fake_instance_obj(self.context) - - class FakeInstanceType(object): - def __init__(self): - self.root_gb = -1 - self.ephemeral_gb = 10 - - fake_instance_type = FakeInstanceType() - self.assertRaises(nova_exception.InstanceFaultRollback, - self.driver.migrate_disk_and_power_off, self.context, - inst, '10.1.1.11', fake_instance_type, [({}, {})]) - - def test__get_eph_disk_info(self): - self.mox.StubOutWithMock(self.driver, '_get_user_directory') - self.driver._get_user_directory('os000001').AndReturn([ - 'os000001: MDISK 0100 3390 n 100 n MR\n', - 'os000001: MDISK 0102 3390 n 200 n MR\n', - 'os000001: MDISK 0103 3390 n 300 n MR\n']) - self.mox.ReplayAll() - - eph_disk_info = self.driver._get_eph_disk_info('os000001') - exp = [{'device_name': '0102', - 'guest_format': None, - 'size': '200', - 'size_in_units': True, - 'vdev': '0102'}, - {'device_name': '0103', - 'guest_format': None, - 'size': '300', - 'size_in_units': True, - 'vdev': '0103'}] - self.assertEqual(exp, eph_disk_info) - - def test__get_eph_disk_info_invalid_resp(self): - self.mox.StubOutWithMock(self.driver, '_get_user_directory') - self.driver._get_user_directory('os000001').AndReturn([ - 'os000001: MDISK 0101\n']) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMInvalidXCATResponseDataError, - self.driver._get_eph_disk_info, 'os000001') - - def _fake_bdi(self): - _fake_eph = {'guest_format': None, - 'device_name': '/dev/sdb', - 'disk_bus': None, - 'device_type': 'disk', - 'size': 2} - _fake_bdi = {'swap': None, - 'root_device_name': '/dev/sda', - 'ephemerals': [_fake_eph], - 'block_device_mapping': []} - return _fake_bdi - - def test_finish_migration_same_mn(self): - self.flags(zvm_xcat_server="10.10.10.10") - network_info = self._fake_network_info() - disk_info = { - 'disk_type': 'FBA', - 'disk_source_mn': '10.10.10.10', - 'disk_source_image': 'root@10.1.1.10:/fakepath/fa-ke-ima-ge.tgz', - 'disk_image_name': 'fa-ke-ima-ge', - 'disk_owner': 'os000001', - 'disk_eph_size_old': 0, - 'disk_eph_size_new': 0, - 'eph_disk_info': [], - 'shared_image_repo': False} - migration = {'source_node': 'FAKENODE1', - 'dest_node': 'FAKENODE2'} - disk_info = jsonutils.dumps(disk_info) - - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.StubOutWithMock(self.driver, 'get_host_ip_addr') - self.mox.StubOutWithMock(self.driver._networkop, - 'clean_mac_switch_host') - self.mox.StubOutWithMock(self.driver._pathutils, 'clean_temp_folder') - self.mox.StubOutWithMock(self.driver, '_copy_instance') - self.mox.StubOutWithMock(instance.ZVMInstance, 'copy_xcat_node') - self.mox.StubOutWithMock(instance.ZVMInstance, 'update_node_def') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_userid') - self.mox.StubOutWithMock(self.driver, '_add_nic_to_table') - self.mox.StubOutWithMock(self.driver, '_deploy_root_and_ephemeral') - self.mox.StubOutWithMock(self.driver, '_wait_and_get_nic_direct') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'delete_image_from_xcat') - self.mox.StubOutWithMock(zvmutils, 'punch_iucv_authorized_file') - self.mox.StubOutWithMock(instance.ZVMInstance, 'power_on') - self.mox.StubOutWithMock(zvmutils, 'xdsh') - self.mox.StubOutWithMock(self.driver, '_attach_volume_to_instance') - - farg = mox.IgnoreArg() - self.driver.get_host_ip_addr().AndReturn('10.1.1.10') - self.driver._networkop.clean_mac_switch_host(farg) - self.driver._pathutils.clean_temp_folder(farg) - self.driver._copy_instance(farg).AndReturn(self._fake_inst) - instance.ZVMInstance.copy_xcat_node(farg) - instance.ZVMInstance.update_node_def(farg, farg) - self.driver._preset_instance_network('os000001', farg) - instance.ZVMInstance.create_userid(farg, farg, farg) - self.driver._add_nic_to_table('os000001', farg) - self.driver._deploy_root_and_ephemeral(farg, farg) - self.driver._wait_and_get_nic_direct('os000001', self._fake_inst) - self.driver._zvm_images.delete_image_from_xcat(farg) - zvmutils.punch_iucv_authorized_file('rszos000001', 'os000001', - 'fakehcp') - instance.ZVMInstance.power_on() - self.driver._attach_volume_to_instance(farg, self._fake_inst, []) - self.mox.ReplayAll() - - self.driver.finish_migration(self.context, migration, self._fake_inst, - disk_info, network_info, - self.fake_imgmeta_obj(), None, - block_device_info=self._fake_bdi()) - self.mox.VerifyAll() - - def test_finish_migration_all_in_one_mode(self): - network_info = self._fake_network_info() - disk_info = { - 'disk_type': 'FBA', - 'disk_source_mn': '10.10.10.10', - 'disk_source_image': 'root@10.1.1.10:/fakepath/fa-ke-ima-ge.tgz', - 'disk_image_name': 'fa-ke-ima-ge', - 'disk_owner': 'os000001', - 'disk_eph_size_old': 0, - 'disk_eph_size_new': 0, - 'eph_disk_info': [], - 'shared_image_repo': True} - migration = {'source_node': 'FAKENODE1', - 'dest_node': 'FAKENODE2'} - disk_info = jsonutils.dumps(disk_info) - - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.StubOutWithMock(self.driver, 'get_host_ip_addr') - self.mox.StubOutWithMock(self.driver._networkop, - 'clean_mac_switch_host') - self.mox.StubOutWithMock(self.driver, '_copy_instance') - self.mox.StubOutWithMock(instance.ZVMInstance, 'copy_xcat_node') - self.mox.StubOutWithMock(instance.ZVMInstance, 'update_node_def') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_userid') - self.mox.StubOutWithMock(self.driver, '_add_nic_to_table') - self.mox.StubOutWithMock(self.driver, '_deploy_root_and_ephemeral') - self.mox.StubOutWithMock(self.driver, '_wait_and_get_nic_direct') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'delete_image_from_xcat') - self.mox.StubOutWithMock(zvmutils, 'punch_iucv_authorized_file') - self.mox.StubOutWithMock(instance.ZVMInstance, 'power_on') - self.mox.StubOutWithMock(zvmutils, 'xdsh') - self.mox.StubOutWithMock(self.driver, '_attach_volume_to_instance') - - farg = mox.IgnoreArg() - self.driver.get_host_ip_addr().AndReturn('10.1.1.10') - self.driver._networkop.clean_mac_switch_host(farg) - self.driver._copy_instance(farg).AndReturn(self._fake_inst) - instance.ZVMInstance.copy_xcat_node(farg) - instance.ZVMInstance.update_node_def(farg, farg) - self.driver._preset_instance_network('os000001', farg) - instance.ZVMInstance.create_userid(farg, farg, farg) - self.driver._add_nic_to_table('os000001', farg) - self.driver._deploy_root_and_ephemeral(farg, farg) - self.driver._wait_and_get_nic_direct('os000001', self._fake_inst) - self.driver._zvm_images.delete_image_from_xcat(farg) - zvmutils.punch_iucv_authorized_file('rszos000001', 'os000001', - 'fakehcp') - instance.ZVMInstance.power_on() - self.driver._attach_volume_to_instance(farg, self._fake_inst, []) - self.mox.ReplayAll() - - self.driver.finish_migration(self.context, migration, self._fake_inst, - disk_info, network_info, - self.fake_imgmeta_obj(), None, - block_device_info=self._fake_bdi()) - self.mox.VerifyAll() - - def test_finish_migration_same_mn_with_eph(self): - self.flags(zvm_xcat_server="10.10.10.10") - network_info = self._fake_network_info() - disk_info = { - 'disk_type': 'FBA', - 'disk_source_mn': '10.10.10.10', - 'disk_source_image': 'root@10.1.1.10:/fakepath/fa-ke-ima-ge.tgz', - 'disk_image_name': 'fa-ke-ima-ge', - 'disk_owner': 'os000001', - 'disk_eph_size_old': 0, - 'disk_eph_size_new': 1, - 'eph_disk_info': [], - 'shared_image_repo': False} - migration = {'source_node': 'FAKENODE1', - 'dest_node': 'FAKENODE2'} - disk_info = jsonutils.dumps(disk_info) - - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.StubOutWithMock(self.driver, 'get_host_ip_addr') - self.mox.StubOutWithMock(self.driver._networkop, - 'clean_mac_switch_host') - self.mox.StubOutWithMock(self.driver._pathutils, 'clean_temp_folder') - self.mox.StubOutWithMock(self.driver, '_copy_instance') - self.mox.StubOutWithMock(instance.ZVMInstance, 'copy_xcat_node') - self.mox.StubOutWithMock(instance.ZVMInstance, 'update_node_def') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_userid') - self.mox.StubOutWithMock(zvmutils, 'process_eph_disk') - self.mox.StubOutWithMock(self.driver, '_add_nic_to_table') - self.mox.StubOutWithMock(self.driver, '_deploy_root_and_ephemeral') - self.mox.StubOutWithMock(self.driver, '_wait_and_get_nic_direct') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'delete_image_from_xcat') - self.mox.StubOutWithMock(zvmutils, 'punch_iucv_authorized_file') - self.mox.StubOutWithMock(instance.ZVMInstance, 'power_on') - self.mox.StubOutWithMock(self.driver, '_attach_volume_to_instance') - - farg = mox.IgnoreArg() - self.driver.get_host_ip_addr().AndReturn('10.1.1.10') - self.driver._networkop.clean_mac_switch_host(farg) - self.driver._pathutils.clean_temp_folder(farg) - self.driver._copy_instance(farg).AndReturn(self._fake_inst) - instance.ZVMInstance.copy_xcat_node(farg) - instance.ZVMInstance.update_node_def(farg, farg) - self.driver._preset_instance_network('os000001', farg) - instance.ZVMInstance.create_userid(farg, farg, farg) - zvmutils.process_eph_disk('os000001') - self.driver._add_nic_to_table('os000001', farg) - self.driver._deploy_root_and_ephemeral(farg, farg) - self.driver._wait_and_get_nic_direct('os000001', self._fake_inst) - self.driver._zvm_images.delete_image_from_xcat(farg) - zvmutils.punch_iucv_authorized_file('rszos000001', 'os000001', - 'fakehcp') - instance.ZVMInstance.power_on() - self.driver._attach_volume_to_instance(farg, self._fake_inst, []) - self.mox.ReplayAll() - - self.driver.finish_migration(self.context, migration, self._fake_inst, - disk_info, network_info, - self.fake_imgmeta_obj(), None, - block_device_info=self._fake_bdi()) - self.mox.VerifyAll() - - def test_finish_migration_same_mn_deploy_failed(self): - """Exception raised when deploying, verify networking re-configured.""" - self.flags(zvm_xcat_server="10.10.10.10") - network_info = self._fake_network_info() - disk_info = { - 'disk_type': 'FBA', - 'disk_source_mn': '10.10.10.10', - 'disk_source_image': 'root@10.1.1.10:/fakepath/fa-ke-ima-ge.tgz', - 'disk_image_name': 'fa-ke-ima-ge', - 'disk_owner': 'os000001', - 'disk_eph_size_old': 0, - 'disk_eph_size_new': 0, - 'eph_disk_info': [], - 'shared_image_repo': False} - migration = {'source_node': 'FAKENODE1', - 'dest_node': 'FAKENODE2'} - disk_info = jsonutils.dumps(disk_info) - - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.StubOutWithMock(self.driver, 'get_host_ip_addr') - self.mox.StubOutWithMock(self.driver._networkop, - 'clean_mac_switch_host') - self.mox.StubOutWithMock(self.driver._pathutils, 'clean_temp_folder') - self.mox.StubOutWithMock(self.driver, '_copy_instance') - self.mox.StubOutWithMock(instance.ZVMInstance, 'copy_xcat_node') - self.mox.StubOutWithMock(instance.ZVMInstance, 'update_node_def') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_userid') - self.mox.StubOutWithMock(self.driver, '_add_nic_to_table') - self.mox.StubOutWithMock(self.driver, '_deploy_root_and_ephemeral') - self.mox.StubOutWithMock(self.driver, '_wait_and_get_nic_direct') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'delete_image_from_xcat') - self.mox.StubOutWithMock(instance.ZVMInstance, 'delete_userid') - self.mox.StubOutWithMock(instance.ZVMInstance, 'delete_xcat_node') - self.mox.StubOutWithMock(self.driver, '_reconfigure_networking') - self.mox.StubOutWithMock(instance.ZVMInstance, 'power_on') - - farg = mox.IgnoreArg() - self.driver.get_host_ip_addr().AndReturn('10.1.1.10') - self.driver._networkop.clean_mac_switch_host(farg) - self.driver._pathutils.clean_temp_folder(farg) - self.driver._copy_instance(farg).AndReturn(self._fake_inst) - instance.ZVMInstance.copy_xcat_node(farg) - instance.ZVMInstance.update_node_def(farg, farg) - self.driver._preset_instance_network('os000001', farg) - instance.ZVMInstance.create_userid(farg, farg, farg) - self.driver._add_nic_to_table('os000001', farg) - self.driver._deploy_root_and_ephemeral(farg, farg).AndRaise( - exception.ZVMXCATDeployNodeFailed({'node': 'fn', 'msg': 'e'})) - self.driver._zvm_images.delete_image_from_xcat(farg) - instance.ZVMInstance.delete_userid('fakehcp', farg) - instance.ZVMInstance.delete_xcat_node() - instance.ZVMInstance.copy_xcat_node(farg) - instance.ZVMInstance.delete_xcat_node() - self.driver._reconfigure_networking(farg, network_info, - self._fake_inst, - userid='os000001') - instance.ZVMInstance.power_on() - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMXCATDeployNodeFailed, - self.driver.finish_migration, self.context, migration, - self._fake_inst, disk_info, network_info, self.fake_imgmeta_obj(), - None, block_device_info=self._fake_bdi()) - self.mox.VerifyAll() - - def test_finish_migration_diff_mn(self): - self.flags(zvm_xcat_server="10.10.10.11") - network_info = self._fake_network_info() - disk_info = { - 'disk_type': 'FBA', - 'disk_source_mn': '10.10.10.10', - 'disk_source_image': 'root@10.1.1.10:/fakepath/fa-ke-ima-ge.tgz', - 'disk_image_name': 'fa-ke-ima-ge', - 'disk_owner': 'os000001', - 'disk_eph_size_old': 0, - 'disk_eph_size_new': 0, - 'eph_disk_info': [], - 'shared_image_repo': False} - migration = {'source_node': 'FAKENODE1', - 'dest_node': 'FAKENODE2'} - disk_info = jsonutils.dumps(disk_info) - - self.stubs.Set(self.driver._image_api, 'get', self.fake_image_get) - self.mox.StubOutWithMock(self.driver, 'get_host_ip_addr') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_xcat_node') - self.mox.StubOutWithMock(instance.ZVMInstance, 'update_node_def') - self.mox.StubOutWithMock(self.driver._zvm_images, 'put_image_to_xcat') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'clean_up_snapshot_time_path') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(instance.ZVMInstance, 'create_userid') - self.mox.StubOutWithMock(self.driver, '_add_nic_to_table') - self.mox.StubOutWithMock(self.driver, '_deploy_root_and_ephemeral') - self.mox.StubOutWithMock(self.driver, '_wait_and_get_nic_direct') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'delete_image_from_xcat') - self.mox.StubOutWithMock(zvmutils, 'punch_iucv_authorized_file') - self.mox.StubOutWithMock(instance.ZVMInstance, 'power_on') - self.mox.StubOutWithMock(self.driver, '_attach_volume_to_instance') - - farg = mox.IgnoreArg() - self.driver.get_host_ip_addr().AndReturn('10.1.1.10') - instance.ZVMInstance.create_xcat_node(farg) - instance.ZVMInstance.update_node_def(farg, farg) - self.driver._zvm_images.put_image_to_xcat(farg, farg) - self.driver._zvm_images.clean_up_snapshot_time_path(farg) - self.driver._preset_instance_network('os000001', farg) - instance.ZVMInstance.create_userid(farg, farg, farg) - self.driver._add_nic_to_table('os000001', farg) - self.driver._deploy_root_and_ephemeral(farg, farg) - self.driver._wait_and_get_nic_direct('os000001', self._fake_inst) - self.driver._zvm_images.delete_image_from_xcat(farg) - zvmutils.punch_iucv_authorized_file('os000001', 'os000001', 'fakehcp') - instance.ZVMInstance.power_on() - self.driver._attach_volume_to_instance(farg, self._fake_inst, []) - self.mox.ReplayAll() - - self.driver.finish_migration(self.context, migration, self._fake_inst, - disk_info, network_info, - self.fake_imgmeta_obj(), None, - block_device_info=self._fake_bdi()) - self.mox.VerifyAll() - - def test_confirm_migration_same_mn(self): - self.flags(zvm_xcat_server="10.10.10.10") - inst = instance.CopiedInstance(self._fake_inst) - self.stubs.Set(self.driver, '_instance_exists', self._fake_fun(True)) - self.mox.StubOutWithMock(self.driver, 'destroy') - self.driver.destroy(self.context, mox.IgnoreArg()) - self.mox.ReplayAll() - self.driver.confirm_migration(self.context, [], inst, []) - self.mox.VerifyAll() - - def test_confirm_migration_diff_mn(self): - self.flags(zvm_xcat_server="10.10.10.11") - self.stubs.Set(self.driver, '_instance_exists', self._fake_fun(False)) - self.stubs.Set(self.driver._zvm_images, 'delete_image_from_xcat', - self._fake_fun()) - self.stubs.Set(self.driver._zvm_images, - 'cleanup_image_after_migration', self._fake_fun()) - self.mox.StubOutWithMock(self.driver, 'destroy') - self.driver.destroy(self.context, self._fake_inst) - self.mox.ReplayAll() - self.driver.confirm_migration(self.context, [], self._fake_inst, []) - self.mox.VerifyAll() - - def test_finish_revert_migration_same_mn(self): - self.flags(zvm_xcat_server="10.10.10.10") - self.stubs.Set(instance.ZVMInstance, 'copy_xcat_node', - self._fake_fun()) - self.stubs.Set(zvmutils, 'copy_zvm_table_status', - self._fake_fun()) - self.stubs.Set(instance.ZVMInstance, 'delete_xcat_node', - self._fake_fun()) - - self.mox.StubOutWithMock(self.driver, '_instance_exists') - self.mox.StubOutWithMock(self.driver, '_preset_instance_network') - self.mox.StubOutWithMock(self.driver, '_add_nic_to_table') - self.mox.StubOutWithMock(self.driver, '_wait_and_get_nic_direct') - self.mox.StubOutWithMock(self.driver, '_attach_volume_to_instance') - self.mox.StubOutWithMock(self.driver, 'power_on') - - self.driver._instance_exists('rszos000001').AndReturn(True) - self.driver._preset_instance_network('os000001', - self._fake_network_info()).AndReturn(None) - self.driver._add_nic_to_table('os000001', - self._fake_network_info()).AndReturn(None) - self.driver._wait_and_get_nic_direct('os000001', self._fake_inst) - self.driver._attach_volume_to_instance({}, mox.IgnoreArg(), - []).AndReturn(None) - self.driver.power_on({}, mox.IgnoreArg(), []).AndReturn(None) - self.mox.ReplayAll() - - self.driver.finish_revert_migration({}, self._fake_inst, - self._fake_network_info(), None, True) - self.mox.VerifyAll() - - def test_finish_revert_migration_diff_mn(self): - self.flags(zvm_xcat_server="10.10.10.11") - - self.mox.StubOutWithMock(self.driver, '_instance_exists') - self.mox.StubOutWithMock(self.driver._zvm_images, - 'cleanup_image_after_migration') - self.mox.StubOutWithMock(self.driver, '_attach_volume_to_instance') - - self.driver._instance_exists('rszos000001').AndReturn(False) - self.driver._zvm_images.cleanup_image_after_migration( - 'os000001').AndReturn(None) - self.driver._attach_volume_to_instance({}, mox.IgnoreArg(), - []).AndReturn(None) - self.mox.ReplayAll() - - self.driver.finish_revert_migration({}, self._fake_inst, - self._fake_network_info(), None, False) - -# def test_get_host_uptime(self): -# ipl_time = 'IPL at 03/13/14 21:43:12 EDT' -# self.assertEqual(self.driver.get_host_uptime(), ipl_time) - - def _fake_console_rinv_info(self): - fake_console_rinv_info = ["fakenode: 00: zIPL boot menu\n"] - return self._generate_xcat_resp(fake_console_rinv_info) - - # Because of Bug 534, ignore this test now - def _test_get_console_output(self): - self._set_fake_xcat_responses([self._fake_console_rinv_info()]) - console_log = self.driver.get_console_output(self.context, - self.instance).split('\n')[0] - self.mox.VerifyAll() - self.assertEqual('fakenode: 00: zIPL boot menu', console_log) - - def test_create_config_drive_non_iso9660(self): - self.flags(config_drive_format='vfat') - linuxdist = dist.ListDistManager().get_linux_dist('rhel6') - self.assertRaises(exception.ZVMConfigDriveError, - self.driver._create_config_drive, self.context, - '', self.instance, '', '', '', - '', linuxdist) - - def test_get_hcp_info(self): - hcp_info = self.driver._get_hcp_info() - self.assertEqual('fakehcp.fake.com', hcp_info['hostname']) - self.assertEqual('fakehcp', hcp_info['nodename']) - self.assertEqual('fakehcp', hcp_info['userid']) - - def test_get_hcp_info_first_call(self): - self.driver._host_stats = [] - - self.mox.StubOutWithMock(zvmutils, 'get_userid') - zvmutils.get_userid('fakehcp').AndReturn('fakehcp') - self.mox.ReplayAll() - - hcp_info = self.driver._get_hcp_info('fakehcp.fake.com') - self.mox.VerifyAll() - self.assertEqual('fakehcp.fake.com', hcp_info['hostname']) - self.assertEqual('fakehcp', hcp_info['nodename']) - self.assertEqual('fakehcp', hcp_info['userid']) - - def test_get_hcp_info_incorrect_init(self): - self.driver._host_stats = [] - fake_hcpinfo = {'hostname': 'fakehcp.fake.com', - 'nodename': 'fakehcp', - 'userid': 'fakehcp'} - - self.mox.StubOutWithMock(self.driver, 'update_host_status') - self.driver.update_host_status().AndReturn([{'zhcp': fake_hcpinfo}]) - self.mox.ReplayAll() - - hcp_info = self.driver._get_hcp_info() - self.mox.VerifyAll() - self.assertEqual('fakehcp.fake.com', hcp_info['hostname']) - self.assertEqual('fakehcp', hcp_info['nodename']) - self.assertEqual('fakehcp', hcp_info['userid']) - - def test_detach_volume_from_instance(self): - bdm = [{'connection_info': 'fake', 'mount_device': None}] - self.mox.StubOutWithMock(self.driver, '_instance_exists') - self.mox.StubOutWithMock(instance.ZVMInstance, 'is_reachable') - self.mox.StubOutWithMock(instance.ZVMInstance, 'detach_volume') - self.driver._instance_exists('os000001').AndReturn(True) - instance.ZVMInstance.is_reachable().AndReturn(True) - instance.ZVMInstance.detach_volume(self.driver._volumeop, 'fake', - mox.IgnoreArg(), None, True, rollback=False) - self.mox.ReplayAll() - - self.driver._detach_volume_from_instance(self.instance, bdm) - self.mox.VerifyAll() - - def test_is_shared_image_repo_not_exist(self): - self.mox.StubOutWithMock(self.driver, '_get_xcat_image_file_path') - self.driver._get_xcat_image_file_path('img-uuid').AndRaise( - exception.ZVMImageError(msg='err')) - self.mox.ReplayAll() - - self.assertFalse(self.driver._is_shared_image_repo('img-uuid')) - self.mox.VerifyAll() - - def test_get_available_nodes(self): - nodes = self.driver.get_available_nodes() - self.assertEqual(nodes[0], 'fakenode') - - def test_get_xcat_version(self): - res = ["Version 2.8.3.5 (built Mon Apr 27 10:50:11 EDT 2015)"] - self._set_fake_xcat_responses([self._gen_resp(data=res)]) - version = self.driver._get_xcat_version() - self.assertEqual(version, '2.8.3.5') - - def test_has_min_version(self): - self.driver._xcat_version = '1.2.3.4' - self.assertFalse(self.driver.has_min_version((1, 3, 3, 4))) - self.assertTrue(self.driver.has_min_version((1, 1, 3, 5))) - self.assertTrue(self.driver.has_min_version(None)) - - def test_has_version(self): - xcat_ver = (1, 2, 3, 4) - self.driver._xcat_version = '1.2.3.4' - self.assertTrue(self.driver.has_version(xcat_ver)) - - for xcat_ver_ in [(1, 1, 3, 4), (1, 3, 3, 2)]: - self.assertFalse(self.driver.has_version(xcat_ver_)) - - self.assertTrue(self.driver.has_version(None)) - - @mock.patch('nova.compute.utils.default_device_names_for_instance') - def test_default_device_names_for_instance(self, mock_dflt_names): - class Mock_Bdm(object): - def __init__(self, device_name): - self.device_name = device_name - - def save(self): - pass - - mock_dflt_names.return_value = None - fake_bd_list = [Mock_Bdm('/dev/da'), Mock_Bdm('/dev/db')] - expected = [Mock_Bdm('/dev/vda'), Mock_Bdm('/dev/vdb')] - - self.driver.default_device_names_for_instance(mox.IgnoreArg(), - '/dev/dasda', - fake_bd_list) - self.assertEqual(expected[0].device_name, fake_bd_list[0].device_name) - self.assertEqual(expected[1].device_name, fake_bd_list[1].device_name) - - @mock.patch('nova.compute.utils.get_device_name_for_instance') - def test_get_device_name_for_instance(self, mock_get_dev_name): - mock_get_dev_name.return_value = '/dev/da' - fake_bdo = {'device_name': None} - expected = '/dev/vda' - - device_name = self.driver.get_device_name_for_instance( - mox.IgnoreArg(), mox.IgnoreArg(), fake_bdo) - self.assertEqual(expected, device_name) - - -class ZVMInstanceTestCases(ZVMTestCase): - """Test cases for zvm.instance.""" - - _fake_inst_info_list_cpumem = [ - "os000001: Uptime: 4 days 20 hr 00 min", - "os000001: CPU Used Time: 330528353", - "os000001: Total Memory: 128M", - "os000001: Max Memory: 2G", - "os000001: ", - "os000001: Processors: ", - "os000001: CPU 03 ID FF00EBBE20978000 CP CPUAFF ON", - "os000001: CPU 00 ID FF00EBBE20978000 (BASE) CP CPUAFF ON", - "os000001: CPU 01 ID FF00EBBE20978000 CP CPUAFF ON", - "os000001: CPU 02 ID FF00EBBE20978000 CP CPUAFF ON", - "os000001: ", - ] - - _fake_inst_info_list_cpumempower = [ - "os000001: Power state: on", - "os000001: Total Memory: 128M", - "os000001: Guest CPUs: 4", - "os000001: CPU Used Time: 3305.3 sec", - ] - - def setUp(self): - super(ZVMInstanceTestCases, self).setUp() - self.flags(zvm_user_profile='fakeprof') - self.instance['ephemeral_gb'] = 0 - self.drv = mock.Mock() - self.drv._xcat_version = '100.100.100.100' - self._instance = instance.ZVMInstance(self.drv, self.instance) - - def test_create_xcat_node(self): - info = ["1 object definitions have been created or modified."] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self._instance.create_xcat_node('fakehcp') - self.mox.VerifyAll() - - def test_create_xcat_node_failed(self): - resp = {'data': [{'errorcode': ['1'], - 'error': ["One or more errors occured\n"]}]} - self._set_fake_xcat_responses([resp]) - self.assertRaises(exception.ZVMXCATCreateNodeFailed, - self._instance.create_xcat_node, 'fakehcp') - self.mox.VerifyAll() - - def test_add_mdisk_eckd(self): - info = ["os000001: Adding a disk to LINUX171... Done\n" - "os000001: Active disk configuration... Done\n"] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self._instance.add_mdisk('fakedp', '0101', '1g') - - def test_add_mdisk_fba_with_fmt(self): - self.flags(zvm_diskpool_type='FBA') - info = ["os000001: Adding a disk to LINUX171... Done\n" - "os000001: Active disk configuration... Done\n"] - punch_body = ["--add9336 fakedp 0101 1g MR '' '' '' ext3"] - self._set_fake_xcat_resp([ - ("PUT", None, punch_body, self._gen_resp(info=info)) - ]) - self._instance.add_mdisk('fakedp', '0101', '1g', 'ext3') - self.mox.VerifyAll() - - def test_set_ipl(self): - info = ["os000001: Adding IPL statement to OS000001's " - "directory entry... Done\n"] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self._instance._set_ipl('0100') - - def test_create_userid(self): - """Create userid.""" - image_meta = {'name': 'fake#@%4test', - 'id': '00-11-22-33'} - info = ['os000001: Defining OS000001 in directory... Done\n' - 'os000001: Granting VSwitch for OS000001... Done\n'] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self.stubs.Set(self._instance, '_set_ipl', lambda *args: None) - self.stubs.Set(self._instance, 'add_mdisk', lambda *args: None) - self._instance.create_userid({}, image_meta, {}) - - def test_create_userid_different_image_dict(self): - """Test Create userid with image dictionary that is different - from spawn - """ - image_meta = {'container_format': 'bare', - 'disk_format': 'raw', - 'min_disk': 0, - 'min_ram': 0, - 'properties': {'os_version': 'fake', - 'architecture': 'fake', - 'provisioning_method': 'fake', - 'root_disk_units': '3'}} - info = ['os000001: Defining OS000001 in directory... Done\n' - 'os000001: Granting VSwitch for OS000001... Done\n'] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self.stubs.Set(self._instance, '_set_ipl', lambda *args: None) - self.stubs.Set(self._instance, 'add_mdisk', lambda *args: None) - self._instance.create_userid({}, image_meta, {}) - - def test_create_userid_has_ephemeral(self): - """Create userid with epheral disk added.""" - image_meta = {'name': 'fake', - 'id': '00-11-22-33', - 'properties': {'os_version': 'fake', - 'architecture': 'fake', - 'provisioning_method': 'fake', - 'root_disk_units': '3338:CYL'}} - self.flags(zvm_diskpool_type='ECKD') - self._instance._instance['ephemeral_gb'] = 20 - self._instance._instance['root_gb'] = 0 - cu_info = ['os000001: Defining OS000001 in directory... Done\n' - 'os000001: Granting VSwitch for OS000001... Done\n'] - am_info = ["os000001: Adding a disk to OS00001... Done\n" - "os000001: Active disk configuration... Done\n"] - self._set_fake_xcat_responses([self._generate_xcat_resp(cu_info), - self._generate_xcat_resp(am_info), - self._generate_xcat_resp(am_info)]) - self.stubs.Set(self._instance, '_set_ipl', lambda *args: None) - self._instance.create_userid({}, image_meta, {}) - self.mox.VerifyAll() - - def test_create_userid_with_eph_opts(self): - """Create userid with '--ephemeral' options.""" - self._instance._instance['root_gb'] = 0 - self._instance._instance['ephemeral_gb'] = 20 - fake_bdi = {'ephemerals': [ - {'device_name': '/dev/sdb', - 'device_type': None, - 'disk_bus': None, - 'guest_format': u'ext4', - 'size': 2}, - {'device_name': '/dev/sdc', - 'device_type': None, - 'disk_bus': None, - 'guest_format': u'ext3', - 'size': 1}]} - image_meta = {'name': 'fake', - 'id': '00-11-22-33', - 'properties': {'os_version': 'fake', - 'architecture': 'fake', - 'provisioning_method': 'fake', - 'root_disk_units': '3338:CYL'}} - self.flags(zvm_diskpool_type='ECKD') - - self.mox.StubOutWithMock(zvmutils, 'xcat_request') - self.mox.StubOutWithMock(self._instance, 'add_mdisk') - self.mox.StubOutWithMock(self._instance, '_set_ipl') - - zvmutils.xcat_request('POST', mox.IgnoreArg(), mox.IgnoreArg()) - self._instance.add_mdisk('fakedp', '0100', '3338') - self._instance.add_mdisk('fakedp', '0102', '2g', 'ext4') - self._instance.add_mdisk('fakedp', '0103', '1g', 'ext3') - self.mox.ReplayAll() - - self._instance.create_userid(fake_bdi, image_meta, {}) - self.mox.VerifyAll() - - def test_create_userid_with_eph_opts_resize(self): - """Create userid with '--ephemeral' options.""" - self._instance._instance['ephemeral_gb'] = 20 - self._instance._instance['root_gb'] = 5 - fake_bdi = {'ephemerals': [ - {'device_name': '/dev/sdb', - 'device_type': None, - 'disk_bus': None, - 'guest_format': u'ext4', - 'size': '200000', - 'size_in_units': True}, - {'device_name': '/dev/sdc', - 'device_type': None, - 'disk_bus': None, - 'guest_format': u'ext3', - 'size': '100000', - 'size_in_units': True}]} - image_meta = {'name': 'fake', - 'id': '00-11-22-33', - 'properties': {'os_version': 'fake', - 'architecture': 'fake', - 'provisioning_method': 'fake', - 'root_disk_units': '3:CYL'}} - self.mox.StubOutWithMock(zvmutils, 'xcat_request') - self.mox.StubOutWithMock(self._instance, 'add_mdisk') - self.mox.StubOutWithMock(self._instance, '_set_ipl') - - zvmutils.xcat_request('POST', mox.IgnoreArg(), mox.IgnoreArg()) - self._instance.add_mdisk('fakedp', '0100', '5g') - self._instance.add_mdisk('fakedp', '0102', '200000', 'ext4') - self._instance.add_mdisk('fakedp', '0103', '100000', 'ext3') - self.mox.ReplayAll() - - self._instance.create_userid(fake_bdi, image_meta, {}) - self.mox.VerifyAll() - - def test_update_node_info(self): - image_meta = {'name': 'fake#@%4test', - 'id': '00-11-22-33', - 'properties': {'os_version': 'fake', - 'architecture': 'fake', - 'provisioning_method': 'fake'}} - info = ['os000001: update node info ... Done\n'] - self._set_fake_xcat_responses([self._generate_xcat_resp(info)]) - self._instance.update_node_info(image_meta) - - @mock.patch('nova_zvm.virt.zvm.utils.xcat_request') - @mock.patch('nova_zvm.virt.zvm.utils.get_host') - def test_deploy_node(self, get_host, xcat_req): - get_host.return_value = 'fake@fakehost' - xcat_req.return_value = 'fake' - self._instance.deploy_node('fakeimg', '/fake/file', '0100') - - @mock.patch('nova_zvm.virt.zvm.utils.xcat_request') - @mock.patch('nova_zvm.virt.zvm.utils.get_host') - def test_deploy_node_failed(self, get_host, xcat_req): - get_host.return_value = 'fake@fakehost' - xcat_req.side_effect = exception.ZVMXCATDeployNodeFailed(node="fake", - msg='msg') - self.assertRaises(exception.ZVMXCATDeployNodeFailed, - self._instance.deploy_node, 'fakeimg', '/fake/file') - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.unlock_userid') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._delete_userid') - def test_delete_userid_is_locked(self, delete_uid, unlock_uid): - delete_uid.side_effect = [exception.ZVMXCATInternalError( - 'Return Code: 400\nReason Code: 12\n'), - None] - self._instance.delete_userid('fakehcp', {}) - delete_uid.assert_called() - unlock_uid.assert_called_once_with('fakehcp') - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.unlock_devices') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._delete_userid') - def test_delete_userid_device_is_locked(self, delete_uid, unlock_dev): - delete_uid.side_effect = [exception.ZVMXCATInternalError( - 'Return Code: 408\nReason Code: 12\n'), - None] - self._instance.delete_userid('fakehcp', {}) - delete_uid.assert_called() - unlock_dev.assert_called_once_with('fakehcp') - - def test_modify_storage_format(self): - mem = self._instance._modify_storage_format('0') - self.assertEqual(0, mem) - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._get_rinv_info') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.is_reachable') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._check_power_stat') - def test_get_info_cpumem(self, mk_get_ps, mk_is_reach, - mk_get_rinv_info): - mk_get_ps.return_value = power_state.RUNNING - mk_is_reach.return_value = True - mk_get_rinv_info.return_value = self._fake_inst_info_list_cpumem - - with mock.patch.object(self.drv, 'has_min_version') as mock_v: - mock_v.return_value = False - inst_info = self._instance.get_info() - - self.assertEqual(0x01, inst_info.state) - self.assertEqual(131072, inst_info.mem_kb) - self.assertEqual(4, inst_info.num_cpu) - self.assertEqual(330528353, inst_info.cpu_time_ns) - self.assertEqual(1048576, inst_info.max_mem_kb) - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._get_rinv_info') - def test_get_info_cpumempowerstat(self, mk_get_rinv_info): - mk_get_rinv_info.return_value = self._fake_inst_info_list_cpumempower - - with mock.patch.object(self.drv, 'has_min_version') as mock_v: - mock_v.return_value = True - inst_info = self._instance.get_info() - - self.assertEqual(0x01, inst_info.state) - self.assertEqual(131072, inst_info.mem_kb) - self.assertEqual(4, inst_info.num_cpu) - self.assertEqual(3305.3, inst_info.cpu_time_ns) - self.assertEqual(1048576, inst_info.max_mem_kb) - - @mock.patch('nova_zvm.virt.zvm.exception.ZVMXCATInternalError.msg_fmt') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._get_rinv_info') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.is_reachable') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._check_power_stat') - def test_get_info_inv_err(self, mk_get_ps, mk_is_reach, mk_get_rinv_info, - mk_msg_fmt): - mk_get_ps.return_value = power_state.RUNNING - mk_is_reach.return_value = True - mk_get_rinv_info.side_effect = exception.ZVMXCATInternalError - mk_msg_fmt.return_value = "fake msg" - - with mock.patch.object(self.drv, 'has_min_version') as mock_v: - mock_v.return_value = False - self.assertRaises(nova_exception.InstanceNotFound, - self._instance.get_info) - - @mock.patch('nova_zvm.virt.zvm.exception.ZVMInvalidXCATResponseDataError.' - 'msg_fmt') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._get_current_memory') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._get_rinv_info') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.is_reachable') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._check_power_stat') - def test_get_info_invalid_data(self, mk_get_ps, mk_is_reach, - mk_get_rinv_info, mk_get_mem, mk_msg_fmt): - mk_get_ps.return_value = power_state.RUNNING - mk_is_reach.return_value = True - mk_get_rinv_info.return_value = self._fake_inst_info_list_cpumem - mk_get_mem.side_effect = exception.ZVMInvalidXCATResponseDataError - mk_msg_fmt.return_value = "fake msg" - - with mock.patch.object(self.drv, 'has_min_version') as mock_v: - mock_v.return_value = False - inst_info = self._instance.get_info() - - self.assertEqual(0x01, inst_info.state) - self.assertEqual(1048576, inst_info.mem_kb) - self.assertEqual(2, inst_info.num_cpu) - self.assertEqual(0, inst_info.cpu_time_ns) - self.assertEqual(1048576, inst_info.max_mem_kb) - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._get_rinv_info') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.is_reachable') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._check_power_stat') - def test_get_info_down(self, mk_get_ps, mk_is_reach, mk_get_rinv_info): - mk_get_ps.return_value = power_state.SHUTDOWN - mk_is_reach.return_value = False - mk_get_rinv_info.return_value = self._fake_inst_info_list_cpumem - - with mock.patch.object(self.drv, 'has_min_version') as mock_v: - mock_v.return_value = False - inst_info = self._instance.get_info() - - self.assertEqual(power_state.SHUTDOWN, inst_info.state) - self.assertEqual(0, inst_info.mem_kb) - self.assertEqual(2, inst_info.num_cpu) - self.assertEqual(0, inst_info.cpu_time_ns) - self.assertEqual(1048576, inst_info.max_mem_kb) - - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.is_reachable') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance._check_power_stat') - def test_get_info_paused(self, mk_get_ps, mk_is_reach): - mk_get_ps.return_value = power_state.RUNNING - mk_is_reach.return_value = False - - _inst = fake_instance.fake_instance_obj(self.context, name='fake', - power_state=power_state.PAUSED, memory_mb='1024', - vcpus='2') - inst = instance.ZVMInstance(self.drv, _inst) - - with mock.patch.object(self.drv, 'has_min_version') as mock_v: - mock_v.return_value = False - inst_info = inst.get_info() - self.assertEqual(power_state.PAUSED, inst_info.state) - self.assertEqual(1048576, inst_info.mem_kb) - self.assertEqual(2, inst_info.num_cpu) - self.assertEqual(0, inst_info.cpu_time_ns) - self.assertEqual(1048576, inst_info.max_mem_kb) - - @mock.patch('nova_zvm.virt.zvm.utils.xdsh') - @mock.patch('nova_zvm.virt.zvm.instance.ZVMInstance.get_userid') - def test_unlock_devices(self, get_uid, xdsh): - get_uid.return_value = 'fakeuid' - xdshv1 = {'data': [['Locked type: DEVICE\nDevice address: 0100\n' - 'Device locked by: fake\nDevice address: 0101\n' - 'Device locked by: fake']]} - xdsh.side_effect = [xdshv1, None, None] - self._instance.unlock_devices('fakezhcp') - - get_uid.assert_called_with() - xdsh.assert_any_call('fakezhcp', - '/opt/zhcp/bin/smcli Image_Lock_Query_DM -T fakeuid') - xdsh.assert_any_call('fakezhcp', - '/opt/zhcp/bin/smcli Image_Unlock_DM -T fakeuid -v 0100') - xdsh.assert_any_call('fakezhcp', - '/opt/zhcp/bin/smcli Image_Unlock_DM -T fakeuid -v 0101') - - -class ZVMXCATConnectionTestCases(test.TestCase): - """Test cases for xCAT connection.""" - - def setUp(self): - super(ZVMXCATConnectionTestCases, self).setUp() - self.flags(zvm_xcat_server='10.10.10.10', - zvm_xcat_username='fake', - zvm_xcat_password='fake') - - def _set_fake_response(self, response): - self.mox.StubOutWithMock(httplib.HTTPSConnection, 'request') - httplib.HTTPSConnection.request(mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg(), mox.IgnoreArg()) - self.mox.StubOutWithMock(httplib.HTTPSConnection, 'getresponse') - httplib.HTTPSConnection.getresponse().AndReturn(response) - self.mox.ReplayAll() - - def test_get(self): - self._set_fake_response(FakeHTTPResponse(200, 'OK', 'fake')) - conn = zvmutils.XCATConnection() - conn.request("GET", 'fakeurl') - self.mox.VerifyAll() - - def test_get_failed(self): - self._set_fake_response(FakeHTTPResponse(201, 'OK', 'fake')) - conn = zvmutils.XCATConnection() - self.assertRaises(exception.ZVMXCATRequestFailed, conn.request, - "GET", 'fakeurl') - self.mox.VerifyAll() - - def test_post(self): - self._set_fake_response(FakeHTTPResponse(201, 'Created', 'fake')) - conn = zvmutils.XCATConnection() - conn.request("POST", 'fakeurl') - self.mox.VerifyAll() - - def test_post_failed(self): - self._set_fake_response(FakeHTTPResponse(200, 'OK', 'fake')) - conn = zvmutils.XCATConnection() - self.assertRaises(exception.ZVMXCATRequestFailed, conn.request, - "POST", 'fakeurl') - self.mox.VerifyAll() - - def test_invalid_url(self): - self.mox.StubOutWithMock(httplib.HTTPSConnection, 'request') - httplib.HTTPSConnection.request(mox.IgnoreArg(), mox.IgnoreArg(), - mox.IgnoreArg(), mox.IgnoreArg()).AndRaise(socket.gaierror()) - self.mox.ReplayAll() - conn = zvmutils.XCATConnection() - self.assertRaises(exception.ZVMXCATRequestFailed, - conn.request, "GET", 'fakeurl') - - @mock.patch.object(zvmutils.LOG, "warning") - @mock.patch.object(zvmutils.LOG, "info") - @mock.patch.object(zvmutils.XCATConnection, 'request') - def test_xcat_request_503(self, mock_req, mock_info, mock_warn): - res = {'message': jsonutils.dumps({"data": [{'data': 1}]})} - mock_req.return_value = (1, res) - - zvmutils.xcat_request("GET", "/dummy") - self.assertEqual(5, len(mock_info.call_args_list)) - self.assertEqual(1, len(mock_warn.call_args_list)) - - -class ZVMNetworkTestCases(ZVMTestCase): - """Test cases for network operator.""" - - def setUp(self): - super(ZVMNetworkTestCases, self).setUp() - self.networkop = networkop.NetworkOperator() - self.iname = self.instance['name'] - - def test_config_xcat_mac(self): - self._set_fake_xcat_responses([{'data': [{'data': ['mac']}]}]) - self.networkop.config_xcat_mac(self.iname) - - def test_add_xcat_host(self): - self._set_fake_xcat_responses([{'data': [{'data': ['mac']}]}]) - self.networkop.add_xcat_host(self.iname, '11.11.11.11', self.iname) - - def test_makehosts(self): - self._set_fake_xcat_responses([{'data': [{'data': ['mac']}]}]) - self.networkop.makehosts() - - def test_add_instance_ignore_recoverable_issue(self): - data = 'Return Code: 596\n Reason Code: 1186' - self.assertFalse(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 597\n Reason Code: 1185' - self.assertFalse(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 597\n Reason Code: 1186' - self.assertFalse(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 596\n Reason Code: 1185' - self.assertTrue(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 596\n Reason Code: 6313' - self.assertFalse(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 597\n Reason Code: 6312' - self.assertFalse(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 597\n Reason Code: 6313' - self.assertFalse(zvmutils._is_recoverable_issue(data)) - - data = 'Return Code: 596\n Reason Code: 6312' - self.assertTrue(zvmutils._is_recoverable_issue(data)) - - -class ZVMUtilsTestCases(ZVMTestCase): - - def setUp(self): - super(ZVMUtilsTestCases, self).setUp() - - def test_generate_eph_vdev(self): - vdev0 = zvmutils.generate_eph_vdev(0) - vdev1 = zvmutils.generate_eph_vdev(1) - vdev2 = zvmutils.generate_eph_vdev(253) - self.assertEqual(vdev0, '0102') - self.assertEqual(vdev1, '0103') - self.assertEqual(vdev2, '01ff') - self.assertRaises(exception.ZVMDriverError, - zvmutils.generate_eph_vdev, -1) - self.assertRaises(exception.ZVMDriverError, - zvmutils.generate_eph_vdev, 254) - - def test__log_warnings(self): - resp = {'info': [''], - 'data': [], - 'node': ['Warn ...'], - 'error': []} - self.mox.StubOutWithMock(zvmutils.LOG, 'info') - msg = ("Warning from xCAT: %s") - zvmutils.LOG.info(msg, str(resp['node'])) - self.mox.ReplayAll() - - zvmutils._log_warnings(resp) - self.mox.VerifyAll() - - def test_parse_os_version(self): - fake_os = {'rhel': ['rhelx.y', 'redhatx.y', 'red hatx.y'], - 'sles': ['susex.y', 'slesx.y']} - for distro, patterns in fake_os.items(): - for i in patterns: - os, version = zvmutils.parse_os_version(i) - self.assertEqual(os, distro) - self.assertEqual(version, 'x.y') - - def test_parse_os_version_exception(self): - self.assertRaises(exception.ZVMImageError, - zvmutils.parse_os_version, - 'ubuntu') - - def test_xcat_cmd_gettab(self): - fake_resp = {"data": [["/install"]]} - self.mox.StubOutWithMock(zvmutils, 'xcat_request') - zvmutils.xcat_request("GET", mox.IgnoreArg()).AndReturn(fake_resp) - self.mox.ReplayAll() - - outp = zvmutils.xcat_cmd_gettab("site", "key", "installdir", "value") - self.mox.VerifyAll() - self.assertEqual(outp, "/install") - - def test_xcat_cmd_gettab_multi_attr(self): - attr_list = ['name', 'type', 'version'] - res_data = {'data': [['table.name: fake'], - ['table.type: fake'], - ['table.version: fake']]} - - self.mox.StubOutWithMock(zvmutils, 'xcat_request') - zvmutils.xcat_request('GET', mox.IgnoreArg()).AndReturn(res_data) - self.mox.ReplayAll() - - outp = zvmutils.xcat_cmd_gettab_multi_attr('table', 'id', 'fake', - attr_list) - self.mox.VerifyAll() - self.assertEqual(outp['name'], 'fake') - self.assertEqual(outp['type'], 'fake') - self.assertEqual(outp['version'], 'fake') - - def test_generate_network_configration(self): - network = model.Network(bridge=None, subnets=[model.Subnet( - ips=[model.FixedIP(meta={}, version=4, type=u'fixed', - floating_ips=[], address=u'192.168.11.12')], - version=4, meta={'dhcp_server': u'192.168.11.7'}, - dns=[], routes=[], cidr=u'192.168.11.0/24', - gateway=model.IP(address=None, type='gateway'))], meta={}, - id=u'51e1f38b-f717-4a8b-9fb4-acefc2d7224e', - label=u'mgt') - vdev = '1000' - device_num = 0 - os_type = 'rhel' - (cfg_str, cmd_str, dns_str, - route_str) = zvmutils.NetworkUtils().generate_network_configration( - network, vdev, device_num, os_type) - - self.assertEqual(cfg_str, 'DEVICE="eth0"\nBOOTPROTO="static"\n' - 'BROADCAST="192.168.11.255"\nGATEWAY=""\n' - 'IPADDR="192.168.11.12"\nNETMASK="255.255.255.0"\n' - 'NETTYPE="qeth"\nONBOOT="yes"\nPORTNAME="PORT1000"\n' - 'OPTIONS="layer2=1"\nSUBCHANNELS=' - '"0.0.1000,0.0.1001,0.0.1002"\n') - self.assertIsNone(cmd_str) - self.assertEqual(dns_str, '') - self.assertEqual(route_str, '') - - def test_looping_call(self): - fake_func = mock.Mock() - fake_func.__name__ = "fake_func" - fake_func.side_effect = [exception.ZVMRetryException, None] - - zvmutils.looping_call(fake_func, 1, 0, 1, 3, - exception.ZVMRetryException) - # expect called 2 times - fake_func.assert_has_calls([(), ()]) - - @mock.patch.object(zvmutils, "LOG") - def test_expect_invalid_xcat_resp_data_list(self, mock_log): - data = ['abcdef'] - try: - with zvmutils.expect_invalid_xcat_resp_data(data): - raise ValueError - except exception.ZVMInvalidXCATResponseDataError: - pass - - mock_log.error.assert_called_with('Parse %s encounter error', data) - - @mock.patch.object(zvmutils, "LOG") - def test_expect_invalid_xcat_resp_data_dict(self, mock_log): - data = {'data': ['a', 'b'], 'error': 1, 'info': {'a': [1, 3, '5']}} - try: - with zvmutils.expect_invalid_xcat_resp_data(data): - raise ValueError - except exception.ZVMInvalidXCATResponseDataError: - pass - - mock_log.error.assert_called_with('Parse %s encounter error', data) - - def test_convert_to_mb(self): - self.assertEqual(2355.2, zvmutils.convert_to_mb('2.3G')) - self.assertEqual(20, zvmutils.convert_to_mb('20M')) - self.assertEqual(1153433.6, zvmutils.convert_to_mb('1.1T')) - - def test_remove_prefix_of_unicode(self): - self.assertEqual(u'6d4b8bd575284f8b', - zvmutils.remove_prefix_of_unicode(u'\u6d4b\u8bd5\u7528\u4f8b')) - self.assertEqual('uuuuasd81m.qw38927u', - zvmutils.remove_prefix_of_unicode('uuuuasd81m.qw38927u')) - - -class ZVMConfigDriveTestCase(test.NoDBTestCase): - - def setUp(self): - super(ZVMConfigDriveTestCase, self).setUp() - self.flags(config_drive_format='iso9660', - tempdir='/tmp/os') - self.inst_md = FakeInstMeta() - - def test_create_configdrive_tgz(self): - self._file_path = CONF.tempdir - fileutils.ensure_tree(self._file_path) - self._file_name = self._file_path + '/configdrive.tgz' - - try: - with configdrive.ZVMConfigDriveBuilder( - instance_md=self.inst_md) as c: - c.make_drive(self._file_name) - - self.assertTrue(os.path.exists(self._file_name)) - - finally: - fileutils.remove_path_on_error(self._file_path) - - def test_make_tgz(self): - self._file_path = CONF.tempdir - fileutils.ensure_tree(self._file_path) - self._file_name = self._file_path + '/configdrive.tgz' - - self.mox.StubOutWithMock(os, 'getcwd') - os.getcwd().AndRaise(OSError('msg')) - os.getcwd().AndReturn(self._file_path) - os.getcwd().AndReturn(self._file_path) - self.mox.ReplayAll() - - try: - with configdrive.ZVMConfigDriveBuilder( - instance_md=self.inst_md) as c: - c.make_drive(self._file_name) - finally: - fileutils.remove_path_on_error(self._file_path) - - self.mox.VerifyAll() - - -class ZVMVolumeOperatorTestCase(ZVMTestCase): - - def setUp(self): - super(ZVMVolumeOperatorTestCase, self).setUp() - self.volumeop = volumeop.VolumeOperator() - self.fake_inst = self.instance - self.mox.UnsetStubs() - - def test_init(self): - self.assertIsInstance(self.volumeop._svc_driver, volumeop.SVCDriver) - - def test_attach_volume_to_instance_check_args(self): - fake_connection_info = {'info': 'fake_info'} - - farg = mox.IgnoreArg() - self.assertRaises(exception.ZVMDriverError, - self.volumeop.attach_volume_to_instance, None, - fake_connection_info, None, farg, True) - self.assertRaises(exception.ZVMDriverError, - self.volumeop.attach_volume_to_instance, None, - None, self.fake_inst, farg, False) - - def test_detach_volume_from_instance_check_args(self): - fake_connection_info = {'info': 'fake_info'} - - farg = mox.IgnoreArg() - self.assertRaises(exception.ZVMDriverError, - self.volumeop.detach_volume_from_instance, - fake_connection_info, None, farg, True) - self.assertRaises(exception.ZVMDriverError, - self.volumeop.detach_volume_from_instance, - None, self.fake_inst, farg, False) - - def test_attach_volume_to_instance_active(self): - fake_connection_info = {'info': 'fake_info'} - is_active = True - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(self.volumeop._svc_driver, - 'attach_volume_active') - self.volumeop._svc_driver.attach_volume_active( - farg, farg, farg, farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.volumeop.attach_volume_to_instance(farg, fake_connection_info, - self.fake_inst, farg, - is_active) - self.mox.VerifyAll() - - def test_attach_volume_to_instance_inactive(self): - fake_connection_info = {'info': 'fake_info'} - is_active = False - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(self.volumeop._svc_driver, - 'attach_volume_inactive') - self.volumeop._svc_driver.attach_volume_inactive( - farg, farg, farg, farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.volumeop.attach_volume_to_instance(farg, fake_connection_info, - self.fake_inst, farg, - is_active) - self.mox.VerifyAll() - - def test_detach_volume_from_instance_active(self): - fake_connection_info = {'info': 'fake_info'} - is_active = True - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(self.volumeop._svc_driver, - 'detach_volume_active') - self.volumeop._svc_driver.detach_volume_active(farg, farg, farg, - farg).AndReturn(None) - self.mox.ReplayAll() - - self.volumeop.detach_volume_from_instance(fake_connection_info, - self.fake_inst, farg, - is_active) - self.mox.VerifyAll() - - def test_detach_volume_from_instance_inactive(self): - fake_connection_info = {'info': 'fake_info'} - is_active = False - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(self.volumeop._svc_driver, - 'detach_volume_inactive') - self.volumeop._svc_driver.detach_volume_inactive(farg, farg, farg, - farg).AndReturn(None) - self.mox.ReplayAll() - - self.volumeop.detach_volume_from_instance(fake_connection_info, - self.fake_inst, farg, - is_active) - self.mox.VerifyAll() - - def test_get_volume_connector_check_args(self): - self.assertRaises(exception.ZVMDriverError, - self.volumeop.get_volume_connector, None) - - def test_has_persistent_volume_check_args(self): - self.assertRaises(exception.ZVMDriverError, - self.volumeop.has_persistent_volume, None) - - def test_extract_connection_info_check_args(self): - self.assertRaises(exception.ZVMDriverError, - self.volumeop.extract_connection_info, - mox.IgnoreArg(), None) - - def test_get_root_volume_connection_info_check_args(self): - fake_bdm = {'connection_info': 'fake_info', 'mount_device': '/dev/sda'} - fake_root_device = '/dev/sda' - self.assertRaises(exception.ZVMDriverError, - self.volumeop.get_root_volume_connection_info, - [fake_bdm], None) - self.assertRaises(exception.ZVMDriverError, - self.volumeop.get_root_volume_connection_info, - [], fake_root_device) - - def test_get_root_volume_connection_info_get_none(self): - fake_bdm = {'connection_info': 'fake_info', 'mount_device': '/dev/sda'} - fake_root_device = '/dev/sdb' - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(zvmutils, 'is_volume_root') - zvmutils.is_volume_root(farg, farg).AndReturn(False) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMDriverError, - self.volumeop.get_root_volume_connection_info, - [fake_bdm], fake_root_device) - self.mox.VerifyAll() - - def test_get_root_volume_connection_info(self): - fake_bdm1 = {'connection_info': 'fake_info_a', - 'mount_device': '/dev/sda'} - fake_bdm2 = {'connection_info': 'fake_info_b', - 'mount_device': '/dev/sdb'} - fake_root_device = '/dev/sdb' - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(zvmutils, 'is_volume_root') - zvmutils.is_volume_root(farg, farg).AndReturn(False) - zvmutils.is_volume_root(farg, farg).AndReturn(True) - self.mox.ReplayAll() - - connection_info = self.volumeop.get_root_volume_connection_info( - [fake_bdm1, fake_bdm2], fake_root_device) - self.assertEqual('fake_info_b', connection_info) - self.mox.VerifyAll() - - def test_volume_boot_init_check_args(self): - self.assertRaises(exception.ZVMDriverError, - self.volumeop.volume_boot_init, None, '1fa0') - self.assertRaises(exception.ZVMDriverError, - self.volumeop.volume_boot_init, - {'name': 'fake'}, None) - - def test_volume_boot_cleanup_check_args(self): - self.assertRaises(exception.ZVMDriverError, - self.volumeop.volume_boot_cleanup, None, '1fa0') - self.assertRaises(exception.ZVMDriverError, - self.volumeop.volume_boot_cleanup, - {'name': 'fake'}, None) - - -class FCPTestCase(ZVMTestCase): - - def setUp(self): - super(FCPTestCase, self).setUp() - fcp_info = ['opnstk1: FCP device number: B83D', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181'] - self.fcp = volumeop.SVCDriver.FCP(fcp_info) - self.mox.UnsetStubs() - - def test_init(self): - self.assertEqual('b83d', self.fcp.get_dev_no()) - self.assertIsNone(self.fcp.get_npiv_port()) - self.assertEqual('5A', self.fcp.get_chpid()) - self.assertEqual('20076d8500005181', self.fcp.get_physical_port()) - self.assertTrue(self.fcp.is_valid()) - - def test_get_wwpn_from_line(self): - info_line = 'opnstk1: NPIV world wide port number: NONE' - self.assertIsNone(self.fcp._get_wwpn_from_line(info_line)) - info_line = 'opnstk1: NPIV world wide port number: 20076D8500005181' - self.assertEqual('20076d8500005181', - self.fcp._get_wwpn_from_line(info_line)) - info_line = 'opnstk1: Physical world wide port number:' - self.assertIsNone(self.fcp._get_wwpn_from_line(info_line)) - info_line = ' '.join(['opnstk1: Physical world wide port number:', - '20076D8500005182']) - self.assertEqual('20076d8500005182', - self.fcp._get_wwpn_from_line(info_line)) - - def test_get_dev_number_from_line(self): - info_line = 'opnstk1: FCP device number: B83D' - self.assertEqual('b83d', self.fcp._get_dev_number_from_line(info_line)) - info_line = 'opnstk1: FCP device number: ' - self.assertIsNone(self.fcp._get_dev_number_from_line(info_line)) - - def test_get_chpid_from_line(self): - info_line = 'opnstk1: Channel path ID: 5A' - self.assertEqual('5A', self.fcp._get_chpid_from_line(info_line)) - info_line = 'opnstk1: Channel path ID: ' - self.assertIsNone(self.fcp._get_chpid_from_line(info_line)) - - def test_validate_device(self): - dev_line = 'opnstk1: FCP device number: B83D' - status_line = 'opnstk1: Status: Free' - npiv_line = 'opnstk1: NPIV world wide port number: NONE' - chpid_line = 'opnstk1: Channel path ID: 5A' - physical_port_line = ' '.join(['opnstk1:', - 'Physical world wide port number:', - '20076D8500005181']) - - fcp_info = ['opnstk1: FCP device number: ', status_line, npiv_line, - chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = ['opnstk1: FCP device number: help', status_line, npiv_line, - chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, '', npiv_line, chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertTrue(fcp.is_valid()) - - fcp_info = [dev_line, status_line, - 'opnstk1: NPIV world wide port number: ', - chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertTrue(fcp.is_valid()) - - fcp_info = [dev_line, status_line, - 'opnstk1: NPIV world wide port number: 20076D850000', - chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, - 'opnstk1: NPIV world wide port number: 20076D850000help', - chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, - 'opnstk1: NPIV world wide port number: 20076D8500005182', - chpid_line, physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertTrue(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, - 'opnstk1: Channel path ID: ', physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, - 'opnstk1: Channel path ID: help', physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, - 'opnstk1: Channel path ID: 5', physical_port_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, npiv_line, - 'opnstk1: Physical world wide port number: '] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, npiv_line, - 'opnstk1: Physical world wide port number: 20076D850000'] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, npiv_line, - 'opnstk1: Physical world wide port number: ' - '20076D850000help'] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - fcp_info = [dev_line, status_line, npiv_line, npiv_line] - fcp = volumeop.SVCDriver.FCP(fcp_info) - self.assertFalse(fcp.is_valid()) - - -class SVCDriverTestCase(ZVMTestCase): - - def setUp(self): - super(SVCDriverTestCase, self).setUp() - self.driver = volumeop.SVCDriver() - self.flags(zvm_multiple_fcp=False) - self.fake_inst = self.instance - self.inst_name = self.fake_inst['name'] - self.mox.UnsetStubs() - - def test_init(self): - self.assertIsInstance(self.driver._xcat_url, zvmutils.XCATUrl) - self.assertIsInstance(self.driver._path_utils, zvmutils.PathUtils) - - def test_init_host_check_args(self): - self.assertRaises(exception.ZVMDriverError, - self.driver.init_host, None) - - def test_init_host_no_fcp(self): - fake_host_stats = [{'zhcp': {'nodename': 'fakename'}}] - self.flags(zvm_fcp_list=None) - - self.mox.StubOutWithMock(self.driver, '_expand_fcp_list') - self.driver._expand_fcp_list(mox.IgnoreArg()).AndReturn(set()) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.init_host, fake_host_stats) - self.mox.VerifyAll() - - def test_init_host(self): - fake_host_stats = [{'zhcp': {'nodename': 'fakename'}}] - self.mox.StubOutWithMock(self.driver, '_expand_fcp_list') - self.mox.StubOutWithMock(self.driver, '_attach_device') - self.mox.StubOutWithMock(self.driver, '_online_device') - self.mox.StubOutWithMock(self.driver, '_init_fcp_pool') - self.mox.StubOutWithMock(self.driver, '_init_instance_fcp_map') - - farg = mox.IgnoreArg() - self.driver._expand_fcp_list(farg).AndReturn(['1FB0']) - self.driver._attach_device(farg, farg).AndReturn(None) - self.driver._online_device(farg, farg).AndReturn(None) - self.driver._init_fcp_pool(farg).AndReturn(None) - self.driver._init_instance_fcp_map(farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.init_host(fake_host_stats) - self.mox.VerifyAll() - - def test_init_fcp_pool(self): - fake_fcp_list = '0001-0003' - fcp_info = ['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181', - 'opnstk1: FCP device number: 0002', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: NONE', - 'opnstk1: FCP device number: 0003', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181', - 'opnstk1: FCP device number: 0004', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181', - 'opnstk1: FCP device number: 0005', - 'opnstk1: Status: Active', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181'] - - self.mox.StubOutWithMock(self.driver, '_get_all_fcp_info') - self.driver._get_all_fcp_info().AndReturn(fcp_info) - self.mox.ReplayAll() - - self.driver._init_fcp_pool(fake_fcp_list) - self.assertEqual(len(self.driver._fcp_pool), 2) - - fcp1 = self.driver._fcp_pool.get('0001') - fcp2 = self.driver._fcp_pool.get('0003') - self.assertTrue(fcp1.is_valid()) - self.assertTrue(fcp2.is_valid()) - self.mox.VerifyAll() - self.driver._fcp_pool = set() - self.driver._instance_fcp_map = {} - - def test_get_all_fcp_info(self): - free_info = ['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181'] - active_info = ['opnstk1: FCP device number: 0002', - 'opnstk1: Status: Active', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181'] - farg = mox.IgnoreArg() - - self.mox.StubOutWithMock(self.driver, '_list_fcp_details') - self.driver._list_fcp_details(farg).AndReturn(free_info) - self.driver._list_fcp_details(farg).AndReturn(active_info) - self.mox.ReplayAll() - - fcp_info = self.driver._get_all_fcp_info() - expected_info = free_info - expected_info.extend(active_info) - self.assertEqual(expected_info, fcp_info) - self.mox.VerifyAll() - - def test_init_instance_fcp_map_unconfigured_fcp(self): - fcp_list = set(['1fa1', '1fa2', '1fa3']) - connection_info1 = {'data': {'zvm_fcp': ['1fa4']}} - connection_info2 = {'data': {'zvm_fcp': ['1fa5', '1fa6']}} - inst_bdms1 = {'instance': {'name': 'inst1'}, - 'instance_bdms': connection_info1} - inst_bdms2 = {'instance': {'name': 'inst2'}, - 'instance_bdms': connection_info2} - farg = mox.IgnoreArg() - - self.mox.StubOutWithMock(self.driver, '_expand_fcp_list') - self.mox.StubOutWithMock(self.driver, '_get_host_volume_bdms') - self.mox.StubOutWithMock(self.driver, '_build_connection_info') - self.driver._expand_fcp_list(farg).AndReturn(fcp_list) - self.driver._get_host_volume_bdms().AndReturn([inst_bdms1, inst_bdms2]) - self.driver._build_connection_info(farg).AndReturn(connection_info1) - self.driver._build_connection_info(farg).AndReturn(connection_info2) - self.mox.ReplayAll() - - self.driver._init_instance_fcp_map(farg) - self.assertEqual({}, self.driver._instance_fcp_map) - self.mox.VerifyAll() - - def test_init_instance_fcp_map(self): - fcp1 = self.driver.FCP([]) - fcp2 = self.driver.FCP([]) - fcp3 = self.driver.FCP([]) - fcp4 = self.driver.FCP([]) - self.driver._fcp_pool = {'1fa1': fcp1, '1fa2': fcp2, - '1fa3': fcp3, '1fa4': fcp4} - fcp_list = set(['1fa1', '1fa2', '1fa3', '1fa4']) - connection_info1 = {'data': {'zvm_fcp': ['1fa1']}} - connection_info2 = {'data': {'zvm_fcp': ['1fa2', '1fa3']}} - inst_bdms1 = {'instance': {'name': 'inst1'}, - 'instance_bdms': connection_info1} - inst_bdms2 = {'instance': {'name': 'inst2'}, - 'instance_bdms': connection_info2} - farg = mox.IgnoreArg() - - self.mox.StubOutWithMock(self.driver, '_expand_fcp_list') - self.mox.StubOutWithMock(self.driver, '_get_host_volume_bdms') - self.mox.StubOutWithMock(self.driver, '_build_connection_info') - self.driver._expand_fcp_list(farg).AndReturn(fcp_list) - self.driver._get_host_volume_bdms().AndReturn([inst_bdms1, inst_bdms2]) - self.driver._build_connection_info(farg).AndReturn(connection_info1) - self.driver._build_connection_info(farg).AndReturn(connection_info2) - self.mox.ReplayAll() - - self.driver._init_instance_fcp_map(farg) - expected_map = {'inst1': {'fcp_list': ['1fa1'], 'count': 1}, - 'inst2': {'fcp_list': ['1fa2', '1fa3'], 'count': 1}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.mox.VerifyAll() - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_different_set(self): - self.driver._instance_fcp_map = {'inst1': - {'fcp_list': ['0001', '0002'], 'count': 1}} - fcp_list = ['0002', '0003'] - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', fcp_list, self.driver._INCREASE) - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_reserve_in_use(self): - self.driver._instance_fcp_map = {'inst1': - {'fcp_list': ['0001', '0002'], 'count': 1}} - fcp_list = ['0001', '0002'] - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', fcp_list, self.driver._RESERVE) - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_reserve(self): - fcp1 = self.driver.FCP([]) - fcp2 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1, '0002': fcp2} - fcp_list = ['0001', '0002'] - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._RESERVE) - expected_map = {'inst1': {'fcp_list': ['0001', '0002'], 'count': 0}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.assertTrue(fcp1.is_reserved()) - self.assertFalse(fcp1.is_in_use()) - self.assertTrue(fcp2.is_reserved()) - self.assertFalse(fcp2.is_in_use()) - - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_increase(self): - fcp1 = self.driver.FCP([]) - fcp2 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1, '0002': fcp2} - fcp_list = ['0001', '0002'] - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._INCREASE) - expected_map = {'inst1': {'fcp_list': ['0001', '0002'], 'count': 1}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.assertTrue(fcp1.is_in_use()) - self.assertTrue(fcp2.is_in_use()) - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._INCREASE) - expected_map = {'inst1': {'fcp_list': ['0001', '0002'], 'count': 2}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - - self.driver._instance_fcp_map = {} - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._RESERVE) - expected_map = {'inst1': {'fcp_list': ['0001', '0002'], 'count': 0}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._INCREASE) - expected_map = {'inst1': {'fcp_list': ['0001', '0002'], 'count': 1}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_decrease_not_exist(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', fcp_list, self.driver._DECREASE) - - self.driver._fcp_pool = {} - - def test_update_instance_fcp_map_decrease_0(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - self.driver._instance_fcp_map = {'inst1': {'fcp_list': ['0001'], - 'count': 0}} - - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', fcp_list, self.driver._DECREASE) - - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_decrease(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - self.driver._instance_fcp_map = {'inst1': {'fcp_list': ['0001'], - 'count': 2}} - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._DECREASE) - fcp1.set_in_use() - expected_map = {'inst1': {'fcp_list': ['0001'], 'count': 1}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.assertTrue(fcp1.is_in_use()) - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._DECREASE) - expected_map = {'inst1': {'fcp_list': ['0001'], 'count': 0}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.assertFalse(fcp1.is_in_use()) - - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_remove_not_exist(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', fcp_list, self.driver._REMOVE) - - self.driver._fcp_pool = {} - - def test_update_instance_fcp_map_remove_in_use(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - self.driver._instance_fcp_map = {'inst1': {'fcp_list': ['0001'], - 'count': 1}} - - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', fcp_list, self.driver._REMOVE) - - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_update_instance_fcp_map_remove(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - self.driver._instance_fcp_map = {'inst1': {'fcp_list': ['0001'], - 'count': 0}} - fcp1.set_in_use() - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._REMOVE) - self.assertEqual({}, self.driver._instance_fcp_map) - self.assertFalse(fcp1.is_in_use()) - self.assertFalse(fcp1.is_reserved()) - - self.driver._fcp_pool = {} - - def test_update_instance_fcp_map_force_remove(self): - fcp1 = self.driver.FCP([]) - self.driver._fcp_pool = {'0001': fcp1} - fcp_list = ['0001'] - self.driver._instance_fcp_map = {'inst1': {'fcp_list': ['0001'], - 'count': 2}} - fcp1.set_in_use() - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._FORCE_REMOVE) - self.assertEqual({}, self.driver._instance_fcp_map) - self.assertFalse(fcp1.is_in_use()) - self.assertFalse(fcp1.is_reserved()) - - self.driver._instance_fcp_map = {'inst1': {'fcp_list': ['0001'], - 'count': 0}} - fcp1.set_in_use() - - self.driver._update_instance_fcp_map('inst1', fcp_list, - self.driver._FORCE_REMOVE) - self.assertEqual({}, self.driver._instance_fcp_map) - self.assertFalse(fcp1.is_in_use()) - self.assertFalse(fcp1.is_reserved()) - - self.driver._fcp_pool = {} - - def test_update_instance_fcp_map_unknown_action(self): - self.assertRaises(exception.ZVMVolumeError, - self.driver._update_instance_fcp_map, - 'inst1', {'0001': self.driver.FCP([])}, 7) - - def test_build_connection_info_get_none(self): - self.assertIsNone(self.driver._build_connection_info(None)) - self.assertIsNone(self.driver._build_connection_info({'fake': 'bdm'})) - invalid_bdm = {"connection_info": 'aaa{"data": {"host": "fake_host",' + - '"zvm_fcp": "0001"}}'} - self.assertIsNone(self.driver._build_connection_info(invalid_bdm)) - - def test_build_connection_info(self): - fake_bdm = {"connection_info": '{"data": {"host": "fake_host",' + - '"zvm_fcp": "0001"}}'} - target_info = {'data': {'host': 'fake_host', 'zvm_fcp': ['0001']}} - connection_info = self.driver._build_connection_info(fake_bdm) - self.assertEqual(target_info, connection_info) - - fake_bdm = {"connection_info": '{"data": {"host": "fake_host",' + - '"zvm_fcp": ["0001", "0002"]}}'} - target_info = {'data': {'host': 'fake_host', - 'zvm_fcp': ['0001', '0002']}} - connection_info = self.driver._build_connection_info(fake_bdm) - self.assertEqual(target_info, connection_info) - - def test_get_volume_connector_no_fcp(self): - self.mox.StubOutWithMock(self.driver, '_get_fcp_from_pool') - self.driver._get_fcp_from_pool().AndReturn(None) - self.mox.ReplayAll() - - empty_connector = {'zvm_fcp': [], 'wwpns': [], 'host': ''} - self.assertEqual(empty_connector, - self.driver.get_volume_connector(self.fake_inst)) - self.mox.VerifyAll() - - def test_get_volume_connector_from_instance(self): - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - - farg = mox.IgnoreArg() - self.mox.StubOutWithMock(self.driver, '_get_wwpn') - self.driver._get_wwpn(farg).AndReturn('0000000100000001') - self.mox.ReplayAll() - - target_connector = {'host': 'fakenode', - 'wwpns': ['0000000100000001'], - 'zvm_fcp': ['1faa']} - self.assertEqual(target_connector, - self.driver.get_volume_connector(self.fake_inst)) - self.mox.VerifyAll() - - self.driver._instance_fcp_map = {} - - def test_get_volume_connector_from_pool(self): - self.driver._fcp_pool = {'1faa': self.driver.FCP([]), - '1fab': self.driver.FCP([])} - self.mox.StubOutWithMock(self.driver, '_get_fcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_get_wwpn') - farg = mox.IgnoreArg() - self.driver._get_fcp_from_pool().AndReturn(['1faa', '1fab']) - self.driver._get_wwpn(farg).AndReturn('0000000100000001') - self.driver._get_wwpn(farg).AndReturn('0000000200000002') - self.mox.ReplayAll() - - target_connector = {'host': 'fakenode', - 'wwpns': ['0000000100000001', '0000000200000002'], - 'zvm_fcp': ['1faa', '1fab']} - self.assertEqual(target_connector, - self.driver.get_volume_connector(self.fake_inst)) - self.assertEqual({'fcp_list': ['1faa', '1fab'], 'count': 0}, - self.driver._instance_fcp_map.get(self.inst_name)) - self.mox.VerifyAll() - - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {} - - def test_get_volume_connector_no_wwpn(self): - self.driver._fcp_pool = {'0001': self.driver.FCP([])} - self.mox.StubOutWithMock(self.driver, '_get_fcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_get_wwpn') - farg = mox.IgnoreArg() - self.driver._get_fcp_from_pool().AndReturn(['0001']) - self.driver._get_wwpn(farg).AndReturn(None) - self.mox.ReplayAll() - - empty_connector = {'zvm_fcp': [], 'wwpns': [], 'host': ''} - self.assertEqual(empty_connector, - self.driver.get_volume_connector(self.fake_inst)) - self.mox.VerifyAll() - self.driver._fcp_pool = {} - - def test_get_wwpn_fcp_not_exist(self): - self.assertIsNone(self.driver._get_wwpn('0001')) - - def test_get_wwpn_fcp_invalid(self): - self.driver._fcp_pool = {'0001': self.driver.FCP([])} - self.assertIsNone(self.driver._get_wwpn('0001')) - self.driver._fcp_pool = {} - - def test_get_wwpn(self): - fcp_info1 = ['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181'] - fcp_info2 = ['opnstk1: FCP device number: 0002', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: ' - '20076D8500005182', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: NONE'] - self.driver._fcp_pool = {'0001': self.driver.FCP(fcp_info1), - '0002': self.driver.FCP(fcp_info2)} - self.assertEqual('20076d8500005181', self.driver._get_wwpn('0001')) - self.assertEqual('20076d8500005182', self.driver._get_wwpn('0002')) - self.driver._fcp_pool = {} - - def test_list_fcp_details_get_none(self): - self.mox.StubOutWithMock(self.driver, '_xcat_rinv') - self.driver._xcat_rinv(mox.IgnoreArg()).AndReturn({'info': None}) - self.mox.ReplayAll() - - self.assertIsNone(self.driver._list_fcp_details('free')) - self.mox.VerifyAll() - - def test_list_fcp_details(self): - fake_rsp = {'info': [['line1\nline2\nline3\nline4\nline5\n']]} - self.mox.StubOutWithMock(self.driver, '_xcat_rinv') - self.driver._xcat_rinv(mox.IgnoreArg()).AndReturn(fake_rsp) - self.mox.ReplayAll() - - target = ['line1', 'line2', 'line3', 'line4', 'line5'] - self.assertTrue(target == self.driver._list_fcp_details('active')) - self.mox.VerifyAll() - - def test_get_fcp_from_pool_get_none(self): - self.driver._fcp_pool = {} - self.assertEqual([], self.driver._get_fcp_from_pool()) - fcp = self.driver.FCP(['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - fcp.set_in_use() - self.driver._fcp_pool = {'0001': fcp} - self.assertEqual([], self.driver._get_fcp_from_pool()) - self.driver._fcp_pool = {} - - def test_get_fcp_from_pool_no_release(self): - fcp = self.driver.FCP(['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - self.driver._fcp_pool = {'0001': fcp} - self.assertEqual(self.driver._get_fcp_from_pool(), ['0001']) - self.driver._fcp_pool = {} - - def test_get_fcp_from_pool_do_release(self): - fcp = self.driver.FCP(['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - self.driver._fcp_pool = {'0001': fcp} - self.driver._update_instance_fcp_map('inst2', ['0001'], - self.driver._RESERVE) - time.sleep(30) - self.assertTrue(fcp.is_reserved()) - expected_map = {'inst2': {'fcp_list': ['0001'], 'count': 0}} - self.assertEqual(expected_map, self.driver._instance_fcp_map) - self.assertEqual(self.driver._get_fcp_from_pool(), ['0001']) - self.assertFalse(fcp.is_reserved()) - self.assertEqual({}, self.driver._instance_fcp_map) - self.driver._fcp_pool = {} - - def test_get_fcp_from_pool_same_chpid(self): - fcp1 = self.driver.FCP(['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - fcp2 = self.driver.FCP(['opnstk1: FCP device number: 0002', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - self.driver._fcp_pool = {'0001': fcp1, '0002': fcp2} - self.flags(zvm_multiple_fcp=True) - self.assertEqual([], self.driver._get_fcp_from_pool()) - self.driver._fcp_pool = {} - self.flags(zvm_multiple_fcp=False) - - def test_get_fcp_from_pool_multiple_fcp(self): - fcp1 = self.driver.FCP(['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - fcp2 = self.driver.FCP(['opnstk1: FCP device number: 0002', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5B', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - self.driver._fcp_pool = {'0001': fcp1, '0002': fcp2} - self.flags(zvm_multiple_fcp=True) - self.assertEqual(set(['0001', '0002']), - set(self.driver._get_fcp_from_pool())) - self.driver._fcp_pool = {} - self.flags(zvm_multiple_fcp=False) - - def test_release_fcps_reserved(self): - fcp1 = self.driver.FCP(['opnstk1: FCP device number: 0001', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5A', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - fcp2 = self.driver.FCP(['opnstk1: FCP device number: 0002', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5B', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - fcp3 = self.driver.FCP(['opnstk1: FCP device number: 0003', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5B', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - fcp4 = self.driver.FCP(['opnstk1: FCP device number: 0004', - 'opnstk1: Status: Free', - 'opnstk1: NPIV world wide port number: NONE', - 'opnstk1: Channel path ID: 5B', - 'opnstk1: Physical world wide port number: ' - '20076D8500005181']) - self.driver._fcp_pool = {'0001': fcp1, '0002': fcp2, - '0003': fcp3, '0004': fcp4} - self.driver._update_instance_fcp_map('inst1', ['0001'], - self.driver._INCREASE) - self.driver._update_instance_fcp_map('inst2', ['0002', '0003'], - self.driver._RESERVE) - self.driver._update_instance_fcp_map('inst3', ['0004'], - self.driver._RESERVE) - expected = {'inst1': {'fcp_list': ['0001'], 'count': 1}, - 'inst2': {'fcp_list': ['0002', '0003'], 'count': 0}, - 'inst3': {'fcp_list': ['0004'], 'count': 0}} - self.assertEqual(expected, self.driver._instance_fcp_map) - self.driver._release_fcps_reserved() - self.assertEqual(expected, self.driver._instance_fcp_map) - - time.sleep(35) - self.driver._release_fcps_reserved() - expected = {'inst1': {'fcp_list': ['0001'], 'count': 1}} - self.assertEqual(expected, self.driver._instance_fcp_map) - - self.driver._fcp_pool = {} - self.driver._instance_fcp_map = {} - - def test_extract_connection_info_error(self): - fake_connection_info = 'fake_info' - self.assertRaises(exception.ZVMVolumeError, - self.driver._extract_connection_info, - None, fake_connection_info) - - def test_extract_connection_info_no_context(self): - fake_connection_info = {'data': {'target_lun': 10, - 'target_wwn': '0000000B', - 'zvm_fcp': ['1FAA']}} - target_info = ('000a000000000000', '0000000b', '0G', '1faa') - self.assertEqual(target_info, - self.driver._extract_connection_info( - None, fake_connection_info)) - - def test_extract_connection_info_with_context(self): - fake_connection_info = {'data': {'target_lun': 10, - 'target_wwn': '0000000B', - 'volume_id': 'fake_id', - 'zvm_fcp': ['1FAA']}} - fake_context = 'fake_context' - self.mox.StubOutWithMock(self.driver, '_get_volume_by_id') - - farg = mox.IgnoreArg() - self.driver._get_volume_by_id(farg, farg).AndReturn({'size': 2}) - self.mox.ReplayAll() - - target_info = ('000a000000000000', '0000000b', '2G', '1faa') - self.assertEqual(target_info, - self.driver._extract_connection_info( - fake_context, fake_connection_info)) - self.mox.VerifyAll() - - def test_extract_connection_info_multipath(self): - fake_connection_info = {'data': {'target_lun': 10, - 'target_wwn': ['00000B', '00000E'], - 'volume_id': 'fake_id', - 'zvm_fcp': ['1FAA']}} - fake_context = 'fake_context' - self.mox.StubOutWithMock(self.driver, '_get_volume_by_id') - - farg = mox.IgnoreArg() - self.driver._get_volume_by_id(farg, farg).AndReturn({'size': 2}) - self.mox.ReplayAll() - - target_info = ('000a000000000000', '00000b;00000e', '2G', '1faa') - self.assertEqual(target_info, - self.driver._extract_connection_info( - fake_context, fake_connection_info)) - self.mox.VerifyAll() - - def test_extract_connection_info_multifcp(self): - fake_connection_info = {'data': {'target_lun': 10, - 'target_wwn': ['00000B', '00000E'], - 'volume_id': 'fake_id', - 'zvm_fcp': ['1FAA', '1Fab']}} - fake_context = 'fake_context' - self.mox.StubOutWithMock(self.driver, '_get_volume_by_id') - - farg = mox.IgnoreArg() - self.driver._get_volume_by_id(farg, farg).AndReturn({'size': 2}) - self.mox.ReplayAll() - - target_info = ('000a000000000000', '00000b;00000e', '2G', '1faa;1fab') - self.assertEqual(target_info, - self.driver._extract_connection_info( - fake_context, fake_connection_info)) - self.mox.VerifyAll() - - def test_format_wwpn(self): - self.assertEqual('0a0b', self.driver._format_wwpn('0A0B')) - self.assertEqual('0a0b;0a0c', - self.driver._format_wwpn(['0A0B', '0A0c'])) - - def test_format_fcp_list(self): - self.assertEqual('0001', self.driver._format_fcp_list(['0001'])) - self.assertEqual('0001;0002', - self.driver._format_fcp_list(['0001', '0002'])) - - def test_attach_volume_active_error_no_rollback(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.driver._create_mountpoint(farg, farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.attach_volume_active, farg, - farg, self.fake_inst, fake_mountpoint, rollback) - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_active_error_rollback_and_detach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_detach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.driver._create_mountpoint(farg, farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_mountpoint(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_zfcp(farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_zfcp_from_pool(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._detach_device(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.attach_volume_active, farg, farg, - self.fake_inst, fake_mountpoint, rollback) - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_active_error_rollback_no_detach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.driver._create_mountpoint(farg, farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_mountpoint(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_zfcp(farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_zfcp_from_pool(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.attach_volume_active, farg, - farg, self.fake_inst, fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_active(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.driver._create_mountpoint(farg, farg, farg, farg, farg).AndReturn( - None) - self.mox.ReplayAll() - - self.driver.attach_volume_active(farg, farg, self.fake_inst, - fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_active_multi_fcp(self): - fake_info = ('lun', 'wwpn', '1G', '1faa;1fab') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([]), - '1fab': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.driver._create_mountpoint(farg, farg, farg, farg, farg).AndReturn( - None) - self.mox.ReplayAll() - - self.driver.attach_volume_active(farg, farg, self.fake_inst, - fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_active_no_mountpoint(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.attach_volume_active(farg, farg, self.fake_inst, - None, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_active_error_no_rollback(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_detach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_mountpoint(farg, farg).AndReturn(None) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.detach_volume_active, farg, - self.fake_inst, fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_active_error_rollback_with_mountpoint(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_detach_device') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - self.mox.StubOutWithMock(self.driver, '_create_mountpoint') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_mountpoint(farg, farg).AndReturn(None) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._create_mountpoint(farg, farg, farg, farg, farg).AndReturn( - None) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.detach_volume_active, farg, - self.fake_inst, fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_active_error_rollback_no_mountpoint(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_detach_device') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_add_zfcp') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._add_zfcp(farg, farg, farg, farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.detach_volume_active, - farg, self.fake_inst, None, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_active_and_detach_fcp(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_detach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_mountpoint(farg, farg).AndReturn(None) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.detach_volume_active(farg, self.fake_inst, - fake_mountpoint, rollback) - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_active_and_reserve_fcp(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 2}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_mountpoint(farg, farg).AndReturn(None) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.detach_volume_active(farg, self.fake_inst, - fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_active_multi_fcp(self): - fake_info = ('lun', 'wwpn', '1G', '1faa;1fab') - fake_mountpoint = '/dev/vdd' - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': [ - '1faa', '1fab'], - 'count': 2}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([]), - '1fab': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_mountpoint') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_mountpoint(farg, farg).AndReturn(None) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.detach_volume_active(farg, self.fake_inst, - fake_mountpoint, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_inactive_error_no_rollback(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_attach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._attach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.attach_volume_inactive, farg, - farg, self.fake_inst, farg, rollback) - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_inactive_error_rollback_and_detach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_attach_device') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_detach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._attach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_zfcp_from_pool(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._detach_device(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.attach_volume_inactive, farg, - farg, self.fake_inst, farg, rollback) - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_inactive_error_rollback_no_detach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._remove_zfcp_from_pool(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.attach_volume_inactive, farg, - farg, self.fake_inst, farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_inactive(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_attach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._attach_device(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.attach_volume_inactive(farg, farg, self.fake_inst, - farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_inactive_multi_fcp(self): - fake_info = ('lun', 'wwpn', '1G', '1faa;1fab') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {} - self.driver._fcp_pool = {'1faa': self.driver.FCP([]), - '1fab': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_attach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._attach_device(farg, farg).AndReturn(None) - self.driver._attach_device(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.attach_volume_inactive(farg, farg, self.fake_inst, - farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_attach_volume_inactive_no_attach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(None) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.attach_volume_inactive(farg, farg, self.fake_inst, - farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_inactive_error_no_rollback(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = False - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - self.mox.StubOutWithMock(self.driver, '_detach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.detach_volume_inactive, - farg, self.fake_inst, farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_inactive_error_detach_rollback(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - self.mox.StubOutWithMock(self.driver, '_detach_device') - self.mox.StubOutWithMock(self.driver, '_attach_device') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._attach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndReturn(True) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.detach_volume_inactive, - farg, self.fake_inst, farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_inactive_error_no_detach_rollback(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 2}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - self.mox.StubOutWithMock(self.driver, '_attach_device') - self.mox.StubOutWithMock(self.driver, '_notice_attach') - self.mox.StubOutWithMock(self.driver, '_add_zfcp_to_pool') - self.mox.StubOutWithMock(self.driver, '_allocate_zfcp') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._attach_device(farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._notice_attach(farg, farg, farg, farg, farg, - farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._add_zfcp_to_pool(farg, farg, farg, farg).AndRaise( - exception.ZVMVolumeError(msg='No msg')) - self.driver._allocate_zfcp(farg, farg, farg, farg, farg).AndReturn( - None) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMVolumeError, - self.driver.detach_volume_inactive, - farg, self.fake_inst, farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_inactive_and_detach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 1}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - self.mox.StubOutWithMock(self.driver, '_detach_device') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.driver._detach_device(farg, farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.detach_volume_inactive(farg, self.fake_inst, - farg, rollback) - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_inactive_no_detach(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 2}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.detach_volume_inactive(farg, self.fake_inst, - farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_detach_volume_inactive_multi_path(self): - fake_info = ('lun', 'wwpn', '1G', '1faa') - self.fake_inst.system_metadata = {'image_os_version': 'sles11'} - self.driver._instance_fcp_map = {self.inst_name: {'fcp_list': ['1faa'], - 'count': 2}} - self.driver._fcp_pool = {'1faa': self.driver.FCP([]), - '1fab': self.driver.FCP([])} - rollback = True - - self.mox.StubOutWithMock(self.driver, '_extract_connection_info') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp') - self.mox.StubOutWithMock(self.driver, '_remove_zfcp_from_pool') - self.mox.StubOutWithMock(self.driver, '_notice_detach') - - farg = mox.IgnoreArg() - self.driver._extract_connection_info(farg, farg).AndReturn(fake_info) - self.driver._remove_zfcp(farg, farg, farg, farg).AndReturn(None) - self.driver._remove_zfcp_from_pool(farg, farg).AndReturn(None) - self.driver._notice_detach(farg, farg, farg, farg, farg, - farg).AndReturn(None) - self.mox.ReplayAll() - - self.driver.detach_volume_inactive(farg, self.fake_inst, - farg, rollback) - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - self.driver._instance_fcp_map = {} - - def test_expand_fcp_list(self): - fcp_list = '01aF;01b9-01BB;01BA-01BC;0000;9999;AAAA;FFFF;1;a' - target_list = set(['01af', '01b9', '01ba', '01bb', '01bc', '0000', - '9999', 'aaaa', 'ffff', '0001', '000a']) - self.assertEqual(target_list, self.driver._expand_fcp_list(fcp_list)) - - def test_is_fcp_in_use_no_record(self): - self.driver._instance_fcp_map = {} - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - - def test_is_fcp_in_use_reserved(self): - self.driver._instance_fcp_map = {self.inst_name: {'fcp': '1faa', - 'count': 0}} - self.assertFalse(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - - def test_is_fcp_in_use_only_one(self): - self.driver._instance_fcp_map = {self.inst_name: {'fcp': '1faa', - 'count': 1}} - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - - def test_is_fcp_in_use_multi_volume(self): - self.driver._instance_fcp_map = {self.inst_name: {'fcp': '1faa', - 'count': 3}} - self.assertTrue(self.driver._is_fcp_in_use(self.fake_inst)) - self.mox.VerifyAll() - - -class ZVMImageOPTestCases(ZVMTestCase): - """Basic test cases of image operation.""" - - def setUp(self): - super(ZVMImageOPTestCases, self).setUp() - self.imageop = imageop.ZVMImages() - self.res_data = {"data": [{"data": [["song017c: Filesystem " - "Size Used Avail Use% Mounted on\nsong017c: " - "/dev/dasda1 3.0G 1.7G 1.2G 60% /"]]}, - {"errorcode": [["0"]], "NoErrorPrefix": [["1"]], - "error": [["song017c: @@@@@@@@@@@@@@@@@@@@@@@@@@@" - "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", "song017c: @ " - "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!" - "@\n", "song017c: @@@@@@@@@@@@@@@@@" - "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n", - "song017c: IT IS POSSIBLE THAT SOMEONE IS " - "DOING SOMETHING NASTY!\n", - "song017c: Someone could be eavesdropping on " - "you right now (man-in-the-middle attack)!\n", - "song017c: It is also possible that a host key " - "has just been changed.\n", - "song017c: The fingerprint for the RSA key sent " - "by the remote host is\nsong017c: d9:9c:71:ed:be:8e:" - "cd:0e:b1:1a:f3:fe:a8:30:84:8b.\n", - "song017c: Please contact your administrator.\n", - "song017c: Add host key in /root/.ssh/known_hosts" - "to get rid of this message.\n", - "song017c: /root/.ssh/known_hosts:22\n", - "song017c: Password authentication is disabled " - "to avoid man-in-the-middle attacks.\n", - "song017c: Keyboard-interactive authentication " - "is disabled to avoid man-the-middle attacks.\n"]]}, - {"errorcode": ["0"]}]} - - def test_get_imgcapture_needed(self): - rdisk_dict = [ - 'os000001: MDISK 0100 3390 n 100 n MR\n', - 'os000001: MDISK 0102 3390 n 200 n MR\n', - 'os000001: MDISK 0103 3390 n 300 n MR\n'] - size = self.imageop.get_imgcapture_needed(self.instance, rdisk_dict) - self.assertEqual(size, float(100) * 737280 / 1024.0 / 1024 / 1024 * 2) - - self.mox.VerifyAll() - - def test_get_image_file_path_from_image_name(self): - image_name_xcat = "rhel65-s390x-netboot-img_uuid" - - self.mox.StubOutWithMock(zvmutils, 'xcat_cmd_gettab') - zvmutils.xcat_cmd_gettab("linuximage", "imagename", image_name_xcat, - "rootimgdir").AndReturn('/install/netboot/rhel65/s390x/img_uuid') - self.mox.ReplayAll() - - image_file_path = self.imageop.get_image_file_path_from_image_name( - image_name_xcat) - self.mox.VerifyAll() - self.assertEqual(image_file_path, - '/install/netboot/rhel65/s390x/img_uuid') - - def test_delete_image_from_xcat(self): - image_name_xcat = "rhel65-s390x-netboot-img_uuid" - - self.mox.StubOutWithMock(self.imageop, '_delete_image_file_from_xcat') - self.mox.StubOutWithMock(self.imageop, - '_delete_image_object_from_xcat') - self.imageop._delete_image_file_from_xcat(image_name_xcat) - self.imageop._delete_image_object_from_xcat(image_name_xcat) - self.mox.ReplayAll() - - self.imageop.delete_image_from_xcat(image_name_xcat) - self.mox.VerifyAll() - - def test_get_image_file_name_not_exist(self): - self.mox.StubOutWithMock(os.path, 'exists') - os.path.exists('/fake').AndReturn(False) - self.mox.ReplayAll() - - self.assertRaises(exception.ZVMImageError, - self.imageop.get_image_file_name, '/fake') - self.mox.VerifyAll() - - @mock.patch('nova.utils.execute') - def test_get_root_disk_units(self, mk_exec): - # mk_exec.return_value = (''.join(['CKD', '1' * 160]), None) - mk_exec.return_value = (('00000000 78 43 41 54 20 43 4b 44 20 44 69' - ' 73 6b 20 49 6d |xCAT CKD Disk Im| 00000010 61 67 65 3a 20 20 20 20' - ' 20 20 20 20 33 33 33 38 |age: 3338| 00000020 20 43 59 4c' - ' 20 48 4c 65 6e 3a 20 30 30 35 35 20 | CYL HLen: 0055 | 00000030'), - None) - self.assertEqual('3338:CYL', - self.imageop.get_root_disk_units('/fake')) - - @mock.patch('nova.utils.execute') - def test_get_root_disk_units_cmd_err(self, mk_exec): - mk_exec.side_effect = processutils.ProcessExecutionError - self.assertRaises(exception.ZVMImageError, - self.imageop.get_root_disk_units, '/fake') - - @mock.patch('nova.utils.execute') - def test_get_root_disk_units_value_err(self, mk_exec): - mk_exec.return_value = (''.join(['CKD', 's' * 160]), None) - self.assertRaises(exception.ZVMImageError, - self.imageop.get_root_disk_units, '/fake') - - @mock.patch('nova.utils.execute') - def test_get_root_disk_units_invalid_type(self, mk_exec): - mk_exec.return_value = ('1' * 160, None) - self.assertRaises(exception.ZVMImageError, - self.imageop.get_root_disk_units, '/fake') - - def test_zimage_check(self): - image_meta = {} - image_meta['properties'] = {'image_file_name': 'name', - 'image_type__xcat': 'type', - 'architecture': 's390x', - 'os_name': 'name', - 'provisioning_method': 'method'} - image_meta['id'] = '0' - exc = self.assertRaises(nova_exception.ImageUnacceptable, - self.imageop.zimage_check, image_meta) - msg = ("Image 0 is unacceptable: The image 0 is not a valid zVM " - "image, property ['image_type_xcat', 'os_version'] " - "are missing") - self.assertEqual(msg, six.text_type(exc)) - - -class ZVMDistTestCases(test.TestCase): - def setUp(self): - super(ZVMDistTestCases, self).setUp() - - self.rhel6 = dist.rhel6() - self.rhel7 = dist.rhel7() - self.sles11 = dist.sles11() - self.sles12 = dist.sles12() - self.ubuntu16 = dist.ubuntu16() - self.support_list = [self.rhel6, self.rhel7, - self.sles11, self.sles12, self.ubuntu16] - - def test_get_znetconfig_contents(self): - for v in self.support_list: - contents = v.get_znetconfig_contents() - self.assertTrue(len(contents) > 0) - - def test_get_dns_filename(self): - for v in self.support_list: - v._get_dns_filename() - - def test_get_cmd_str(self): - for v in self.support_list: - v._get_cmd_str('0', '0', '0') - - def test_get_route_str(self): - for v in self.support_list: - v._get_route_str(0) - - def test_get_network_file_path(self): - for v in self.support_list: - contents = v._get_network_file_path() - if v != self.ubuntu16: - self.assertTrue(len(contents) > 0) - - def test_get_change_passwd_command(self): - for v in self.support_list: - contents = v.get_change_passwd_command('0') - self.assertTrue(len(contents) > 0) - - def test_get_device_name(self): - for v in self.support_list: - contents = v._get_device_name(0) - self.assertTrue(len(contents) > 0) - - def test_get_cfg_str(self): - for v in self.support_list: - if v is self.ubuntu16: - v._get_cfg_str('0', '0', '0', '0', '0') - else: - v._get_cfg_str('0', '0', '0', '0', '0', '0', '0') - - def test_get_device_filename(self): - for v in self.support_list: - contents = v._get_device_filename(0) - self.assertTrue(len(contents) > 0) - - def test_get_udev_configuration(self): - for v in self.support_list: - v._get_udev_configuration('0', '0') - - def test_append_udev_info(self): - for v in self.support_list: - v._append_udev_info([], '0', '0', '0') - - def test_get_scp_string(self): - root = "/dev/sda2" - fcp = "1faa" - wwpn = "55556666" - lun = "11112222" - - expected = ("=root=/dev/sda2 selinux=0 zfcp.allow_lun_scan=0 " - "rd_ZFCP=0.0.1faa,0x55556666,0x11112222") - actual = self.rhel6.get_scp_string(root, fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ("=root=/dev/sda2 selinux=0 zfcp.allow_lun_scan=0 " - "rd.zfcp=0.0.1faa,0x55556666,0x11112222") - actual = self.rhel7.get_scp_string(root, fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ("=root=/dev/sda2 zfcp.allow_lun_scan=0 " - "zfcp.device=0.0.1faa,0x55556666,0x11112222") - actual = self.sles11.get_scp_string(root, fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ("=root=/dev/sda2 zfcp.allow_lun_scan=0 " - "zfcp.device=0.0.1faa,0x55556666,0x11112222") - actual = self.sles12.get_scp_string(root, fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ("=root=/dev/sda2 zfcp.allow_lun_scan=0 " - "zfcp.device=0.0.1faa,0x55556666,0x11112222") - actual = self.ubuntu16.get_scp_string(root, fcp, wwpn, lun) - self.assertEqual(expected, actual) - - def test_get_zipl_script_lines(self): - image = "image" - ramdisk = "ramdisk" - root = "/dev/sda2" - fcp = "1faa" - wwpn = "55556666" - lun = "11112222" - - expected = ['#!/bin/bash\n', - 'echo -e "[defaultboot]\\n' - 'timeout=5\\n' - 'default=boot-from-volume\\n' - 'target=/boot/\\n' - '[boot-from-volume]\\n' - 'image=image\\n' - 'ramdisk=ramdisk\\n' - 'parameters=\\"root=/dev/sda2 ' - 'rd_ZFCP=0.0.1faa,0x55556666,0x11112222 ' - 'zfcp.allow_lun_scan=0 selinux=0\\""' - '>/etc/zipl_volume.conf\n' - 'zipl -c /etc/zipl_volume.conf'] - actual = self.rhel6.get_zipl_script_lines(image, ramdisk, root, - fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ['#!/bin/bash\n', - 'echo -e "[defaultboot]\\n' - 'timeout=5\\n' - 'default=boot-from-volume\\n' - 'target=/boot/\\n' - '[boot-from-volume]\\n' - 'image=image\\n' - 'ramdisk=ramdisk\\n' - 'parameters=\\"root=/dev/sda2 ' - 'rd.zfcp=0.0.1faa,0x55556666,0x11112222 ' - 'zfcp.allow_lun_scan=0 selinux=0\\""' - '>/etc/zipl_volume.conf\n' - 'zipl -c /etc/zipl_volume.conf'] - actual = self.rhel7.get_zipl_script_lines(image, ramdisk, root, - fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ['#!/bin/bash\n', - 'echo -e "[defaultboot]\\n' - 'default=boot-from-volume\\n' - '[boot-from-volume]\\n' - 'image=image\\n' - 'target = /boot/zipl\\n' - 'ramdisk=ramdisk\\n' - 'parameters=\\"root=/dev/sda2 ' - 'zfcp.device=0.0.1faa,0x55556666,0x11112222 ' - 'zfcp.allow_lun_scan=0\\""' - '>/etc/zipl_volume.conf\n' - 'mkinitrd\n' - 'zipl -c /etc/zipl_volume.conf'] - actual = self.sles11.get_zipl_script_lines(image, ramdisk, root, - fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ['#!/bin/bash\n', - 'echo -e "[defaultboot]\\n' - 'default=boot-from-volume\\n' - '[boot-from-volume]\\n' - 'image=image\\n' - 'target = /boot/zipl\\n' - 'ramdisk=ramdisk\\n' - 'parameters=\\"root=/dev/sda2 ' - 'zfcp.device=0.0.1faa,0x55556666,0x11112222 ' - 'zfcp.allow_lun_scan=0\\""' - '>/etc/zipl_volume.conf\n' - 'mkinitrd\n' - 'zipl -c /etc/zipl_volume.conf'] - actual = self.sles12.get_zipl_script_lines(image, ramdisk, root, - fcp, wwpn, lun) - self.assertEqual(expected, actual) - - expected = ['#!/bin/bash\n', - 'echo -e "[defaultboot]\\n' - 'default=boot-from-volume\\n' - '[boot-from-volume]\\n' - 'image=image\\n' - 'target = /boot/zipl\\n' - 'ramdisk=ramdisk\\n' - 'parameters=\\"root=/dev/sda2 ' - 'zfcp.device=0.0.1faa,0x55556666,0x11112222 ' - 'zfcp.allow_lun_scan=0\\""' - '>/etc/zipl_volume.conf\n' - 'mkinitrd\n' - 'zipl -c /etc/zipl_volume.conf'] - actual = self.ubuntu16.get_zipl_script_lines(image, ramdisk, root, - fcp, wwpn, lun) - self.assertEqual(expected, actual) - - -class ZVMDistRhel7TestCases(test.TestCase): - def setUp(self): - super(ZVMDistRhel7TestCases, self).setUp() - - self.rhel7 = dist.rhel7() - - def test_get_device_name(self): - result = ['enccw0.0.1000', 'enccw0.0.1003', 'enccw0.0.100c'] - devices = [0, 1, 4] - for (index, s) in enumerate(devices): - contents = self.rhel7._get_device_name(s) - self.assertEqual(result[index], contents) - - result = ['enccw0.0.0800', 'enccw0.0.0803', 'enccw0.0.080c'] - temp = CONF.zvm_default_nic_vdev - self.flags(zvm_default_nic_vdev='800') - devices = [0, 1, 4] - for (index, s) in enumerate(devices): - contents = self.rhel7._get_device_name(s) - self.assertEqual(result[index], contents) - self.flags(zvm_default_nic_vdev=temp) - - def test_get_device_filename(self): - result = ['ifcfg-enccw0.0.1000', - 'ifcfg-enccw0.0.1003', - 'ifcfg-enccw0.0.100c'] - devices = [0, 1, 4] - for (index, s) in enumerate(devices): - contents = self.rhel7._get_device_filename(s) - self.assertEqual(result[index], contents) - - result = ['ifcfg-enccw0.0.0800', - 'ifcfg-enccw0.0.0803', - 'ifcfg-enccw0.0.080c'] - temp = CONF.zvm_default_nic_vdev - self.flags(zvm_default_nic_vdev='800') - devices = [0, 1, 4] - for (index, s) in enumerate(devices): - contents = self.rhel7._get_device_filename(s) - self.assertEqual(result[index], contents) - self.flags(zvm_default_nic_vdev=temp) - - -class ZVMDistManagerTestCases(test.TestCase): - def setUp(self): - super(ZVMDistManagerTestCases, self).setUp() - self.dist_manager = dist.ListDistManager() - - def test_rhel6(self): - os_versions = ['rhel6.5', 'rhel6', 'redhat6.4', 'redhat6' - 'red hat6', 'red hat6.5', 'RhEl6', 'RedHat6.5', - 'REDHAT6.4'] - for v in os_versions: - d = self.dist_manager.get_linux_dist(v)() - self.assertIsInstance(d, dist.rhel6) - - def test_rhel7(self): - os_versions = ['rhel7.1', 'red hat7.1', 'redhat7.1', 'RHEL7.1'] - for v in os_versions: - d = self.dist_manager.get_linux_dist(v)() - self.assertIsInstance(d, dist.rhel7) - - def test_sles11(self): - os_versions = ['sles11sp2', 'sles11sp3', 'sles11.2', 'sles11.3', - 'Sles11sp3', 'SLES11.2'] - for v in os_versions: - d = self.dist_manager.get_linux_dist(v)() - self.assertIsInstance(d, dist.sles11) - - def test_sles12(self): - os_versions = ['sles12', 'sles12.0', 'Sles12', 'SLES12.0'] - for v in os_versions: - d = self.dist_manager.get_linux_dist(v)() - self.assertIsInstance(d, dist.sles12) - - def test_ubuntu16(self): - os_versions = ['ubuntu16', 'Ubuntu16.04', 'Ubuntu16.04.5'] - for v in os_versions: - d = self.dist_manager.get_linux_dist(v)() - self.assertIsInstance(d, dist.ubuntu16) - - def test_invalid(self): - os_versions = ['', 'sles 11.0', 'sles13.0', 'sles10', 'rhel8', - 'rhel 6', 'fake', 'SELS12.0', 'Ubuntu17'] - for v in os_versions: - self.assertRaises(exception.ZVMImageError, - self.dist_manager.get_linux_dist, v) - - -class ZVMCopiedInstanceTestCases(test.TestCase): - - def test_getattr_and_getitem(self): - _fake_instace = instance.CopiedInstance({'uuid': 'uuid'}) - copied_inst = instance.CopiedInstance(_fake_instace) - self.assertEqual(copied_inst.uuid, 'uuid') - self.assertEqual(copied_inst['uuid'], 'uuid') diff --git a/nova_zvm/virt/zvm/conf.py b/nova_zvm/virt/zvm/conf.py index 4cfeb3d..a9de3d1 100644 --- a/nova_zvm/virt/zvm/conf.py +++ b/nova_zvm/virt/zvm/conf.py @@ -1,4 +1,4 @@ -# Copyright 2016 IBM Corp. +# Copyright 2017,2018 IBM Corp. # # 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 @@ -15,236 +15,53 @@ from oslo_config import cfg +zvm_opt_group = cfg.OptGroup('zvm', + title='zVM Options', + help=""" +zvm options allows cloud administrator to configure related +z/VM hypervisor driver to be used within an OpenStack deployment. + +zVM options are used when the compute_driver is set to use +zVM (compute_driver=zvm.ZVMDriver) +""") + + zvm_opts = [ - cfg.StrOpt('zvm_xcat_server', + cfg.URIOpt('cloud_connector_url', + sample_default='http://zvm.example.org:8080/', + help=""" +URL to be used to communicate with z/VM Cloud Connector. +"""), + cfg.StrOpt('ca_file', default=None, help=""" -Host name or IP address of xCAT management_node. +CA certificate file to be verified in httpd server with TLS enabled -In order for the nova compute service to manage zVM, it needs to talk to an -xcat management node. This property specifies the ip address or host name -(which can be known from DNS of compute host) of the xcat MN server. +A string, it must be a path to a CA bundle to use. +"""), + cfg.StrOpt('image_tmp_path', + default='/var/lib/nova/images', + help=""" +The path at which images will be stored (snapshot, deploy, etc). + +Images used for deploy and images captured via snapshot +need to be stored on the local disk of the compute host. +This configuration identifies the directory location. Possible values: - IP address(ipaddr) or host name(string) + A file system path on the host running the compute service. """), - cfg.StrOpt('zvm_xcat_username', - default=None, - help=""" -The xCAT user name for the REST API calls. - -This is the user who will be authenticated by xCAT when the REST -API call is made. Usually the user should have admin access to the resource -being managed by xcat. - -Related: - zvm_xcat_password -"""), - cfg.StrOpt('zvm_xcat_password', - default=None, - secret=True, - help=""" -The xCAT password for the REST API calls - -This is the password which will be authenticated by xCAT when -the REST API call is made. - -Related: - zvm_xcat_username -"""), - cfg.StrOpt('zvm_xcat_master', - default=None, - help=""" -The xCAT management node name from the xCAT database. - -The nova zvm driver sometimes needs to interact with the xCAT management node -itself, for example to query the free disk space on the node. - In that case it needs this node name for the REST API call. - -Possible values: - A string specifying the name of the xCAT management node. - This name should come from the xCAT node database. -"""), - cfg.StrOpt('zvm_xcat_ca_file', - default=None, - help=""" -CA file for https connection to xCAT REST API. - -When HTTPS protocol is used to communicate between z/VM driver and xCAT REST -API, z/VM compute service needs to have a CA file which will be used to verify -that it has the correct authorization to talk with xCAT. - -Possible values: - A CA file name and location in the host that is running compute service. -"""), - cfg.StrOpt('zvm_diskpool', - default=None, - help=""" -zVM disk pool for ephemeral disks. - -The volume group name from your directory manager on your z/VM system, -which will be used for ephemeral disks for new instances. -A dollar sign ($) is not allowed in the name. - -Related: - zvm_diskpool_type -"""), - cfg.StrOpt('zvm_diskpool_type', - default='ECKD', - help=""" -Disk type of the disk in the disk pool. - -The disks in the disk pool must all be the same type (ECKD or FBA). - -Related: - zvm_diskpool - -Possible values: - A string, either ECKD or FBA. -"""), - cfg.StrOpt('zvm_host', - default=None, - help=""" -z/VM host that is managed by the compute node. - -This is the name of the hypervisor that is managed by the compute service. -The admin should refer to the z/VM system configuration file -SYSTEM_IDENTIFIER statement for the hypervisor name. - -Possible values: - A 1-8 character string, matching the z/VM system name this - compute service is managing. -"""), - cfg.StrOpt('zvm_xcat_group', - default='all', - help=""" -XCAT group for OpenStack. - -There is a 'group' field in xCAT side and it can divide nodes into -different group and let GUI to select a group of nodes to display. - -Possible values: - A string, only 'all' is valid value. -"""), - cfg.StrOpt('zvm_scsi_pool', - default='xcatzfcp', - help='Default zfcp scsi disk pool'), - cfg.BoolOpt('zvm_multiple_fcp', - default=False, - help='Does use multiple FCP for attaching a volume'), - cfg.StrOpt('zvm_fcp_list', - default=None, - help='Configured fcp list can be used'), - cfg.StrOpt('zvm_zhcp_fcp_list', - default=None, - help='Configured fcp list dedicated to hcp'), - cfg.BoolOpt('zvm_config_drive_inject_password', - default=False, - help='Sets the admin password in the config drive'), - cfg.StrOpt('zvm_vmrelocate_force', - default=None, - help=""" -Specify the z/VM Live Guest Relocation FORCE option. - -Refer to the z/VM CP Commands and Utilities book for more information on -the z/VM VMRELOCATE command and z/VM Live Guest Relocation, -which backs the live migration feature in the zvm nova driver. - -Possible values: - A string which is one of (ARCHITECTURE, DOMAIN, NONE, or STORAGE) - - ARCHITECTURE: attempt migration even though hardware architecture - facilities or z/VM CP features are not available on the destination - system. - - DOMAIN: attempt migration even though the destination system is - not in the z/VM guest's relocation domain. - - STORAGE: attempt migration even if there appears to be insufficient - storage resources on the destination system. - - NONE: No VMRELOCATE FORCE will be used. Live migration will fail if - architecture, domain or storage warnings or errors are encountered. - -Related values: - zvm_vmrelocate_immediate - zvm_vmrelocate_max_total - zvm_vmrelocate_max_quiesce -"""), - cfg.StrOpt('zvm_vmrelocate_immediate', - default='no', - help=""" -Specify the option of live migration timing. - -Refer to zVM manual for more info on zVM live migration. - -Possible values: - A string which is one of (YES, NO) - - YES: VMRELOCATE command will do one early pass through virtual machine - storage and then go directly to the quiesce stage. - NO: specifies immediate processing. - -Related values: - zvm_vmrelocate_force - zvm_vmrelocate_max_total - zvm_vmrelocate_max_quiesce -"""), - cfg.StrOpt('zvm_vmrelocate_max_total', - default='nolimit', - help=""" -Maximum time to wait, in seconds, for live migration to complete. -If this time is exceeded, z/VM will cancel the migration and leave -the z/VM guest running on the source system. - -Refer to the z/VM CP Commands and Utilities book for more information -on the z/VM VMRELOCATE command and z/VM Live Guest Relocation, -which backs the live migration feature in the zvm nova driver. - -Possible values: - A positive integer indicating the time in seconds to wait for - the migration to complete, or "nolimit" to indicate that there is no - time limit on the total amount of time the system should allow for - this live migration - -Related values: - zvm_vmrelocate_force - zvm_vmrelocate_immediate - zvm_vmrelocate_max_quiesce -"""), - cfg.StrOpt('zvm_vmrelocate_max_quiesce', - default='nolimit', - help=""" -Maximum time to wait, in seconds, for the quiesce time of a live migration -to complete. If this time is exceeded, z/VM will cancel the migration and -leave the z/VM guest running on the source system. -The quiesce time is the time during which the Linux guest will be unresponsive -while its final state is transferred. - -Refer to the z/VM CP Commands and Utilities book for more information on the -z/VM VMRELOCATE command and z/VM Live Guest Relocation, -which backs the live migration feature in the zvm nova driver. - -Possible values: - A positive integer indicating the time in seconds to wait for the - quiesce time to complete, or "nolimit" to indicate that there is no time - limit on the quiesce time the system should allow for this live migration. - -Related values: - zvm_vmrelocate_force - zvm_vmrelocate_immediate - zvm_vmrelocate_max_total -"""), - cfg.IntOpt('zvm_reachable_timeout', + cfg.IntOpt('reachable_timeout', default=300, help=""" Timeout (seconds) to wait for an instance to start. -The z/VM driver relies on SSH between the instance and xCAT for communication. -So after an instance is logged on, it must have enough time to start SSH -communication. The driver will keep rechecking SSH communication to the -instance for this timeout. If it can not SSH to the instance, it will notify -the user that starting the instance failed and put the instance in ERROR state. +The z/VM driver relies on communication between the instance and cloud +connector. After an instance is created, it must have enough time to wait +for all the network info to be written into the user directory. +The driver will keep rechecking network status to the instance with the +timeout value, If setting network failed, it will notify the user that +starting the instance failed and put the instance in ERROR state. The underlying z/VM guest will then be deleted. Possible Values: @@ -253,238 +70,13 @@ Possible Values: A value of 0 is used for debug. In this case the underlying z/VM guest will not be deleted when the instance is marked in ERROR state. """), - cfg.IntOpt('zvm_xcat_connection_timeout', - default=3600, - help=""" -Timeout (seconds) for an xCAT HTTP request. +] -The z/VM driver communicates with xCAT via REST APIs. This value states the -maximum amount of time the z/VM driver will wait before timing out a REST API -call. Some actions, like copying an image to the OpenStack compute node, -may take a long time, so set this to the maximum time these long -running tasks may take. -Possible Values: - Any positive integer. - Recommended to be larger than 3600 (1 hour), depending on the size of - your images. -"""), - cfg.IntOpt('zvm_console_log_size', - default=100, - help=""" -The maximum allowed console log size, in kilobytes. +def list_opts(): + return {zvm_opt_group: zvm_opts} -Console logs must be transferred to OpenStack from xCAT, this controls how -large each file can be. A smaller size may mean more calls will be needed -to transfer large consoles, which may not be desirable for performance reasons. -"""), - ] - -zvm_user_opts = [ - cfg.StrOpt('zvm_user_profile', - default='osdflt', - help=""" -PROFILE name to use when creating a z/VM guest. - -When nova deploys an instance on z/VM, it can include some common statements -from a PROFILE definition. -This PROFILE must already be included in your z/VM user directory. - -Possible values: - An 8 character name of a PROFILE that is already defined in the z/VM - user directory. -"""), - cfg.StrOpt('zvm_user_default_password', - default='dfltpass', - deprecated_for_removal=True, - deprecated_reason=""" -zvm_user_default_password is not secure and has been replaced by -zvm_default_admin_userid from Pike release and will be removed in -the near future. -Already running instances are not affected. -""", - secret=True, - help=""" -Default password for a new created z/VM user. - -This is the password for any z/VM user IDs created when OpenStack deploys new -virtual servers (also called nova instances), defined in the USER directory -statement. - -Possible values: - A 1-8 character string. -"""), - cfg.StrOpt('zvm_default_admin_userid', - default='', - help=""" -Default LOGONBY userid(s) for the cloud. - -This is a set of z/VM userid(s) which are allowed to logon using the LOGONBY -keyword to the guests created by the z/VM openstack solution, compatible with -the LBYONLY keyword of the user directory statement. This value is only used -when a guest is created. If you change this value, existing guests' directory -entries are not automatically updated with the new value. -When an ESM is installed, this parameter only governs when the ESM -defers to CP's processing. - -Usage note: - The default is an empty string (''). When the string is empty, you can't - log on to your instances using the 3270 protocol; When a - non-empty string is provided, blank chars will be used as delimiter, - you can use LOGONBY xxx command to log on the - OpenStack created guest using the corresponding admin userid's password. - - For example, when you set this value to 'oper1 oper2 oper3 jones', it means - you can use any one of 'oper1', 'oper2', 'oper3', 'jones' as an admin user. - - see the z/VM CP Planning and Administration for additional information. - -Possible values: - A maximum of 8 blank-delimited strings. Each non-blank string must be a - valid z/VM userid. - e.g '' is a valid value. - 'oper1 oper2' is a valid value. - 'o1 o2 o3 o4 o5 o6 o7 o8 o9' is NOT a valid value. -"""), - cfg.StrOpt('zvm_user_default_privilege', - default='G', - help=""" -Privilege class for the z/VM guest. - -The privilege class controls which z/VM commands the guest can issue. -Classes A-G are defined by default, or the administrator can define -their own using characters A-Z, 1-6. - -Possible value: - Currently, 'G' is the recommended value. - Consult with your z/VM administrator for more information. -"""), - cfg.StrOpt('zvm_user_root_vdev', - default='0100', - help=""" -Virtual device number for root disk. - -When nova deploys an instance, it creates a root disk and several ephemeral -or persistent disks. This value is the virtual device number of the root -disk. If the root disk is a cinder volume instead, this value does not apply. - -Possible values: - An integer value in hex format, between 0 and 65536 (x'FFFF'). - It should not conflict with other device numbers in the z/VM guest's - configuration, for example device numbers of the NICs or ephemeral or - persistent disks. - -Sample root disk in user directory: - MDISK 0100 - -Related values: - zvm_default_nic_vdev - zvm_user_adde_vdev -"""), - cfg.StrOpt('zvm_default_nic_vdev', - default='1000', - help=""" -Virtual device number for default NIC address. - -This value is the first NIC virtual device number, -each NIC needs 3 numbers for control/read/write, so by default -the first NIC's address is 1000, the second one is 1003 etc. - -Possible values: - An integer value in hex format, between 0 and 65536 (x'FFFF'). - It should not conflict with other device numbers in the z/VM guest's - configuration, for example device numbers of the root or ephemeral or - persistent disks. - -Sample NIC definitions in the z/VM user directory: - NICDEF 1000 TYPE QDIO LAN SYSTEM MACID - NICDEF 1003 TYPE QDIO LAN SYSTEM MACID - -Related values: - zvm_user_root_vdev - zvm_user_adde_vdev -"""), - cfg.StrOpt('zvm_user_adde_vdev', - default='0101', - help=""" -Virtual device number for additional ephemeral disk. - -Nova allows a user to deploy an instance with one or more ephemeral disks. -This value is the virtual device number of the first ephemeral disk. -Other ephemeral disks will take subsequent numbers. - -Possible values: - An integer value in hex format, between 0 and 65536 (x'FFFF'). - It should not conflict with other device numbers in the z/VM guest's - configuration, for example device numbers of the root disk or NICs. - -Sample ephemeral disk definitions in the z/VM user directory: - MDISK 0101 - MDISK 0102 - -Related values: - zvm_user_root_vdev - vm_default_nic_vdev -"""), - ] - -zvm_image_opts = [ - cfg.StrOpt('zvm_image_tmp_path', - default='/var/lib/nova/images', - help=""" -The path at which images will be stored (snapshot, deploy, etc). - -The image used to deploy or image captured from instance need to be -stored in local disk of compute node host. This configuration identifies -the directory location. - -Possible values: - A path in host that running compute service. -"""), - cfg.StrOpt('zvm_default_ephemeral_mntdir', - default='/mnt/ephemeral', - help='The path to which the ephemeral disk be mounted'), - cfg.StrOpt('zvm_image_default_password', - default='rootpass', - secret=True, - help='Default os root password for a new created vm'), - cfg.IntOpt('xcat_image_clean_period', - default=30, - help=""" -Number of days an unused xCAT image will be retained before it is purged. - -Copies of Glance images are kept in the xCAT MN to make deploys faster. -Unused images are purged to reclaim disk space. If an image has been purged -from xCAT, the next deploy will take slightly longer as it must be copied -from OpenStack into xCAT. - -Possible values: - Any positive integer, recommended to be at least 30 (1 month). -"""), - cfg.IntOpt('xcat_free_space_threshold', - default=50, - help='The threshold for xCAT free space, if snapshot or spawn ' - 'check xCAT free space not enough for its image ' - 'operations, it will prune image to meet the threshold'), - cfg.StrOpt('zvm_image_compression_level', - default=None, - help=""" -The level of gzip compression used when capturing disk. - -A snapshotted image will consume disk space on xCAT MN host and the OpenStack -compute host. To save disk space the image should be compressed. -The zvm driver uses gzip to compress the image. gzip has a set of different -levels depending on the speed and quality of compression. -For more information, please refer to the -N option of the gzip command. - -Possible values: - An integer between 0 and 9, where 0 is no compression and 9 is the best, - but slowest compression. A value of "None" will result in the default - compression level, which is currently '6' for gzip. -"""), - ] CONF = cfg.CONF -CONF.register_opts(zvm_opts) -CONF.register_opts(zvm_user_opts) -CONF.register_opts(zvm_image_opts) +CONF.register_group(zvm_opt_group) +CONF.register_opts(zvm_opts, group=zvm_opt_group) diff --git a/nova_zvm/virt/zvm/configdrive.py b/nova_zvm/virt/zvm/configdrive.py deleted file mode 100644 index 276d690..0000000 --- a/nova_zvm/virt/zvm/configdrive.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -import os -import tarfile - -from nova import exception -from nova import utils -from nova.virt import configdrive -from oslo_config import cfg -from oslo_log import log as logging - -from nova_zvm.virt.zvm import utils as zvmutils - - -LOG = logging.getLogger(__name__) - - -CONF = cfg.CONF - - -class ZVMConfigDriveBuilder(configdrive.ConfigDriveBuilder): - """Enable ConfigDrive to make tgz package.""" - - def __init__(self, instance_md): - super(ZVMConfigDriveBuilder, self).__init__(instance_md) - - def make_drive(self, path): - """Make the config drive. - - :param path: the path to place the config drive image at - :raises ProcessExecuteError if a helper process has failed. - - """ - if CONF.config_drive_format in ['tgz', 'iso9660']: - self._make_tgz(path) - else: - raise exception.ConfigDriveUnknownFormat( - format=CONF.config_drive_format) - - def _make_tgz(self, path): - try: - olddir = os.getcwd() - except OSError: - olddir = CONF.state_path - - with utils.tempdir() as tmpdir: - self._write_md_files(tmpdir) - tar = tarfile.open(path, "w:gz") - os.chdir(tmpdir) - tar.add("openstack") - tar.add("ec2") - try: - os.chdir(olddir) - except Exception as e: - emsg = zvmutils.format_exception_msg(e) - LOG.debug('exception in _make_tgz %s', emsg) - - tar.close() diff --git a/nova_zvm/virt/zvm/const.py b/nova_zvm/virt/zvm/const.py deleted file mode 100644 index dc5b06d..0000000 --- a/nova_zvm/virt/zvm/const.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - - -from nova.compute import power_state - - -HYPERVISOR_TYPE = 'zvm' -ARCHITECTURE = 's390x' -ALLOWED_VM_TYPE = 'zLinux' -XCAT_MGT = 'zvm' - -XCAT_RINV_HOST_KEYWORDS = { - "zvm_host": "z/VM Host:", - "zhcp": "zHCP:", - "cec_vendor": "CEC Vendor:", - "cec_model": "CEC Model:", - "hypervisor_os": "Hypervisor OS:", - "hypervisor_name": "Hypervisor Name:", - "architecture": "Architecture:", - "lpar_cpu_total": "LPAR CPU Total:", - "lpar_cpu_used": "LPAR CPU Used:", - "lpar_memory_total": "LPAR Memory Total:", - "lpar_memory_used": "LPAR Memory Used:", - "lpar_memory_offline": "LPAR Memory Offline:", - "ipl_time": "IPL Time:", - } - -XCAT_DISKPOOL_KEYWORDS = { - "disk_total": "Total:", - "disk_used": "Used:", - "disk_available": "Free:", - } - -XCAT_RESPONSE_KEYS = ('info', 'data', 'node', 'errorcode', 'error') - -ZVM_POWER_STAT = { - 'on': power_state.RUNNING, - 'off': power_state.SHUTDOWN, - } - -ZVM_DEFAULT_ROOT_DISK = "dasda" -ZVM_DEFAULT_SECOND_DISK = "dasdb" -ZVM_DEFAULT_ROOT_VOLUME = "sda" -ZVM_DEFAULT_SECOND_VOLUME = "sdb" -ZVM_DEFAULT_THIRD_VOLUME = "sdc" -ZVM_DEFAULT_LAST_VOLUME = "sdz" - -DEFAULT_EPH_DISK_FMT = "ext3" -DISK_FUNC_NAME = "setupDisk" - -ZVM_DEFAULT_FCP_ID = 'auto' - -ZVM_IMAGE_SIZE_MAX = 10 - -# This is the version that our plugin can work ,if xcat version -# is lower than this version, we will warn user and prevent -# compute service to start up. -# Will bump this version time to time for each release. -XCAT_MINIMUM_VERSION = '2.8.3.16' - - -# It means we introduced 'version' concept at 2.8.3.7 -# later on, any new features especially backward incompatible -# change need a new version such as -# Support_xxxx = 'x.x.x.x', then we will compare whether -# The XCAT we are using has higher or lower version than x.x.x.x -# and do different things according to the version -# we might INFO in log if version is lower than XCAT_INIT_VERSION -XCAT_INIT_VERSION = '2.8.3.7' - -# From 2.8.3.7 version, openstack will only send 'nozip' format -# to xcat, so before that, a tgz format will be send and processed -# while >= this version, a nozip flag along with tar format is sent. -# xcat was bumped to this version at 2015.08.06, so version lower than -# it should use zip format instead. -XCAT_BUNDLE_USE_NOZIP = '2.8.3.7' - -XCAT_SUPPORT_CHVM_SMCLI_VERSION = '2.8.3.8' - -# e0a7bc3502e8635bf7a2954220a6406f563f3a1f added this -# support add --ipl as param to mkvm call -XCAT_MKVM_SUPPORT_IPL = '2.8.3.11' - -# I1a6ff024609e50f3a20a4bac2d86f94f7152e3cf xcat change added this -# rinv now can use 'cpumempowerstat' option -XCAT_RINV_SUPPORT_CPUMEMPOWERSTAT = '2.8.3.13' - -# xCAT version that supports collection of diagnostics for failed deployments -XCAT_SUPPORT_COLLECT_DIAGNOSTICS_DEPLOYFAILED = '2.8.3.16' - -# Reasons for collecting diagnostics. -# xCAT may adjust diagnostic contents based on the reason passed in. -DIAGNOSTICS_RSN_DEPLOYMENT_TIMEOUT = 'OpenStackDeploymentTimeout' - -# add IUCV support to replace ssh -XCAT_SUPPORT_IUCV = '2.8.3.15' diff --git a/nova_zvm/virt/zvm/dist.py b/nova_zvm/virt/zvm/dist.py deleted file mode 100644 index f973ca4..0000000 --- a/nova_zvm/virt/zvm/dist.py +++ /dev/null @@ -1,678 +0,0 @@ -# Copyright 2015 IBM Corp. -# -# 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. - -import abc - -from nova.i18n import _ -from oslo_config import cfg -from oslo_log import log as logging -import six - -from nova_zvm.virt.zvm import exception - -CONF = cfg.CONF - -LOG = logging.getLogger(__name__) - - -@six.add_metaclass(abc.ABCMeta) -class LinuxDist(object): - """Linux distribution base class - - Due to we need to interact with linux dist and inject different files - according to the dist version. Currently only RHEL and SLES are supported - """ - - def create_network_configuration_files(self, file_path, network_info, - base_vdev): - """Generate network configuration files to instance.""" - device_num = 0 - cfg_files = [] - cmd_strings = '' - udev_cfg_str = '' - dns_cfg_str = '' - route_cfg_str = '' - cmd_str = None - file_path = self._get_network_file_path() - file_name_route = file_path + 'routes' - - file_name_dns = self._get_dns_filename() - for vif in network_info: - file_name = self._get_device_filename(device_num) - network = vif['network'] - (cfg_str, cmd_str, dns_str, - route_str) = self._generate_network_configuration(network, - base_vdev, device_num) - target_net_conf_file_name = file_path + file_name - cfg_str_for_log = cfg_str.replace('\n', ' ') - LOG.debug('Network configure file[%(file)s] content is: %(cfg)s', - {'file': target_net_conf_file_name, - 'cfg': cfg_str_for_log}) - cfg_files.append((target_net_conf_file_name, cfg_str)) - udev_cfg_str += self._get_udev_configuration(device_num, - '0.0.' + str(base_vdev).zfill(4)) - self._append_udev_rules_file(cfg_files, base_vdev) - if cmd_str is not None: - cmd_strings += cmd_str - if len(dns_str) > 0: - dns_cfg_str += dns_str - if len(route_str) > 0: - route_cfg_str += route_str - base_vdev = str(hex(int(base_vdev, 16) + 3))[2:] - device_num += 1 - - if len(dns_cfg_str) > 0: - cfg_files.append((file_name_dns, dns_cfg_str)) - self._append_udev_info(cfg_files, file_name_route, route_cfg_str, - udev_cfg_str) - return cfg_files, cmd_strings - - def _generate_network_configuration(self, network, vdev, device_num): - ip_v4 = dns_str = '' - - subnets_v4 = [s for s in network['subnets'] if s['version'] == 4] - - if len(subnets_v4[0]['ips']) > 0: - ip_v4 = subnets_v4[0]['ips'][0]['address'] - if len(subnets_v4[0]['dns']) > 0: - for dns in subnets_v4[0]['dns']: - dns_str += 'nameserver ' + dns['address'] + '\n' - netmask_v4 = str(subnets_v4[0].as_netaddr().netmask) - gateway_v4 = subnets_v4[0]['gateway']['address'] or '' - broadcast_v4 = str(subnets_v4[0].as_netaddr().broadcast) - device = self._get_device_name(device_num) - address_read = str(vdev).zfill(4) - address_write = str(hex(int(vdev, 16) + 1))[2:].zfill(4) - address_data = str(hex(int(vdev, 16) + 2))[2:].zfill(4) - subchannels = '0.0.%s' % address_read.lower() - subchannels += ',0.0.%s' % address_write.lower() - subchannels += ',0.0.%s' % address_data.lower() - - cfg_str = self._get_cfg_str(device, broadcast_v4, gateway_v4, - ip_v4, netmask_v4, address_read, - subchannels) - cmd_str = self._get_cmd_str(address_read, address_write, - address_data) - route_str = self._get_route_str(gateway_v4) - - return cfg_str, cmd_str, dns_str, route_str - - def assemble_zfcp_srcdev(self, fcp, wwpn, lun): - path = '/dev/disk/by-path/ccw-0.0.%(fcp)s-zfcp-0x%(wwpn)s:0x%(lun)s' - srcdev = path % {'fcp': fcp, 'wwpn': wwpn, 'lun': lun} - return srcdev - - @abc.abstractmethod - def _get_network_file_path(self): - """Get network file configuration path.""" - pass - - def get_change_passwd_command(self, admin_password): - """construct change password command - - :admin_password: the password to be changed to - """ - return "echo 'root:%s' | chpasswd" % admin_password - - @abc.abstractmethod - def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4, - netmask_v4, address_read, subchannels): - """construct configuration file of network device.""" - pass - - @abc.abstractmethod - def _get_device_filename(self, device_num): - """construct the name of a network device file.""" - pass - - @abc.abstractmethod - def _get_route_str(self, gateway_v4): - """construct a router string.""" - pass - - @abc.abstractmethod - def _get_cmd_str(self, address_read, address_write, address_data): - """construct network startup command string.""" - pass - - @abc.abstractmethod - def _get_dns_filename(self): - """construct the name of dns file.""" - pass - - @abc.abstractmethod - def get_znetconfig_contents(self): - """construct znetconfig file will be called during first boot.""" - pass - - @abc.abstractmethod - def _get_device_name(self, device_num): - """construct the name of a network device.""" - pass - - @abc.abstractmethod - def _get_udev_configuration(self, device, dev_channel): - """construct udev configuration info.""" - pass - - @abc.abstractmethod - def _get_udev_rules(self, channel_read, channel_write, channel_data): - """construct udev rules info.""" - pass - - @abc.abstractmethod - def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str, - udev_cfg_str): - pass - - @abc.abstractmethod - def _append_udev_rules_file(self, cfg_files, base_vdev): - pass - - @abc.abstractmethod - def get_scp_string(self, root, fcp, wwpn, lun): - """construct scp_data string for ipl parameter""" - pass - - @abc.abstractmethod - def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun): - """construct the lines composing the script to generate - the /etc/zipl.conf file - """ - pass - - -class rhel(LinuxDist): - def _get_network_file_path(self): - return '/etc/sysconfig/network-scripts/' - - def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4, - netmask_v4, address_read, subchannels): - cfg_str = 'DEVICE=\"' + device + '\"\n' - cfg_str += 'BOOTPROTO=\"static\"\n' - cfg_str += 'BROADCAST=\"' + broadcast_v4 + '\"\n' - cfg_str += 'GATEWAY=\"' + gateway_v4 + '\"\n' - cfg_str += 'IPADDR=\"' + ip_v4 + '\"\n' - cfg_str += 'NETMASK=\"' + netmask_v4 + '\"\n' - cfg_str += 'NETTYPE=\"qeth\"\n' - cfg_str += 'ONBOOT=\"yes\"\n' - cfg_str += 'PORTNAME=\"PORT' + address_read + '\"\n' - cfg_str += 'OPTIONS=\"layer2=1\"\n' - cfg_str += 'SUBCHANNELS=\"' + subchannels + '\"\n' - return cfg_str - - def _get_route_str(self, gateway_v4): - return '' - - def _get_cmd_str(self, address_read, address_write, address_data): - return '' - - def _get_dns_filename(self): - return '/etc/resolv.conf' - - def _get_device_name(self, device_num): - return 'eth' + str(device_num) - - def _get_udev_configuration(self, device, dev_channel): - return '' - - def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str, - udev_cfg_str): - pass - - def _get_udev_rules(self, channel_read, channel_write, channel_data): - """construct udev rules info.""" - return '' - - def _append_udev_rules_file(self, cfg_files, base_vdev): - pass - - -class rhel6(rhel): - def get_znetconfig_contents(self): - return '\n'.join(('cio_ignore -R', - 'znetconf -R -n', - 'udevadm trigger', - 'udevadm settle', - 'sleep 2', - 'znetconf -A', - 'service network restart', - 'cio_ignore -u')) - - def _get_device_filename(self, device_num): - return 'ifcfg-eth' + str(device_num) - - def _get_device_name(self, device_num): - return 'eth' + str(device_num) - - def get_scp_string(self, root, fcp, wwpn, lun): - return ("=root=%(root)s selinux=0 zfcp.allow_lun_scan=0 " - "rd_ZFCP=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % { - 'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun} - - def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun): - return ['#!/bin/bash\n', - ('echo -e "[defaultboot]\\n' - 'timeout=5\\n' - 'default=boot-from-volume\\n' - 'target=/boot/\\n' - '[boot-from-volume]\\n' - 'image=%(image)s\\n' - 'ramdisk=%(ramdisk)s\\n' - 'parameters=\\"root=%(root)s ' - 'rd_ZFCP=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s ' - 'zfcp.allow_lun_scan=0 selinux=0\\""' - '>/etc/zipl_volume.conf\n' - 'zipl -c /etc/zipl_volume.conf') - % {'image': image, 'ramdisk': ramdisk, 'root': root, - 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}] - - -class rhel7(rhel): - def get_znetconfig_contents(self): - return '\n'.join(('cio_ignore -R', - 'znetconf -R -n', - 'udevadm trigger', - 'udevadm settle', - 'sleep 2', - 'znetconf -A', - 'cio_ignore -u')) - - def _get_device_filename(self, device_num): - # Construct a device like ifcfg-enccw0.0.1000, ifcfg-enccw0.0.1003 - base = int(CONF.zvm_default_nic_vdev, 16) - device = str(hex(base + device_num * 3))[2:] - return 'ifcfg-enccw0.0.' + str(device).zfill(4) - - def _get_device_name(self, device_num): - # Construct a device like enccw0.0.1000, enccw0.0.1003 - base = int(CONF.zvm_default_nic_vdev, 16) - device = str(hex(base + device_num * 3))[2:] - return 'enccw0.0.' + str(device).zfill(4) - - def get_scp_string(self, root, fcp, wwpn, lun): - return ("=root=%(root)s selinux=0 zfcp.allow_lun_scan=0 " - "rd.zfcp=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % { - 'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun} - - def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun): - return ['#!/bin/bash\n', - ('echo -e "[defaultboot]\\n' - 'timeout=5\\n' - 'default=boot-from-volume\\n' - 'target=/boot/\\n' - '[boot-from-volume]\\n' - 'image=%(image)s\\n' - 'ramdisk=%(ramdisk)s\\n' - 'parameters=\\"root=%(root)s ' - 'rd.zfcp=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s ' - 'zfcp.allow_lun_scan=0 selinux=0\\""' - '>/etc/zipl_volume.conf\n' - 'zipl -c /etc/zipl_volume.conf') - % {'image': image, 'ramdisk': ramdisk, 'root': root, - 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}] - - -class sles(LinuxDist): - def _get_network_file_path(self): - return '/etc/sysconfig/network/' - - def _get_cidr_from_ip_netmask(self, ip, netmask): - netmask_fields = netmask.split('.') - bin_str = '' - for octet in netmask_fields: - bin_str += bin(int(octet))[2:].zfill(8) - mask = str(len(bin_str.rstrip('0'))) - cidr_v4 = ip + '/' + mask - return cidr_v4 - - def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4, - netmask_v4, address_read, subchannels): - cidr_v4 = self._get_cidr_from_ip_netmask(ip_v4, netmask_v4) - cfg_str = "BOOTPROTO=\'static\'\n" - cfg_str += "IPADDR=\'%s\'\n" % cidr_v4 - cfg_str += "BROADCAST=\'%s\'\n" % broadcast_v4 - cfg_str += "STARTMODE=\'onboot\'\n" - cfg_str += ("NAME=\'OSA Express Network card (%s)\'\n" % - address_read) - return cfg_str - - def _get_route_str(self, gateway_v4): - route_str = 'default %s - -\n' % gateway_v4 - return route_str - - def _get_cmd_str(self, address_read, address_write, address_data): - cmd_str = 'qeth_configure -l 0.0.%s ' % address_read.lower() - cmd_str += '0.0.%(write)s 0.0.%(data)s 1\n' % {'write': - address_write.lower(), 'data': address_data.lower()} - cmd_str += ('echo "0.0.%(read)s,0.0.%(write)s,0.0.%(data)s #`date`"' - ' >>/boot/zipl/active_devices.txt\n' % {'read': - address_read.lower(), 'write': address_write.lower(), - 'data': address_data.lower()}) - return cmd_str - - def _get_dns_filename(self): - return '/etc/resolv.conf' - - def _get_device_filename(self, device_num): - return 'ifcfg-eth' + str(device_num) - - def _get_device_name(self, device_num): - return 'eth' + str(device_num) - - def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str, - udev_cfg_str): - udev_file_name = '/etc/udev/rules.d/70-persistent-net.rules' - cfg_files.append((udev_file_name, udev_cfg_str)) - if len(route_cfg_str) > 0: - cfg_files.append((file_name_route, route_cfg_str)) - - def _get_udev_configuration(self, device, dev_channel): - cfg_str = 'SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"qeth\",' - cfg_str += ' KERNELS==\"%s\", ATTR{type}==\"1\",' % dev_channel - cfg_str += ' KERNEL==\"eth*\", NAME=\"eth%s\"\n' % device - - return cfg_str - - def _append_udev_rules_file(self, cfg_files, base_vdev): - rules_file_name = '/etc/udev/rules.d/51-qeth-0.0.%s.rules' % base_vdev - read_ch = '0.0.' + base_vdev - write_ch = '0.0.' + str(hex(int(base_vdev, 16) + 1))[2:] - data_ch = '0.0.' + str(hex(int(base_vdev, 16) + 2))[2:] - udev_rules_str = self._get_udev_rules(read_ch, write_ch, data_ch) - cfg_files.append((rules_file_name, udev_rules_str)) - - def _get_udev_rules(self, channel_read, channel_write, channel_data): - """construct udev rules info.""" - sub_str = '%(read)s %%k %(read)s %(write)s %(data)s qeth' % { - 'read': channel_read, - 'read': channel_read, - 'write': channel_write, - 'data': channel_data} - rules_str = '# Configure qeth device at' - rules_str += ' %(read)s/%(write)s/%(data)s\n' % { - 'read': channel_read, - 'write': channel_write, - 'data': channel_data} - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==' - '\"qeth\", IMPORT{program}=\"collect %s\"\n') % sub_str - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"' - '%(read)s\", IMPORT{program}="collect %(channel)s\"\n') % { - 'read': channel_read, 'channel': sub_str} - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"' - '%(write)s\", IMPORT{program}=\"collect %(channel)s\"\n') % { - 'write': channel_write, 'channel': sub_str} - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"' - '%(data)s\", IMPORT{program}=\"collect %(channel)s\"\n') % { - 'data': channel_data, 'channel': sub_str} - rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"drivers\", KERNEL==\"' - 'qeth\", IMPORT{program}=\"collect --remove %s\"\n') % sub_str - rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"ccw\", KERNEL==\"' - '%(read)s\", IMPORT{program}=\"collect --remove %(channel)s\"\n' - ) % {'read': channel_read, 'channel': sub_str} - rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"ccw\", KERNEL==\"' - '%(write)s\", IMPORT{program}=\"collect --remove %(channel)s\"\n' - ) % {'write': channel_write, 'channel': sub_str} - rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"ccw\", KERNEL==\"' - '%(data)s\", IMPORT{program}=\"collect --remove %(channel)s\"\n' - ) % {'data': channel_data, 'channel': sub_str} - rules_str += ('TEST==\"[ccwgroup/%(read)s]\", GOTO=\"qeth-%(read)s' - '-end\"\n') % {'read': channel_read, 'read': channel_read} - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", ENV{COLLECT_' - '%(read)s}==\"0\", ATTR{[drivers/ccwgroup:qeth]group}=\"' - '%(read)s,%(write)s,%(data)s\"\n') % { - 'read': channel_read, 'read': channel_read, - 'write': channel_write, 'data': channel_data} - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"qeth' - '\", ENV{COLLECT_%(read)s}==\"0\", ATTR{[drivers/' - 'ccwgroup:qeth]group}=\"%(read)s,%(write)s,%(data)s\"\n' - 'LABEL=\"qeth-%(read)s-end\"\n') % { - 'read': channel_read, 'read': channel_read, 'write': channel_write, - 'data': channel_data, 'read': channel_read} - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccwgroup\", KERNEL==' - '\"%s\", ATTR{layer2}=\"1\"\n') % channel_read - rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccwgroup\", KERNEL==' - '\"%s\", ATTR{online}=\"1\"\n') % channel_read - return rules_str - - def get_scp_string(self, root, fcp, wwpn, lun): - return ("=root=%(root)s zfcp.allow_lun_scan=0 " - "zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % { - 'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun} - - def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun): - return ['#!/bin/bash\n', - ('echo -e "[defaultboot]\\n' - 'default=boot-from-volume\\n' - '[boot-from-volume]\\n' - 'image=%(image)s\\n' - 'target = /boot/zipl\\n' - 'ramdisk=%(ramdisk)s\\n' - 'parameters=\\"root=%(root)s ' - 'zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s ' - 'zfcp.allow_lun_scan=0\\""' - '>/etc/zipl_volume.conf\n' - 'mkinitrd\n' - 'zipl -c /etc/zipl_volume.conf') - % {'image': image, 'ramdisk': ramdisk, 'root': root, - 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}] - - -class sles11(sles): - def get_znetconfig_contents(self): - return '\n'.join(('cio_ignore -R', - 'znetconf -R -n', - 'sleep 2', - 'udevadm trigger', - 'udevadm settle', - 'sleep 2', - 'znetconf -A', - 'service network restart', - 'cio_ignore -u')) - - -class sles12(sles): - def get_znetconfig_contents(self): - remove_route = 'rm -f %s/ifroute-eth*' % self._get_network_file_path() - return '\n'.join(('cio_ignore -R', - 'znetconf -R -n', - 'sleep 2', - remove_route, - 'udevadm trigger', - 'udevadm settle', - 'sleep 2', - 'znetconf -A', - 'cio_ignore -u')) - - -class ubuntu(LinuxDist): - def create_network_configuration_files(self, file_path, network_info, - base_vdev): - """Generate network configuration files to instance.""" - cfg_files = [] - cmd_strings = '' - network_config_file_name = self._get_network_file() - network_cfg_str = 'auto lo\n' - network_cfg_str += 'iface lo inet loopback\n' - - for vif in network_info: - network_hw_config_fname = self._get_device_filename(base_vdev) - network_hw_config_str = self._get_network_hw_config_str(base_vdev) - cfg_files.append((network_hw_config_fname, network_hw_config_str)) - network = vif['network'] - (cfg_str, dns_str) = self._generate_network_configuration(network, - base_vdev) - LOG.debug('Network configure file content is: %s', cfg_str) - network_cfg_str += cfg_str - if len(dns_str) > 0: - network_cfg_str += dns_str - base_vdev = str(hex(int(base_vdev, 16) + 3))[2:] - cfg_files.append((network_config_file_name, network_cfg_str)) - return cfg_files, cmd_strings - - def _get_network_file(self): - return '/etc/network/interfaces' - - def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4, - netmask_v4): - cfg_str = 'auto ' + device + '\n' - cfg_str += 'iface ' + device + ' inet static\n' - cfg_str += 'address ' + ip_v4 + '\n' - cfg_str += 'netmask ' + netmask_v4 + '\n' - cfg_str += 'broadcast ' + broadcast_v4 + '\n' - cfg_str += 'gateway ' + gateway_v4 + '\n' - return cfg_str - - def _generate_network_configuration(self, network, vdev): - ip_v4 = dns_str = '' - subnets_v4 = [s for s in network['subnets'] if s['version'] == 4] - - if len(subnets_v4[0]['ips']) > 0: - ip_v4 = subnets_v4[0]['ips'][0]['address'] - if len(subnets_v4[0]['dns']) > 0: - for dns in subnets_v4[0]['dns']: - dns_str += 'dns-nameservers ' + dns['address'] + '\n' - netmask_v4 = str(subnets_v4[0].as_netaddr().netmask) - gateway_v4 = subnets_v4[0]['gateway']['address'] or '' - broadcast_v4 = str(subnets_v4[0].as_netaddr().broadcast) - device = self._get_device_name(vdev) - cfg_str = self._get_cfg_str(device, broadcast_v4, gateway_v4, - ip_v4, netmask_v4) - return cfg_str, dns_str - - def _get_route_str(self, gateway_v4): - return '' - - def _get_cmd_str(self, address_read, address_write, address_data): - return '' - - def _get_device_name(self, device_num): - return 'enc' + str(device_num) - - def _get_dns_filename(self): - return '' - - def _get_device_filename(self, device_num): - return '/etc/sysconfig/hardware/config-ccw-0.0.' + str(device_num) - - def _get_network_hw_config_str(self, base_vdev): - ccwgroup_chans_str = ' '.join(( - '0.0.' + str(hex(int(base_vdev, 16)))[2:], - '0.0.' + str(hex(int(base_vdev, 16) + 1))[2:], - '0.0.' + str(hex(int(base_vdev, 16) + 2))[2:])) - return '\n'.join(('CCWGROUP_CHANS=(' + ccwgroup_chans_str + ')', - 'QETH_OPTIONS=layer2')) - - def _get_network_file_path(self): - pass - - def get_znetconfig_contents(self): - return '\n'.join(('cio_ignore -R', - 'znetconf -R -n', - 'sleep 2', - 'udevadm trigger', - 'udevadm settle', - 'sleep 2', - 'znetconf -A', - '/etc/init.d/networking restart', - 'cio_ignore -u')) - - def _get_udev_configuration(self, device, dev_channel): - return '' - - def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str, - udev_cfg_str): - pass - - def get_scp_string(self, root, fcp, wwpn, lun): - # Although the content is the same as SLES, I don't want to merge them. - # Because they are NOT logically related, and it's very likely that - # they evolve separately in the future. - return ("=root=%(root)s zfcp.allow_lun_scan=0 " - "zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % { - 'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun} - - def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun): - return ['#!/bin/bash\n', - ('echo -e "[defaultboot]\\n' - 'default=boot-from-volume\\n' - '[boot-from-volume]\\n' - 'image=%(image)s\\n' - 'target = /boot/zipl\\n' - 'ramdisk=%(ramdisk)s\\n' - 'parameters=\\"root=%(root)s ' - 'zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s ' - 'zfcp.allow_lun_scan=0\\""' - '>/etc/zipl_volume.conf\n' - 'mkinitrd\n' - 'zipl -c /etc/zipl_volume.conf') - % {'image': image, 'ramdisk': ramdisk, 'root': root, - 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}] - - def assemble_zfcp_srcdev(self, fcp, wwpn, lun): - path = '/dev/disk/by-path/ccw-0.0.%(fcp)s-fc-0x%(wwpn)s-lun-%(lun)s' - lun = int(lun[0:4], 16) - srcdev = path % {'fcp': fcp, 'wwpn': wwpn, 'lun': lun} - return srcdev - - def _get_udev_rules(self, channel_read, channel_write, channel_data): - """construct udev rules info.""" - return '' - - def _append_udev_rules_file(self, cfg_files, base_vdev): - pass - - -class ubuntu16(ubuntu): - pass - - -class ListDistManager(object): - def get_linux_dist(self, os_version): - distro, release = self.parse_dist(os_version) - return globals()[distro + release] - - def _parse_release(self, os_version, distro, remain): - supported = {'rhel': ['6', '7'], - 'sles': ['11', '12'], - 'ubuntu': ['16']} - releases = supported[distro] - - for r in releases: - if remain.startswith(r): - return r - else: - msg = _('Can not handle os: %s') % os_version - raise exception.ZVMImageError(msg=msg) - - def parse_dist(self, os_version): - """Separate os and version from os_version. - - Possible return value are only: - ('rhel', x.y) and ('sles', x.y) where x.y may not be digits - """ - supported = {'rhel': ['rhel', 'redhat', 'red hat'], - 'sles': ['suse', 'sles'], - 'ubuntu': ['ubuntu']} - os_version = os_version.lower() - for distro, patterns in supported.items(): - for i in patterns: - if os_version.startswith(i): - # Not guarrentee the version is digital - remain = os_version.split(i, 2)[1] - release = self._parse_release(os_version, distro, remain) - return distro, release - - msg = _('Can not handle os: %s') % os_version - raise exception.ZVMImageError(msg=msg) diff --git a/nova_zvm/virt/zvm/driver.py b/nova_zvm/virt/zvm/driver.py index d47f74a..871f08a 100644 --- a/nova_zvm/virt/zvm/driver.py +++ b/nova_zvm/virt/zvm/driver.py @@ -1,4 +1,4 @@ -# Copyright 2013 IBM Corp. +# Copyright 2017,2018 IBM Corp. # # 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 @@ -12,900 +12,334 @@ # License for the specific language governing permissions and limitations # under the License. -import contextlib -import datetime import eventlet -import itertools -import operator import os import six import time -from nova.api.metadata import base as instance_metadata -from nova.compute import power_state -from nova.compute import task_states -from nova.compute import utils as compute_utils -from nova.compute import vm_states -from nova import exception as nova_exception -from nova.i18n import _ -from nova.image import api as image_api -from nova.image import glance -from nova.objects import fields -from nova import utils -from nova.virt import configdrive -from nova.virt import driver -from nova.volume import cinder +from oslo_concurrency import lockutils from oslo_log import log as logging from oslo_serialization import jsonutils -from oslo_service import loopingcall from oslo_utils import excutils -from oslo_utils import timeutils -from oslo_utils import units -from oslo_utils import uuidutils -from oslo_utils import versionutils +from nova.compute import task_states +from nova import exception +from nova.i18n import _ +from nova.image import glance +from nova.objects import fields as obj_fields +from nova import utils +from nova.virt import driver +from nova.virt import images from nova_zvm.virt.zvm import conf -from nova_zvm.virt.zvm import configdrive as zvmconfigdrive -from nova_zvm.virt.zvm import const -from nova_zvm.virt.zvm import dist -from nova_zvm.virt.zvm import exception -from nova_zvm.virt.zvm import imageop -from nova_zvm.virt.zvm import instance as zvminstance -from nova_zvm.virt.zvm import networkop +from nova_zvm.virt.zvm import exception as zvm_exception +from nova_zvm.virt.zvm import guest +from nova_zvm.virt.zvm import hypervisor from nova_zvm.virt.zvm import utils as zvmutils -from nova_zvm.virt.zvm import volumeop LOG = logging.getLogger(__name__) - CONF = conf.CONF -CONF.import_opt('host', 'nova.conf') -CONF.import_opt('my_ip', 'nova.conf') -ZVMInstance = zvminstance.ZVMInstance + +DEFAULT_EPH_DISK_FMT = 'ext3' class ZVMDriver(driver.ComputeDriver): """z/VM implementation of ComputeDriver.""" - capabilities = { - "has_imagecache": True, - "supports_recreate": False, - "supports_migrate_to_same_host": True, - "supports_attach_interface": False - } - def __init__(self, virtapi): super(ZVMDriver, self).__init__(virtapi) - self._xcat_url = zvmutils.get_xcat_url() - # incremental sleep interval list - _inc_slp = [5, 10, 20, 30, 60] - _slp = 5 + self._virtapi = virtapi + self._validate_options() - # TODO(jichenjc): update _xcat_version when xcat reboot - self._xcat_version = self._get_xcat_version() - version_ok = self.has_min_version(const.XCAT_MINIMUM_VERSION) - while (not version_ok): - LOG.warning(_("WARNING: the xcat version communicating with is " - "%(xcat_version)s, but the minimum requested " - "version by openstack zvm driver is %(minimum)s " - "will sleep some time and check again"), - {'xcat_version': self._xcat_version, - 'minimum': const.XCAT_MINIMUM_VERSION}) - self._xcat_version = self._get_xcat_version() - version_ok = self.has_min_version(const.XCAT_MINIMUM_VERSION) + self._hypervisor = hypervisor.Hypervisor( + CONF.zvm.cloud_connector_url, ca_file=CONF.zvm.ca_file) - _slp = len(_inc_slp) != 0 and _inc_slp.pop(0) or _slp - time.sleep(_slp) + LOG.info("The zVM compute driver has been initialized.") - self._host_stats = [] - _slp = 5 + @staticmethod + def _validate_options(): + if not CONF.zvm.cloud_connector_url: + error = _('Must specify cloud_connector_url in zvm config ' + 'group to use compute_driver=zvm.driver.ZVMDriver') + raise zvm_exception.ZVMDriverException(error=error) - while (self._host_stats == []): - try: - self._host_stats = self.update_host_status() - except Exception as e: - # Ignore any exceptions and log as warning - _slp = len(_inc_slp) != 0 and _inc_slp.pop(0) or _slp - msg = _("Failed to get host stats while initializing zVM " - "driver due to reason %(reason)s, will re-try in " - "%(slp)d seconds") - LOG.warning(msg, {'reason': six.text_type(e), - 'slp': _slp}) - time.sleep(_slp) + # Try a test to ensure length of give guest is smaller than 8 + try: + _test_instance = CONF.instance_name_template % 0 + except Exception: + msg = _("Template is not usable, the template defined is " + "instance_name_template=%s") % CONF.instance_name_template + raise zvm_exception.ZVMDriverException(error=msg) - self._networkop = networkop.NetworkOperator() - self._zvm_images = imageop.ZVMImages() - self._pathutils = zvmutils.PathUtils() - self._networkutils = zvmutils.NetworkUtils() - self._volumeop = volumeop.VolumeOperator() - self._volume_api = cinder.API() - self._dist_manager = dist.ListDistManager() - self._image_api = image_api.API() - - # booting multi instances at sametime will failed because we will - # import image to nova if root_disk_units not set,so multi threads - # might work on same image and the lastest one will overwrite the - # former ones' image data.The former ones can't find the data any more - # so add this semaphore to synchronize between those threads - self._imageop_semaphore = eventlet.semaphore.Semaphore(1) + # For zVM instance, limit the maximum length of instance name to 8 + if len(_test_instance) > 8: + msg = _("Can't spawn instance with template '%s', " + "The zVM hypervisor does not support instance names " + "longer than 8 characters. Please change your config of " + "instance_name_template.") % CONF.instance_name_template + raise zvm_exception.ZVMDriverException(error=msg) def init_host(self, host): - """Initialize anything that is necessary for the driver to function, - including catching up with currently running VM's on the given host. - """ - try: - self._volumeop.init_host(self._host_stats) - except Exception as e: - emsg = zvmutils.format_exception_msg(e) - LOG.warning(_("Exception raised while initializing z/VM driver: " - "%s"), emsg) - - def get_info(self, instance): - """Get the current status of an instance, by name (not ID!) - - Returns a dict containing: - :state: the running state, one of the power_state codes - :max_mem: (int) the maximum memory in KBytes allowed - :mem: (int) the memory in KBytes used by the domain - :num_cpu: (int) the number of virtual CPUs for the domain - :cpu_time: (int) the CPU time used in nanoseconds - - """ - inst_name = instance['name'] - zvm_inst = ZVMInstance(self, instance) - - try: - return zvm_inst.get_info() - except exception.ZVMXCATRequestFailed as err: - emsg = err.format_message() - if (emsg.__contains__("Invalid nodes and/or groups") and - emsg.__contains__("Forbidden")): - LOG.warning(_("z/VM instance %s does not exist"), inst_name, - instance=instance) - raise nova_exception.InstanceNotFound(instance_id=inst_name) - else: - raise err + pass def list_instances(self): - """Return the names of all the instances known to the virtualization - layer, as a list. - """ - zvm_host = CONF.zvm_host - hcp_base = self._get_hcp_info()['hostname'] - - url = self._xcat_url.tabdump("/zvm") - res_dict = zvmutils.xcat_request("GET", url) - - instances = [] - - with zvmutils.expect_invalid_xcat_resp_data(res_dict): - data_entries = res_dict['data'][0][1:] - for data in data_entries: - l = data.split(",") - node, hcp = l[0].strip("\""), l[1].strip("\"") - hcp_short = hcp_base.partition('.')[0] - - # zvm host and zhcp are not included in the list - if (hcp.upper() == hcp_base.upper() and - node.upper() not in (zvm_host.upper(), - hcp_short.upper(), CONF.zvm_xcat_master.upper())): - instances.append(node) - - return instances - - def _instance_exists(self, instance_name): - """Overwrite this to using instance name as input parameter.""" - return instance_name in self.list_instances() + return self._hypervisor.list_names() def instance_exists(self, instance): - """Overwrite this to using instance name as input parameter.""" - return self._instance_exists(instance.name) + # z/VM driver returns name in upper case and because userid is + # stored instead of uuid, list_instance_uuids is not implemented + return instance.name.upper() in self.list_instances() + + def get_available_resource(self, nodename=None): + host_stats = self._hypervisor.get_available_resource() + + hypervisor_hostname = self._hypervisor.get_available_nodes()[0] + res = { + 'vcpus': host_stats.get('vcpus', 0), + 'memory_mb': host_stats.get('memory_mb', 0), + 'local_gb': host_stats.get('disk_total', 0), + 'vcpus_used': host_stats.get('vcpus_used', 0), + 'memory_mb_used': host_stats.get('memory_mb_used', 0), + 'local_gb_used': host_stats.get('disk_used', 0), + 'hypervisor_type': host_stats.get('hypervisor_type', + obj_fields.HVType.ZVM), + 'hypervisor_version': host_stats.get('hypervisor_version', ''), + 'hypervisor_hostname': host_stats.get('hypervisor_hostname', + hypervisor_hostname), + 'cpu_info': jsonutils.dumps(host_stats.get('cpu_info', {})), + 'disk_available_least': host_stats.get('disk_available', 0), + 'supported_instances': [(obj_fields.Architecture.S390X, + obj_fields.HVType.ZVM, + obj_fields.VMMode.HVM)], + 'numa_topology': None, + } + + LOG.debug("Getting available resource for %(host)s:%(nodename)s", + {'host': CONF.host, 'nodename': nodename}) + + return res + + def get_available_nodes(self, refresh=False): + return self._hypervisor.get_available_nodes(refresh=refresh) + + def get_info(self, instance): + _guest = guest.Guest(self._hypervisor, instance) + return _guest.get_info() def spawn(self, context, instance, image_meta, injected_files, - admin_password, network_info=None, block_device_info=None, - flavor=None): - """Create a new instance/VM/domain on the virtualization platform. + admin_password, allocations, network_info=None, + block_device_info=None): - Once this successfully completes, the instance should be - running (power_state.RUNNING). + LOG.info("Spawning new instance %s on zVM hypervisor", + instance.name, instance=instance) - If this fails, any partial instance should be completely - cleaned up, and the virtualization platform should be in the state - that it was before this call began. + if self._hypervisor.guest_exists(instance): + raise exception.InstanceExists(name=instance.name) - :param context: security context - :param instance: Instance object as returned by DB layer. - This function should use the data there to guide - the creation of the new instance. - :param image_meta: image object returned by nova.image.glance that - defines the image from which to boot this instance - :param injected_files: User files to inject into instance. - :param admin_password: Administrator password to set in instance. - :param network_info: - :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` - :param block_device_info: Information about block devices to be - attached to the instance. - - """ - # For zVM instance, limit the maximum length of instance name to be 8 - if len(instance['name']) > 8: - msg = (_("Don't support spawn vm on zVM hypervisor with instance " - "name: %s, please change your instance_name_template to make " - "sure the length of instance name is not longer than 8 " - "characters") % instance['name']) - raise nova_exception.InvalidInput(reason=msg) - - root_mount_device, boot_from_volume = zvmutils.is_boot_from_volume( - block_device_info) - bdm = driver.block_device_info_get_mapping(block_device_info) - - # Update the root device name in instance table - root_device_name = '/dev/' + const.ZVM_DEFAULT_ROOT_DISK - if boot_from_volume: - root_device_name = root_mount_device - instance.root_device_name = root_device_name - instance.save() - - if not network_info: - msg = _("Not support boot without a NIC.") - raise exception.ZVMDriverError(msg=msg) - - # Use image_type to distinguish the image that is for normal deploy - # and volume snapshot image - image_type = '' - # Ensure the used image is a valid zVM image - if not boot_from_volume: - # This is because commit fbe31e461ac3f16edb795993558a2314b4c16b52 - # changes the image_meta from dict to object, we have several - # unique property can't be handled well - # see bug 1537921 for detail info - image_meta = self._image_api.get(context, image_meta.id) - self._zvm_images.zimage_check(image_meta) - if 'image_comments' not in image_meta['properties']: - image_type = 'xcatconf4z' - else: - image_type = 'cloudimg' - else: - image_type = 'volume-snapshot' - - compute_node = CONF.zvm_host - hcp_info = self._get_hcp_info() - zhcp = hcp_info['hostname'] - zhcp_userid = hcp_info['userid'] - - zvm_inst = ZVMInstance(self, instance) - instance_path = self._pathutils.get_instance_path(compute_node, - zvm_inst._name) - # Create network configuration files - LOG.debug('Creating network configuration files ' - 'for instance: %s', zvm_inst._name, instance=instance) - base_nic_vdev = CONF.zvm_default_nic_vdev - - if not boot_from_volume: - os_version = image_meta['properties']['os_version'] - else: - volume_id = self._extract_volume_id(bdm, root_mount_device) - volume_summery = self._volume_api.get(context, volume_id) - volume_meta = volume_summery['volume_metadata'] - os_version = volume_meta['os_version'] - instance.system_metadata['image_os_version'] = os_version - instance.save() - - linuxdist = self._dist_manager.get_linux_dist(os_version)() - files_and_cmds = linuxdist.create_network_configuration_files( - instance_path, network_info, base_nic_vdev) - (net_conf_files, net_conf_cmds) = files_and_cmds - # Add network configure files to inject_files - if len(net_conf_files) > 0: - injected_files.extend(net_conf_files) - - # Create configure drive - if not CONF.zvm_config_drive_inject_password: - admin_password = CONF.zvm_image_default_password - transportfiles = None - if configdrive.required_by(instance): - transportfiles = self._create_config_drive(context, instance_path, - instance, injected_files, admin_password, - net_conf_cmds, linuxdist, image_type) - - LOG.info(_("The instance %(name)s is spawning at %(node)s"), - {'name': zvm_inst._name, 'node': compute_node}, - instance=instance) - - spawn_start = time.time() + os_distro = image_meta.properties.get('os_distro') + if os_distro is None or len(os_distro) == 0: + reason = _("The `os_distro` image metadata property is required") + raise exception.InvalidInput(reason=reason) try: - deploy_image_name = None - if not boot_from_volume: - tmp_file_fn = None - bundle_file_path = None + spawn_start = time.time() - with self._imageop_semaphore: - root_disk_units = image_meta['properties'].get( - 'root_disk_units', '') - # Currently, disk unit values have the form number:units, - # for example: '3338:CYL'. Images captured using older - # versions of the driver may lack the colon delimiter and - # units. If the unit-less form is found, convert it to the - # new form by adding the units. - if ':' not in root_disk_units: - (tmp_file_fn, image_file_path, - bundle_file_path) = self._import_image_to_nova( - context, - instance, image_meta) - image_meta = self._zvm_images.\ - set_image_root_disk_units( - context, image_meta, image_file_path) - root_disk_units = image_meta['properties'][ - 'root_disk_units'] - disk_units = root_disk_units.split(":")[1] - if ((disk_units == "CYL" and CONF.zvm_diskpool_type == "FBA") - or (disk_units == "BLK" and - CONF.zvm_diskpool_type == "ECKD")): - msg = (_("The image's disk size units is: %(diskunits)s," - " it doesn't match the specified disk type" - " %(disktype)s in nova.conf."), - {'diskunits': disk_units, - 'disktype': CONF.zvm_diskpool_type}) - raise exception.ZVMImageError(msg=msg) + transportfiles = zvmutils.generate_configdrive(context, + instance, injected_files, network_info, + admin_password) - image_in_xcat = self._zvm_images.image_exist_xcat( - instance['image_ref']) - if not image_in_xcat: - self._import_image_to_xcat(context, instance, image_meta, - tmp_file_fn) - elif bundle_file_path is not None: - self._pathutils.clean_temp_folder(bundle_file_path) + spawn_image_name = self._get_image_info(context, image_meta.id, + os_distro) + disk_list, eph_list = self._set_disk_list(instance, + spawn_image_name, + block_device_info) - deploy_image_name = self._zvm_images.get_imgname_xcat( - instance['image_ref']) + # Create the guest vm + self._hypervisor.guest_create(instance.name, + instance.vcpus, instance.memory_mb, + disk_list) - # Create xCAT node and userid for the instance - zvm_inst.create_xcat_node(zhcp) - zvm_inst.create_userid(block_device_info, image_meta, context, - deploy_image_name) + # Deploy image to the guest vm + self._hypervisor.guest_deploy(instance.name, + spawn_image_name, transportfiles=transportfiles) + # Handle ephemeral disks + if eph_list: + self._hypervisor.guest_config_minidisks(instance.name, + eph_list) # Setup network for z/VM instance - self._preset_instance_network(zvm_inst._name, network_info) - self._add_nic_to_table(zvm_inst._name, network_info) + self._wait_vif_plug_events(instance.name, os_distro, + network_info, instance) - # Call nodeset restapi to deploy image on node - if not boot_from_volume: - zvm_inst.update_node_info(image_meta) - zvm_inst.deploy_node(deploy_image_name, transportfiles) - else: - zvmutils.punch_configdrive_file(transportfiles, zvm_inst._name) - - if image_type in ['xcatconf4z', 'volume-snapshot']: - # Change vm's admin password during spawn - zvmutils.punch_adminpass_file(instance_path, zvm_inst._name, - admin_password, linuxdist) - if zvmutils.xcat_support_iucv(self._xcat_version): - # Punch IUCV server files to reader. - zvmutils.punch_iucv_file(os_version, zhcp, zhcp_userid, - zvm_inst._name, instance_path) - - # punch ephemeral disk info to the instance - if instance['ephemeral_gb'] != 0: - eph_disks = block_device_info.get('ephemerals', []) - if eph_disks == []: - zvmutils.process_eph_disk(zvm_inst._name) - else: - for idx, eph in enumerate(eph_disks): - vdev = zvmutils.generate_eph_vdev(idx) - fmt = eph.get('guest_format') - mount_dir = ''.join([CONF.zvm_default_ephemeral_mntdir, - str(idx)]) - zvmutils.process_eph_disk(zvm_inst._name, vdev, fmt, - mount_dir) - - # Wait neutron zvm-agent add NIC information to user direct. - self._wait_and_get_nic_direct(zvm_inst._name, instance) - - # Attach persistent volume, exclude root volume - bdm_attach = list(bdm) - bdm_attach = self._exclude_root_volume_bdm(bdm_attach, - root_mount_device) - self._attach_volume_to_instance(context, instance, bdm_attach) - - # 1. Prepare for booting from volume - # 2. Write the zipl.conf file and issue zipl - if boot_from_volume: - (lun, wwpn, size, fcp) = zvm_inst.prepare_volume_boot(context, - instance, bdm, root_mount_device, - volume_meta) - zvmutils.punch_zipl_file(instance_path, zvm_inst._name, - lun, wwpn, fcp, volume_meta) - - # Power on the instance, then put MN's public key into instance - zvm_inst.power_on() + self._hypervisor.guest_start(instance.name) spawn_time = time.time() - spawn_start - LOG.info(_("Instance spawned succeeded in %s seconds"), + LOG.info("Instance spawned successfully in %s seconds", spawn_time, instance=instance) - except (exception.ZVMXCATCreateNodeFailed, - exception.ZVMImageError): - with excutils.save_and_reraise_exception(): - zvm_inst.delete_xcat_node() - except (exception.ZVMXCATCreateUserIdFailed, - exception.ZVMNetworkError, - exception.ZVMVolumeError, - exception.ZVMXCATUpdateNodeFailed, - exception.ZVMXCATDeployNodeFailed): - with excutils.save_and_reraise_exception(): - self.destroy(context, instance, network_info, - block_device_info) except Exception as err: - # Just a error log then re-raise with excutils.save_and_reraise_exception(): - LOG.error(_("Deploy image to instance %(instance)s " - "failed with reason: %(err)s"), - {'instance': zvm_inst._name, 'err': err}, + LOG.error("Deploy instance %(instance)s " + "failed with reason: %(err)s", + {'instance': instance.name, 'err': err}, instance=instance) - finally: - self._pathutils.clean_temp_folder(instance_path) + try: + self.destroy(context, instance, network_info, + block_device_info) + except Exception as err: + LOG.exception("Failed to destroy instance", + instance=instance) - # Update image last deploy date in xCAT osimage table - if not boot_from_volume: - self._zvm_images.update_last_use_date(deploy_image_name) - - def _create_config_drive(self, context, instance_path, instance, - injected_files, admin_password, commands, - linuxdist, image_type=''): - if CONF.config_drive_format not in ['tgz', 'iso9660']: - msg = (_("Invalid config drive format %s") % - CONF.config_drive_format) - raise exception.ZVMConfigDriveError(msg=msg) - - LOG.debug('Using config drive', instance=instance) - - extra_md = {} - if CONF.zvm_config_drive_inject_password: - extra_md['admin_pass'] = admin_password - - udev_settle = '' - if image_type in ['xcatconf4z', 'volume-snapshot']: - udev_settle = linuxdist.get_znetconfig_contents() - if udev_settle: - if len(commands) == 0: - znetconfig = '\n'.join(('#!/bin/bash', udev_settle)) - else: - znetconfig = '\n'.join(('#!/bin/bash', commands, udev_settle)) - znetconfig += '\nrm -rf /tmp/znetconfig.sh\n' - # Create a temp file in instance to execute above commands - net_cmd_file = [] - net_cmd_file.append(('/tmp/znetconfig.sh', znetconfig)) # nosec - injected_files.extend(net_cmd_file) - # injected_files.extend(('/tmp/znetconfig.sh', znetconfig)) - - inst_md = instance_metadata.InstanceMetadata(instance, - content=injected_files, - extra_md=extra_md, - request_context=context) - # network_metadata will prevent the hostname of the instance from - # being set correctly, so clean the value - inst_md.network_metadata = None - - configdrive_tgz = os.path.join(instance_path, 'cfgdrive.tgz') - - LOG.debug('Creating config drive at %s', configdrive_tgz, - instance=instance) - with zvmconfigdrive.ZVMConfigDriveBuilder(instance_md=inst_md) as cdb: - cdb.make_drive(configdrive_tgz) - - return configdrive_tgz - - def _preset_instance_network(self, instance_name, network_info): - self._networkop.config_xcat_mac(instance_name) - LOG.debug("Add ip/host name on xCAT MN for instance %s", - instance_name) + @lockutils.synchronized('IMAGE_INFO_SEMAPHORE') + def _get_image_info(self, context, image_meta_id, os_distro): try: - network = network_info[0]['network'] - ip_addr = network['subnets'][0]['ips'][0]['address'] - except Exception: - if network_info: - msg = _("Invalid network info: %s") % str(network_info) - else: - msg = _("Network info is Empty") - raise exception.ZVMNetworkError(msg=msg) + res = self._hypervisor.image_query(imagename=image_meta_id) + except zvm_exception.ZVMConnectorError as err: + with excutils.save_and_reraise_exception() as sare: + if err.overallRC == 404: + sare.reraise = False + self._import_spawn_image(context, image_meta_id, os_distro) - self._networkop.add_xcat_host(instance_name, ip_addr, instance_name) - self._networkop.makehosts() + res = self._hypervisor.image_query(imagename=image_meta_id) - def _import_image_to_nova(self, context, instance, image_meta): - image_file_name = image_meta['properties']['image_file_name'] - disk_file = ''.join(j for j in image_file_name.split(".img")[0] - if j.isalnum()) + ".img" - tmp_file_fn = self._pathutils.make_time_stamp() - bundle_file_path = self._pathutils.get_bundle_tmp_path(tmp_file_fn) - image_file_path = self._pathutils.get_img_path( - bundle_file_path, disk_file) + return res[0]['imagename'] - LOG.debug("Downloading the image %s from glance to nova compute " - "server", image_meta['id'], instance=instance) - self._zvm_images.fetch_image(context, - image_meta['id'], - image_file_path, - instance['user_id'], - instance['project_id']) - return (tmp_file_fn, image_file_path, bundle_file_path) - - def _import_image_to_xcat(self, context, instance, image_meta, tmp_f_fn): - # Format the image name and image disk file in case user named them - # with special characters - image_name = ''.join(i for i in image_meta['name'] if i.isalnum()) - spawn_path = self._pathutils.get_spawn_folder() - image_file_name = image_meta['properties']['image_file_name'] - disk_file = ''.join(j for j in image_file_name.split(".img")[0] - if j.isalnum()) + ".img" - if tmp_f_fn is None: - tmp_f_fn = self._pathutils.make_time_stamp() - bundle_file_path = self._pathutils.get_bundle_tmp_path(tmp_f_fn) - image_file_path = self._pathutils.get_img_path( - bundle_file_path, disk_file) - LOG.debug("Downloading the image %s from glance to nova compute " - "server", image_meta['id'], instance=instance) - self._zvm_images.fetch_image(context, - image_meta['id'], - image_file_path, - instance['user_id'], - instance['project_id']) + def _set_disk_list(self, instance, image_name, block_device_info): + if instance.root_gb == 0: + root_disk_size = self._hypervisor.image_get_root_disk_size( + image_name) else: - bundle_file_path = self._pathutils.get_bundle_tmp_path(tmp_f_fn) - image_file_path = self._pathutils.get_img_path( - bundle_file_path, disk_file) + root_disk_size = '%ig' % instance.root_gb - LOG.debug("Generating the manifest.xml as a part of bundle file for " - "image %s", image_meta['id'], instance=instance) + disk_list = [] + root_disk = {'size': root_disk_size, + 'is_boot_disk': True + } + disk_list.append(root_disk) + ephemeral_disks_info = driver.block_device_info_get_ephemerals( + block_device_info) - image_name = zvmutils.remove_prefix_of_unicode(image_name) + eph_list = [] + for eph in ephemeral_disks_info: + eph_dict = {'size': '%ig' % eph['size'], + 'format': (CONF.default_ephemeral_format or + DEFAULT_EPH_DISK_FMT)} + eph_list.append(eph_dict) - self._zvm_images.generate_manifest_file(image_meta, image_name, - disk_file, bundle_file_path) + if eph_list: + disk_list.extend(eph_list) + return disk_list, eph_list - LOG.debug("Generating bundle file for image %s", image_meta['id'], - instance=instance) - image_bundle_package = self._zvm_images.generate_image_bundle( - spawn_path, tmp_f_fn, image_name) + def _setup_network(self, vm_name, os_distro, network_info, instance): + LOG.debug("Creating NICs for vm %s", vm_name) + inst_nets = [] + for vif in network_info: + subnet = vif['network']['subnets'][0] + _net = {'ip_addr': subnet['ips'][0]['address'], + 'gateway_addr': subnet['gateway']['address'], + 'cidr': subnet['cidr'], + 'mac_addr': vif['address'], + 'nic_id': vif['id']} + inst_nets.append(_net) - LOG.debug("Importing the image %s to xCAT", image_meta['id'], - instance=instance) - profile_str = image_name, instance['image_ref'].replace('-', '_') - image_profile = '_'.join(profile_str) - self._zvm_images.check_space_imgimport_xcat(context, instance, - image_bundle_package, CONF.xcat_free_space_threshold, - CONF.zvm_xcat_master) - self._zvm_images.put_image_to_xcat(image_bundle_package, - image_profile) + if inst_nets: + self._hypervisor.guest_create_network_interface(vm_name, + os_distro, inst_nets) - @property - def need_legacy_block_device_info(self): - return False + @staticmethod + def _get_neutron_event(network_info): + if utils.is_neutron() and CONF.vif_plugging_timeout: + return [('network-vif-plugged', vif['id']) + for vif in network_info if vif.get('active') is False] + else: + return [] + + @staticmethod + def _neutron_failed_callback(self, event_name, instance): + LOG.error("Neutron Reported failure on event %s for instance", + event_name, instance=instance) + if CONF.vif_plugging_is_fatal: + raise exception.VirtualInterfaceCreateException() + + def _wait_vif_plug_events(self, vm_name, os_distro, network_info, + instance): + timeout = CONF.vif_plugging_timeout + try: + event = self._get_neutron_event(network_info) + with self.virtapi.wait_for_instance_event( + instance, event, deadline=timeout, + error_callback=self._neutron_failed_callback): + self._setup_network(vm_name, os_distro, network_info, instance) + except eventlet.timeout.Timeout: + LOG.warning("Timeout waiting for vif plugging callback.", + instance=instance) + if CONF.vif_plugging_is_fatal: + raise exception.VirtualInterfaceCreateException() + except Exception as err: + with excutils.save_and_reraise_exception(): + LOG.error("Failed for vif plugging: %s", six.text_type(err), + instance=instance) + + def _import_spawn_image(self, context, image_meta_id, image_os_version): + LOG.debug("Downloading the image %s from glance to nova compute " + "server", image_meta_id) + image_path = os.path.join(os.path.normpath(CONF.zvm.image_tmp_path), + image_meta_id) + if not os.path.exists(image_path): + images.fetch(context, image_meta_id, image_path) + image_url = "file://" + image_path + image_meta = {'os_version': image_os_version} + self._hypervisor.image_import(image_meta_id, image_url, image_meta) def destroy(self, context, instance, network_info=None, block_device_info=None, destroy_disks=False): - """Destroy (shutdown and delete) the specified instance. - - If the instance is not found (for example if networking failed), this - function should still succeed. It's probably a good idea to log a - warning in that case. - - :param instance: Instance object as returned by DB layer. - :param network_info: - :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` - :param block_device_info: Information about block devices that should - be detached from the instance. - :param destroy_disks: Indicates if disks should be destroyed - - """ - inst_name = instance['name'] - root_mount_device, boot_from_volume = zvmutils.is_boot_from_volume( - block_device_info) - zvm_inst = ZVMInstance(self, instance) - - if self._instance_exists(inst_name): - LOG.info(_("Destroying instance %s"), inst_name, - instance=instance) - - # Collect diagnostics when the instance is unreachable, since this - # is most often caused by a deployment failure but the deployment - # artifacts are often needed to debug the root cause. - if zvm_inst.is_reachable(): - LOG.debug(("Node %s is reachable, " - "skipping diagnostics collection"), inst_name, - instance=instance) - elif zvm_inst.is_powered_off(): - LOG.debug(("Node %s is powered off, " - "skipping diagnostics collection"), inst_name, - instance=instance) - else: - LOG.debug(("Node %s is powered on but unreachable, " - "collecting diagnostics for failed deployment"), - inst_name, - instance=instance) - zvm_inst.collect_diagnostics(context, - const.DIAGNOSTICS_RSN_DEPLOYMENT_TIMEOUT) - - bdm = driver.block_device_info_get_mapping(block_device_info) + if self._hypervisor.guest_exists(instance): + LOG.info("Destroying instance", instance=instance) try: - bdm_det = list(bdm) - bdm_det = self._exclude_root_volume_bdm(bdm_det, - root_mount_device) - self._detach_volume_from_instance(instance, bdm_det) - if boot_from_volume: - zvm_inst.clean_volume_boot(context, instance, bdm, - root_mount_device) - except exception.ZVMBaseException as err: - LOG.warning(_("Failed to detach volume: %s"), - err.format_message(), instance=instance) - - if network_info: - try: - for vif in network_info: - self._networkop.clean_mac_switch_host(inst_name) - except exception.ZVMNetworkError: - LOG.warning(_("Clean MAC and VSWITCH failed while " - "destroying z/VM instance %s"), inst_name, + self._hypervisor.guest_delete(instance.name) + except zvm_exception.ZVMConnectorError as err: + if err.overallRC == 404: + LOG.info("instance disappear during destroying", instance=instance) - - try: - nodename = self._get_hcp_info()['nodename'] - zvm_inst.delete_userid(nodename, context) - except exception.ZVMBaseException as err: - LOG.warning(_("Failed to delete user node: %s"), - err.format_message(), instance=instance) + else: + raise else: - LOG.warning(_('Instance %s does not exist'), inst_name, - instance=instance) + LOG.warning("Instance does not exist", instance=instance) - def manage_image_cache(self, context, filtered_instances): - """Clean the image cache in xCAT MN.""" - LOG.info(_("Check and clean image cache in xCAT")) - clean_period = CONF.xcat_image_clean_period - self._zvm_images.clean_image_cache_xcat(clean_period) + def get_host_uptime(self): + return self._hypervisor.get_host_uptime() - def reboot(self, context, instance, network_info, reboot_type, - block_device_info=None, bad_volumes_callback=None): - """Reboot the specified instance. + def snapshot(self, context, instance, image_id, update_task_state): - After this is called successfully, the instance's state - goes back to power_state.RUNNING. The virtualization - platform should ensure that the reboot action has completed - successfully even in cases in which the underlying domain/vm - is paused or halted/stopped. + (image_service, image_id) = glance.get_remote_image_service( + context, image_id) - :param instance: Instance object as returned by DB layer. - :param network_info: - :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` - :param reboot_type: Either a HARD or SOFT reboot - :param block_device_info: Info pertaining to attached volumes - :param bad_volumes_callback: Function to handle any bad volumes - encountered - - """ - zvm_inst = ZVMInstance(self, instance) - if reboot_type == 'SOFT': - zvm_inst.reboot() - else: - zvm_inst.reset() - - if not zvm_inst._reachable: - LOG.error(_("Failed to reboot instance %s: timeout"), - zvm_inst._name, instance=instance) - raise nova_exception.InstanceRebootFailure(reason=_("timeout")) - - def get_host_ip_addr(self): - """Retrieves the IP address of the dom0.""" - return CONF.my_ip - - def _format_mountpoint(self, mountpoint): - """Change mountpoint from /dev/sdX to /dev/vdX. - - When a SCSI device is pluged in, the system will create a file node - /dev/sdX for the SCSI device. If the file node exists already as a - link to another file, the link will be overlayed and the file node - will be seized by the SCSI device. - - For example, if the first SCSI device is pluged in and the mountpoint - is specified as /dev/sdb, the SCSI device will be attached to /dev/sda - and /dev/sdb is created as a link to /dev/sda. Then the second SCSI - device is pluged in. It will be attached to /dev/sdb and the link will - no longer exist. - - To avoid this case, if mountpoint is /dev/sdX, it will be changed to - /dev/vdX. Otherwize it will keep as it is. - - When instance's root_device_name is /dev/dasdX, the mountpoint will be - changed to /dev/dX. That's not what is expected. Format mountpoint to - /dev/vdX in this case. - - :param mountpoint: The file node name of the mountpoint. - - """ - - mountpoint = mountpoint.lower() - mountpoint = mountpoint.replace('/dev/d', '/dev/sd') - return mountpoint.replace('/dev/s', '/dev/v') - - def attach_volume(self, context, connection_info, instance, mountpoint, - disk_bus=None, device_type=None, encryption=None): - """Attach the disk to the instance at mountpoint using info.""" - if instance.vm_state == vm_states.PAUSED: - msg = _("Attaching to a paused instance is not supported.") - raise exception.ZVMDriverError(msg=msg) - if mountpoint: - mountpoint = self._format_mountpoint(mountpoint) - if self._instance_exists(instance['name']): - zvm_inst = ZVMInstance(self, instance) - is_active = zvm_inst.is_reachable() - - zvm_inst.attach_volume(self._volumeop, context, connection_info, - instance, mountpoint, is_active) - - def detach_volume(self, connection_info, instance, mountpoint=None, - encryption=None): - """Detach the disk attached to the instance.""" - if instance.vm_state == vm_states.PAUSED: - msg = _("Detaching from a paused instance is not supported.") - raise exception.ZVMDriverError(msg=msg) - if mountpoint: - mountpoint = self._format_mountpoint(mountpoint) - if self._instance_exists(instance['name']): - zvm_inst = ZVMInstance(self, instance) - is_active = zvm_inst.is_reachable() - - zvm_inst.detach_volume(self._volumeop, connection_info, instance, - mountpoint, is_active) - - def _reset_power_state(self, state, instance): - # If the instance's power_state is "RUNNING", power it on after - # capture. If "PAUSED", pause it after capture. - if state == power_state.RUNNING or state == power_state.PAUSED: - try: - self.power_on({}, instance, []) - except nova_exception.InstancePowerOnFailure as err: - LOG.warning(_("Power On instance %(inst)s fail after " - "capture, please check manually. The error is: %(err)s"), - {'inst': instance['name'], 'err': err.format_message()}, - instance=instance) - if state == power_state.PAUSED: - try: - self.pause(instance) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError) as err: - LOG.warning(_("Pause instance %(inst)s fail after capture, " - "please check manually. The error is: %(err)s"), - {'inst': instance['name'], 'err': err.format_message()}, - instance=instance) - - def _get_xcat_image_file_path(self, image_name_xcat): - """Get image file path from image name in xCAT.""" - image_path = self._zvm_images.get_image_file_path_from_image_name( - image_name_xcat) - image_name = self._zvm_images.get_image_file_name(image_path) - return '/'.join((image_path, image_name)) - - def _is_shared_image_repo(self, image_name_xcat): - """To check whether nova can access xCAT image repo.""" - try: - image_file_path = self._get_xcat_image_file_path(image_name_xcat) - except exception.ZVMImageError: - # image path not exist or image file not found - return False - - return os.stat(image_file_path).st_mode & 4 == 4 - - def snapshot(self, context, instance, image_href, update_task_state): - """Snapshots the specified instance. - - :param context: security context - :param instance: Instance object as returned by DB layer. - :param image_href: Reference to a pre-created image that will - hold the snapshot. - """ - # Check the image status - (image_service, image_id) = glance.get_remote_image_service(context, - image_href) - image_meta = image_service.show(context, image_href) - - # remove user names special characters, this name will only be used - # to pass to xcat and combine with UUID in xcat. - image_name = ''.join(i for i in image_meta['name'] if i.isalnum()) - image_name = zvmutils.remove_prefix_of_unicode(image_name) - image_name_xcat = None - - # Make sure the instance's power_state is running and unpaused before - # doing a capture. - state = instance['power_state'] - if (state == power_state.NOSTATE or state == power_state.CRASHED or - state == power_state.SUSPENDED): - raise nova_exception.InstanceNotReady(instance_id=instance['name']) - elif state == power_state.SHUTDOWN: - self.power_on({}, instance, []) - elif state == power_state.PAUSED: - self.unpause(instance) - - # Check xCAT free space and invoke the zvmimages.create_zvm_image() - try: - free_space_xcat = self._zvm_images.get_free_space_xcat( - CONF.xcat_free_space_threshold, - CONF.zvm_xcat_master) - user_dict = self._get_user_directory(instance['name']) - imgcapture_needed = self._zvm_images.get_imgcapture_needed( - instance, user_dict) - if (free_space_xcat - imgcapture_needed) < 0: - larger = max(CONF.xcat_free_space_threshold, imgcapture_needed) - size_needed = float(larger - free_space_xcat) - self._zvm_images.prune_image_xcat(context, size_needed, - imgcapture_needed) - image_name_xcat = self._zvm_images.create_zvm_image(instance, - image_name, - image_href) - # Update image last create date in xCAT osimage table - self._zvm_images.update_last_use_date(image_name_xcat) - except (exception.ZVMImageError, - exception.ZVMXCATXdshFailed): - with excutils.save_and_reraise_exception(): - self._reset_power_state(state, instance) - self._zvm_images.delete_image_glance(image_service, context, - image_href) - - self._reset_power_state(state, instance) - shared_image_repo = self._is_shared_image_repo(image_name_xcat) - - if not shared_image_repo: - # The image will be exported from xCAT and imported to nova after - # successfully captured. - snapshot_time_path = self._zvm_images.get_snapshot_time_path() - try: - image_bundle = self._zvm_images.get_image_from_xcat( - image_name_xcat, image_name, snapshot_time_path) - except exception.ZVMImageError: - with excutils.save_and_reraise_exception(): - self._zvm_images.delete_image_glance(image_service, - context, image_href) - self._zvm_images.clean_up_snapshot_time_path( - snapshot_time_path) - self._zvm_images.delete_image_from_xcat(image_name_xcat) - - # The image in the xCAT MN will be removed after imported to nova - # Invoke rmimage REST API twice to remove image and object - self._zvm_images.delete_image_from_xcat(image_name_xcat) - - # Untar the image_bundle and parse manifest.xml - image_package_path = os.path.join(snapshot_time_path, - image_name_xcat) - try: - self._zvm_images.untar_image_bundle(snapshot_time_path, - image_bundle) - manifest = self._zvm_images.parse_manifest_xml( - image_package_path) - image_file_name = self._zvm_images.get_image_file_name( - image_package_path) - image_file_path = '/'.join((image_package_path, - image_file_name)) - except exception.ZVMImageError: - with excutils.save_and_reraise_exception(): - self._zvm_images.delete_image_glance(image_service, - context, image_href) - self._zvm_images.clean_up_snapshot_time_path( - snapshot_time_path) - else: - image_file_path = self._get_xcat_image_file_path(image_name_xcat) - (image_package_path, _toss, - image_file_name) = image_file_path.rpartition('/') - manifest = self._zvm_images.get_image_menifest(image_name_xcat) - - root_disk_units = self._zvm_images.get_root_disk_units(image_file_path) - - # Before upload, update the instance task_state to image_pending_upload update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD) - # manifest.xml contributes some new image meta - LOG.debug("Snapshot extracted, beginning image upload", - instance=instance) + try: + self._hypervisor.guest_capture(instance.name, image_id) + except Exception as err: + with excutils.save_and_reraise_exception(): + LOG.error("Failed to capture the instance " + "to generate an image with reason: %(err)s", + {'err': err}, instance=instance) + # Clean up the image from glance + image_service.delete(context, image_id) + + # Export the image to nova-compute server temporary + image_path = os.path.join(os.path.normpath( + CONF.zvm.image_tmp_path), image_id) + dest_path = "file://" + image_path + try: + resp = self._hypervisor.image_export(image_id, dest_path) + except Exception: + with excutils.save_and_reraise_exception(): + LOG.error("Failed to export image %s from SDK server to " + "nova compute server", image_id) + image_service.delete(context, image_id) + self._hypervisor.image_delete(image_id) + + # Save image to glance new_image_meta = { 'is_public': False, 'status': 'active', @@ -913,1138 +347,57 @@ class ZVMDriver(driver.ComputeDriver): 'image_location': 'snapshot', 'image_state': 'available', 'owner_id': instance['project_id'], - 'image_type_xcat': manifest['imagetype'], - 'type': 'snapshot', - 'architecture': manifest['osarch'], - 'os_name': manifest['osname'], - 'os_version': manifest['osvers'], - 'image_profile': manifest['profile'], - 'provisioning_method': manifest['provmethod'], - 'image_file_name': image_file_name, - 'hypervisor_type': const.HYPERVISOR_TYPE, - 'root_disk_units': root_disk_units + 'os_distro': resp['os_version'], + 'architecture': obj_fields.Architecture.S390X, + 'hypervisor_type': obj_fields.HVType.ZVM, }, 'disk_format': 'raw', 'container_format': 'bare', } - - # Upload that image to the image service - image_path = os.path.join(image_package_path, image_file_name) update_task_state(task_state=task_states.IMAGE_UPLOADING, expected_state=task_states.IMAGE_PENDING_UPLOAD) - def cleanup_temp_image(): - if not shared_image_repo: - # Clean up temp image in nova snapshot temp folder - self._zvm_images.clean_up_snapshot_time_path( - snapshot_time_path) - else: - # Clean up image from xCAT image repo for all-in-one mode - self._zvm_images.delete_image_from_xcat(image_name_xcat) - + # Save the image to glance try: with open(image_path, 'r') as image_file: image_service.update(context, - image_href, + image_id, new_image_meta, image_file, purge_props=False) except Exception: with excutils.save_and_reraise_exception(): - self._zvm_images.delete_image_glance(image_service, context, - image_href) - cleanup_temp_image() + image_service.delete(context, image_id) + finally: + zvmutils.clean_up_file(image_path) + self._hypervisor.image_delete(image_id) LOG.debug("Snapshot image upload complete", instance=instance) - cleanup_temp_image() - - LOG.info(_("Snapshot complete successfully"), instance=instance) - - def pause(self, instance): - """Pause the specified instance.""" - LOG.debug('Pausing %s', instance['name'], instance=instance) - zvm_inst = ZVMInstance(self, instance) - zvm_inst.pause() - - def unpause(self, instance): - """Unpause paused VM instance.""" - LOG.debug('Un-pausing %s', instance['name'], instance=instance) - zvm_inst = ZVMInstance(self, instance) - zvm_inst.unpause() - def power_off(self, instance, timeout=0, retry_interval=0): - """Power off the specified instance.""" - LOG.debug('Stopping z/VM instance %s', instance['name'], - instance=instance) - zvm_inst = ZVMInstance(self, instance) - zvm_inst.power_off(timeout, retry_interval) + if timeout >= 0 and retry_interval > 0: + self._hypervisor.guest_softstop(instance.name, timeout=timeout, + retry_interval=retry_interval) + else: + self._hypervisor.guest_softstop(instance.name) def power_on(self, context, instance, network_info, block_device_info=None): - """Power on the specified instance.""" - LOG.debug('Starting z/VM instance %s', instance['name'], - instance=instance) - zvm_inst = ZVMInstance(self, instance) - zvm_inst.power_on() + self._hypervisor.guest_start(instance.name) - def get_available_resource(self, nodename=None): - """Retrieve resource information. + def pause(self, instance): + self._hypervisor.guest_pause(instance.name) - This method is called when nova-compute launches, and - as part of a periodic task + def unpause(self, instance): + self._hypervisor.guest_unpause(instance.name) - :param nodename: - node which the caller want to get resources from - a driver that manages only one node can safely ignore this - :returns: Dictionary describing resources + def reboot(self, context, instance, network_info, reboot_type, + block_device_info=None, bad_volumes_callback=None): - """ - LOG.debug("Getting available resource for %s", CONF.zvm_host) - stats = self.update_host_status()[0] - - mem_used = stats['host_memory_total'] - stats['host_memory_free'] - supported_instances = stats['supported_instances'] - dic = { - 'vcpus': stats['vcpus'], - 'memory_mb': stats['host_memory_total'], - 'local_gb': stats['disk_total'], - 'vcpus_used': stats['vcpus_used'], - 'memory_mb_used': mem_used, - 'local_gb_used': stats['disk_used'], - 'hypervisor_type': stats['hypervisor_type'], - 'hypervisor_version': stats['hypervisor_version'], - 'hypervisor_hostname': stats['hypervisor_hostname'], - 'cpu_info': jsonutils.dumps(stats['cpu_info']), - 'disk_available_least': stats['disk_available'], - 'supported_instances': supported_instances, - 'numa_topology': None, - } - - return dic - - def check_can_live_migrate_destination(self, ctxt, instance_ref, - src_compute_info, dst_compute_info, - block_migration=False, - disk_over_commit=False): - """Check if it is possible to execute live migration. - - This runs checks on the destination host, and then calls - back to the source host to check the results. - - :param ctxt: security context - :param instance_ref: nova.db.sqlalchemy.models.Instance - :param src_compute_info: Info about the sending machine - :param dst_compute_info: Info about the receiving machine - :param block_migration: if true, prepare for block migration - :param disk_over_commit: if true, allow disk over commit - """ - # For z/VM, all live migration check will be done in - # check_can_live_migration_source, so just return a dst_compute_info. - # And we only support shared storage live migration. - migrate_data = {'dest_host': dst_compute_info['hypervisor_hostname'], - 'is_shared_storage': True} - dest_check_data = {'migrate_data': migrate_data} - - return dest_check_data - - def check_can_live_migrate_source(self, ctxt, instance_ref, - dest_check_data, block_device_info=None): - """Check if it is possible to execute live migration. - - This checks if the live migration can succeed, based on the - results from check_can_live_migrate_destination. - - :param context: security context - :param instance_ref: nova.db.sqlalchemy.models.Instance - :param dest_check_data: result of check_can_live_migrate_destination - - """ - LOG.info(_("Checking source host for live-migration for %s"), - instance_ref['name'], instance=instance_ref) - - migrate_data = dest_check_data.get('migrate_data', {}) - dest_host = migrate_data.get('dest_host', None) - userid = zvmutils.get_userid(instance_ref['name']) - migrate_data.update({'source_xcat_mn': CONF.zvm_xcat_server, - 'zvm_userid': userid}) - - if dest_host is not None: - try: - self._vmrelocate(dest_host, instance_ref['name'], 'test') - except nova_exception.MigrationError as err: - emsg = err.format_message() - if isinstance(CONF.zvm_vmrelocate_force, str): - force = CONF.zvm_vmrelocate_force.lower() - if ('domain' in force) or ('architecture' in force): - if '1944' in emsg: - # force domain/architecture in effect, ignore - return migrate_data - LOG.error(_("Live-migrating check failed: %s"), emsg, - instance=instance_ref) - raise nova_exception.MigrationPreCheckError(reason=emsg) - - return migrate_data + if reboot_type == 'SOFT': + self._hypervisor.guest_reboot(instance.name) else: - reason = _("Invalid migration data") - raise nova_exception.MigrationPreCheckError(reason=reason) - - def check_can_live_migrate_destination_cleanup(self, ctxt, - dest_check_data): - """Do required cleanup on dest host after check_can_live_migrate calls - - :param ctxt: security context - :param dest_check_data: result of check_can_live_migrate_destination - - """ - # For z/VM, nothing needed to be cleanup - return - - def pre_live_migration(self, ctxt, instance_ref, block_device_info, - network_info, disk_info, migrate_data=None): - """Prepare an instance for live migration - - :param ctxt: security context - :param instance_ref: instance object that will be migrated - :param block_device_info: instance block device information - :param network_info: instance network information - :param migrate_data: implementation specific data dict. - """ - if block_device_info in ([], None, {}): - msg = _("Not supported live-migration with persistent " - "volume attached") - LOG.error(msg, instance=instance_ref) - raise nova_exception.MigrationError(reason=msg) - - zvm_inst = ZVMInstance(self, instance_ref) - source_xcat_mn = migrate_data.get('source_xcat_mn', '') - userid = migrate_data.get('zvm_userid') - hcp = self._get_hcp_info()['hostname'] - same_xcat_mn = source_xcat_mn == CONF.zvm_xcat_server - dest_diff_mn_key = None - - if not same_xcat_mn: - # The two z/VM system managed by two different xCAT MN - zvm_inst.create_xcat_node(hcp, userid) - dest_diff_mn_key = zvmutils.get_mn_pub_key() - if network_info is not None: - network = network_info[0]['network'] - ip_addr = network['subnets'][0]['ips'][0]['address'] - self._networkop.add_xcat_host(zvm_inst._name, ip_addr, - zvm_inst._name) - self._networkop.makehosts() - - return {'same_xcat_mn': same_xcat_mn, - 'dest_diff_mn_key': dest_diff_mn_key} - - def pre_block_migration(self, ctxt, instance_ref, disk_info): - """Prepare a block device for migration - - :param ctxt: security context - :param instance_ref: instance object that will have its disk migrated - :param disk_info: information about disk to be migrated (as returned - from get_instance_disk_info()) - """ - # We don't support block_migration - return - - def live_migration(self, ctxt, instance_ref, dest, - post_method, recover_method, block_migration=False, - migrate_data=None): - """Live migration of an instance to another host. - - :params ctxt: security context - :params instance_ref: - nova.db.sqlalchemy.models.Instance object - instance object that is migrated. - :params dest: destination host - :params post_method: - post operation method. - expected nova.compute.manager.post_live_migration. - :params recover_method: - recovery method when any exception occurs. - expected nova.compute.manager.recover_live_migration. - :params block_migration: if true, migrate VM disk. - :params migrate_data: implementation specific params. - - """ - inst_name = instance_ref['name'] - dest_host = migrate_data['dest_host'] - LOG.info(_("Live-migrating %(inst)s to %(dest)s"), - {'inst': inst_name, 'dest': dest_host}, instance=instance_ref) - - same_mn = migrate_data['pre_live_migration_result']['same_xcat_mn'] - dest_diff_mn_key = migrate_data['pre_live_migration_result'].get( - 'dest_diff_mn_key', None) - - if not same_mn and dest_diff_mn_key: - auth_command = ('echo "%s" >> /root/.ssh/authorized_keys' % - dest_diff_mn_key) - zvmutils.xdsh(inst_name, auth_command) - - try: - self._vmrelocate(dest_host, inst_name, 'move') - except nova_exception.MigrationError as err: - LOG.error(_("Live-migration failed: %s"), err.format_message(), - instance=instance_ref) - with excutils.save_and_reraise_exception(): - recover_method(ctxt, instance_ref, dest, - block_migration, migrate_data) - - if not same_mn: - # Delete node definition at source xCAT MN - zvm_inst = ZVMInstance(self, instance_ref) - self._networkop.clean_mac_switch_host(zvm_inst._name) - zvm_inst.delete_xcat_node() - - post_method(ctxt, instance_ref, dest, - block_migration, migrate_data) - - def post_live_migration_at_destination(self, ctxt, instance_ref, - network_info, - block_migration=False, - block_device_info=None): - """Post operation of live migration at destination host. - - :param ctxt: security context - :param instance_ref: instance object that is migrated - :param network_info: instance network information - :param block_migration: if true, post operation of block_migration. - - """ - inst_name = instance_ref['name'] - nic_vdev = CONF.zvm_default_nic_vdev - zhcp = self._get_hcp_info()['hostname'] - - for vif in network_info: - LOG.debug('Create nic for instance: %(inst)s, MAC: ' - '%(mac)s Network: %(network)s Vdev: %(vdev)s', - {'inst': inst_name, 'mac': vif['address'], - 'network': vif['network']['label'], 'vdev': nic_vdev}, - instance=instance_ref) - self._networkop.add_xcat_mac(inst_name, nic_vdev, - vif['address'], zhcp) - self._networkop.add_xcat_switch(inst_name, vif['id'], - nic_vdev, zhcp) - nic_vdev = str(hex(int(nic_vdev, 16) + 3))[2:] - - def unfilter_instance(self, instance, network_info): - """Stop filtering instance.""" - # Not supported for now - return - - def _vmrelocate(self, dest_host, instance_name, action): - """Perform live-migration.""" - body = ['destination=%s' % dest_host, - 'action=%s' % action, - 'immediate=%s' % CONF.zvm_vmrelocate_immediate, - 'max_total=%s' % CONF.zvm_vmrelocate_max_total, - 'max_quiesce=%s' % CONF.zvm_vmrelocate_max_quiesce] - if CONF.zvm_vmrelocate_force is not None: - body.append('force=%s' % CONF.zvm_vmrelocate_force) - - url = self._xcat_url.rmigrate('/' + instance_name) - try: - res = zvmutils.xcat_request("PUT", url, body) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError) as err: - raise nova_exception.MigrationError(reason=err.format_message()) - - res_info = res['info'] - if not (len(res_info) > 0 and - len(res_info[0]) > 0 and - res_info[-1][0].__contains__("Done")): - msg = _("Live-migration failed: %s") % str(res_info) - LOG.error(msg) - raise nova_exception.MigrationError(reason=msg) - - def reset_network(self, instance): - """reset networking for specified instance.""" - # TODO(rui): to implement this later. - pass - - def inject_network_info(self, instance, nw_info): - """inject network info for specified instance.""" - # TODO(rui): to implement this later. - pass - - def plug_vifs(self, instance, network_info): - """Plug VIFs into networks.""" - # TODO(rui): to implement this later. - pass - - def unplug_vifs(self, instance, network_info): - """Unplug VIFs from networks.""" - # TODO(rui): to implement this later - pass - - def ensure_filtering_rules_for_instance(self, instance_ref, network_info): - # It enforces security groups on host initialization and live - # migration. In z/VM we do not assume instances running upon host - # initialization - return - - def update_host_status(self): - """Refresh host stats. One compute service entry possibly - manages several hypervisors, so will return a list of host - status information. - """ - LOG.debug("Updating host status for %s", CONF.zvm_host) - - caps = [] - host = CONF.zvm_host - - info = self._get_host_inventory_info(host) - - data = {'host': CONF.host, - 'allowed_vm_type': const.ALLOWED_VM_TYPE} - data['vcpus'] = info['vcpus'] - data['vcpus_used'] = info['vcpus_used'] - data['cpu_info'] = info['cpu_info'] - data['disk_total'] = info['disk_total'] - data['disk_used'] = info['disk_used'] - data['disk_available'] = info['disk_available'] - data['host_memory_total'] = info['memory_mb'] - data['host_memory_free'] = (info['memory_mb'] - - info['memory_mb_used']) - data['hypervisor_type'] = info['hypervisor_type'] - data['hypervisor_version'] = info['hypervisor_version'] - data['hypervisor_hostname'] = info['hypervisor_hostname'] - data['supported_instances'] = [(const.ARCHITECTURE, - const.HYPERVISOR_TYPE, - fields.VMMode.HVM)] - data['zhcp'] = self._get_hcp_info(info['zhcp']) - data['ipl_time'] = info['ipl_time'] - - caps.append(data) - - return caps - - def _get_hcp_info(self, hcp_hostname=None): - if self._host_stats != []: - return self._host_stats[0]['zhcp'] - else: - if hcp_hostname is not None: - hcp_node = hcp_hostname.partition('.')[0] - return {'hostname': hcp_hostname, - 'nodename': hcp_node, - 'userid': zvmutils.get_userid(hcp_node)} - else: - self._host_stats = self.update_host_status() - return self._host_stats[0]['zhcp'] - - def get_volume_connector(self, instance): - """Get connector information for the instance for attaching to volumes. - - Connector information is a dictionary representing the ip of the - machine that will be making the connection, the name of the iscsi - initiator and the hostname of the machine as follows:: - - { - 'zvm_fcp': fcp - 'wwpns': [wwpn] - 'host': host - } - """ - LOG.debug("Getting volume connector") - - res = self._volumeop.get_volume_connector(instance) - - return res - - def _get_host_inventory_info(self, host): - url = self._xcat_url.rinv('/' + host) - inv_info_raw = zvmutils.xcat_request("GET", url)['info'][0] - inv_keys = const.XCAT_RINV_HOST_KEYWORDS - inv_info = zvmutils.translate_xcat_resp(inv_info_raw[0], inv_keys) - dp_info = self._get_diskpool_info(host) - - host_info = {} - - with zvmutils.expect_invalid_xcat_resp_data(inv_info): - host_info['vcpus'] = int(inv_info['lpar_cpu_total']) - host_info['vcpus_used'] = int(inv_info['lpar_cpu_used']) - host_info['cpu_info'] = {} - host_info['cpu_info'] = {'architecture': const.ARCHITECTURE, - 'cec_model': inv_info['cec_model'], } - host_info['disk_total'] = dp_info['disk_total'] - host_info['disk_used'] = dp_info['disk_used'] - host_info['disk_available'] = dp_info['disk_available'] - mem_mb = zvmutils.convert_to_mb(inv_info['lpar_memory_total']) - host_info['memory_mb'] = mem_mb - mem_mb_used = zvmutils.convert_to_mb(inv_info['lpar_memory_used']) - host_info['memory_mb_used'] = mem_mb_used - host_info['hypervisor_type'] = const.HYPERVISOR_TYPE - verl = inv_info['hypervisor_os'].split()[1].split('.') - version = int(''.join(verl)) - host_info['hypervisor_version'] = version - host_info['hypervisor_hostname'] = inv_info['hypervisor_name'] - host_info['zhcp'] = inv_info['zhcp'] - host_info['ipl_time'] = inv_info['ipl_time'] - - return host_info - - def _get_diskpool_info(self, host): - addp = '&field=--diskpoolspace&field=' + CONF.zvm_diskpool - url = self._xcat_url.rinv('/' + host, addp) - res_dict = zvmutils.xcat_request("GET", url) - - dp_info_raw = res_dict['info'][0] - dp_keys = const.XCAT_DISKPOOL_KEYWORDS - dp_info = zvmutils.translate_xcat_resp(dp_info_raw[0], dp_keys) - - with zvmutils.expect_invalid_xcat_resp_data(dp_info): - for k in list(dp_info.keys()): - s = dp_info[k].strip().upper() - if s.endswith('G'): - sl = s[:-1].split('.') - n1, n2 = int(sl[0]), int(sl[1]) - if n2 >= 5: - n1 += 1 - dp_info[k] = n1 - elif s.endswith('M'): - n_mb = int(s[:-1]) - n_gb, n_ad = n_mb / 1024, n_mb % 1024 - if n_ad >= 512: - n_gb += 1 - dp_info[k] = n_gb - else: - exp = "ending with a 'G' or 'M'" - errmsg = _("Invalid diskpool size format: %(invalid)s; " - "Expected: %(exp)s"), {'invalid': s, 'exp': exp} - LOG.error(errmsg) - raise exception.ZVMDriverError(msg=errmsg) - - return dp_info - - def migrate_disk_and_power_off(self, context, instance, dest, - instance_type, network_info, - block_device_info=None, - timeout=0, retry_interval=0): - """Transfers the disk of a running instance in multiple phases, turning - off the instance before the end. - """ - is_volume_base = zvmutils.is_boot_from_volume(block_device_info)[1] - - if is_volume_base: - msg = _("Not support boot from volume.") - LOG.error(msg, instance=instance) - raise nova_exception.InstanceFaultRollback( - nova_exception.MigrationError(reason=msg)) - - new_root_disk_size = instance_type.root_gb - new_eph_disk_size = instance_type.ephemeral_gb - old_root_disk_size = instance.root_gb - old_eph_disk_size = instance.ephemeral_gb - - if (new_root_disk_size < old_root_disk_size or - new_eph_disk_size < old_eph_disk_size): - err = _("Not support shrink disk") - LOG.error(err, instance=instance) - raise nova_exception.InstanceFaultRollback( - nova_exception.MigrationError(reason=err)) - - # Make sure the instance's power_state is running and unpaused before - # doing a capture. - state = instance['power_state'] - if (state == power_state.NOSTATE or state == power_state.CRASHED or - state == power_state.SUSPENDED): - raise nova_exception.InstanceNotReady(instance_id=instance['name']) - elif state == power_state.SHUTDOWN: - self.power_on({}, instance, []) - elif state == power_state.PAUSED: - self.unpause(instance) - - inst_name = instance['name'] - LOG.debug("Starting to migrate instance %s", inst_name, - instance=instance) - - disk_owner = zvmutils.get_userid(inst_name) - eph_disk_info = self._get_eph_disk_info(inst_name) - - bdm = driver.block_device_info_get_mapping(block_device_info) - self._detach_volume_from_instance(instance, bdm) - - image_name_xcat = self._capture_disk_for_instance(context, instance) - - image_bundle = '' - shared_image_repo = self._is_shared_image_repo(image_name_xcat) - if not shared_image_repo: - # Export image from xCAT to compute node if image repo not shared - snapshot_time_path = self._zvm_images.get_snapshot_time_path() - - try: - image_bundle = self._zvm_images.get_image_from_xcat( - image_name_xcat, - image_name_xcat, - snapshot_time_path) - except exception.ZVMImageError: - with excutils.save_and_reraise_exception(): - self._zvm_images.clean_up_snapshot_time_path( - snapshot_time_path) - - source_image = "".join([zvmutils.get_host(), ":", image_bundle]) - disk_info = { - 'disk_type': CONF.zvm_diskpool_type, - 'disk_source_mn': CONF.zvm_xcat_server, - 'disk_source_image': source_image, - 'disk_image_name': image_name_xcat, - 'disk_owner': disk_owner, - 'disk_eph_size_old': old_eph_disk_size, - 'disk_eph_size_new': new_eph_disk_size, - 'eph_disk_info': eph_disk_info, - 'shared_image_repo': shared_image_repo, - } - - return jsonutils.dumps(disk_info) - - def _get_eph_disk_info(self, inst_name): - user_dict = self._get_user_directory(inst_name) - exl = ''.join(['MDISK ', CONF.zvm_user_root_vdev]) - eph_disks = [mdisk for mdisk in user_dict - if (mdisk.__contains__('MDISK ') and - not mdisk.__contains__(exl))] - - eph_disk_info = [] - with zvmutils.expect_invalid_xcat_resp_data(eph_disks): - for eph in eph_disks: - eph_l = eph.rpartition(" MDISK ")[2].split(' ') - eph_disk_info.append({'vdev': eph_l[0], - 'size': eph_l[3], - 'guest_format': None, - 'size_in_units': True, - 'device_name': eph_l[0]}) - - return eph_disk_info - - def _detach_volume_from_instance(self, instance, block_device_mapping): - for bd in block_device_mapping: - connection_info = bd['connection_info'] - mountpoint = bd['mount_device'] - if mountpoint: - mountpoint = self._format_mountpoint(mountpoint) - - if self._instance_exists(instance['name']): - zvm_inst = ZVMInstance(self, instance) - is_active = zvm_inst.is_reachable() - try: - zvm_inst.detach_volume(self._volumeop, connection_info, - instance, mountpoint, is_active, - rollback=False) - except exception.ZVMVolumeError as err: - LOG.warning(_("Failed to detach volume from %(inst)s: " - "%(err)s"), - {'inst': instance['name'], - 'err': err.format_message()}, - instance=instance) - - def _capture_disk_for_instance(self, context, instance): - """Capture disk.""" - zvm_inst = ZVMInstance(self, instance) - image_name = ''.join('rsz' + instance['name']) - image_uuid = str(uuidutils.generate_uuid()) - image_href = image_uuid.replace('-', '_') - - # Capture - orig_provmethod = zvm_inst.get_provmethod() - if orig_provmethod != 'sysclone': - zvm_inst.update_node_provmethod('sysclone') - image_name_xcat = self._zvm_images.create_zvm_image(instance, - image_name, - image_href) - if orig_provmethod != 'sysclone': - zvm_inst.update_node_provmethod(orig_provmethod) - - self._zvm_images.update_last_use_date(image_name_xcat) - - return image_name_xcat - - @contextlib.contextmanager - def cleanup_xcat_image_for_migration(self, image_name_xcat): - """Cleanup xcat image that imported by migrate_disk_and_power_off.""" - try: - yield - except (nova_exception.MigrationError, - exception.ZVMBaseException): - LOG.debug("Cleanup image from xCAT image repository") - with excutils.save_and_reraise_exception(): - self._zvm_images.delete_image_from_xcat(image_name_xcat) - - def finish_migration(self, context, migration, instance, disk_info, - network_info, image_meta, resize_instance, - block_device_info=None, power_on=True): - """Completes a resize, turning on the migrated instance - - :param network_info: - :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info` - :param image_meta: image object returned by nova.image.glance that - defines the image from which this instance - was created - """ - # This is because commit fbe31e461ac3f16edb795993558a2314b4c16b52 - # changes the image_meta from dict to object, we have several - # unique property can't be handled well - # see bug 1537921 for detail info - image_meta = self._image_api.get(context, image_meta.id) - - disk_info = jsonutils.loads(disk_info) - - source_xcat_mn = disk_info['disk_source_mn'].encode('gbk') - source_image = disk_info['disk_source_image'].encode('gbk') - image_name_xcat = disk_info['disk_image_name'].encode('gbk') - disk_type = disk_info['disk_type'].encode('gbk') - disk_eph_size_old = disk_info['disk_eph_size_old'] - disk_eph_size_new = disk_info['disk_eph_size_new'] - eph_disk_info = disk_info['eph_disk_info'] - shared_image_repo = disk_info['shared_image_repo'] - - old_eph_info = block_device_info['ephemerals'] - new_eph_info = [eph for eph in eph_disk_info - if eph['vdev'] != CONF.zvm_user_adde_vdev] - - block_device_info = block_device_info or {} - block_device_info['ephemerals'] = new_eph_info - - if (len(old_eph_info) == 1 and - old_eph_info[0]['size'] == disk_eph_size_old): - # Enlarge the only ephemeral disk - block_device_info['ephemerals'][0]['size'] = disk_eph_size_new - block_device_info['ephemerals'][0]['size_in_units'] = False - - old_userid = disk_info['disk_owner'].encode('gbk') - new_userid = None - if old_userid.startswith("rsz"): - new_userid = instance['name'] - else: - new_userid = ''.join(("rsz", old_userid[3:])) - - same_xcat_mn = source_xcat_mn == CONF.zvm_xcat_server - if disk_type != CONF.zvm_diskpool_type: - if same_xcat_mn: - self._zvm_images.delete_image_from_xcat(image_name_xcat) - msg = _("Can not migration between different disk type" - "current is %(c)s, target is %(t)s") % {'t': disk_type, - 'c': CONF.zvm_diskpool_type} - LOG.error(msg, instance=instance) - raise nova_exception.MigrationError(reason=msg) - - profile = image_name_xcat.split('-')[3] - source_host, t, image_bundle = source_image.partition(":") - source_ip = source_host.rpartition("@")[2] - source_image_time_path = image_bundle.rpartition('/')[0] - local_ip = self.get_host_ip_addr() - - same_os = local_ip == source_ip - - hcp_info = self._get_hcp_info() - zhcp = hcp_info['hostname'] - zhcp_userid = hcp_info['userid'] - - new_inst = ZVMInstance(self, instance) - if same_xcat_mn: - # Same xCAT MN - # cleanup networking, will re-configure later - for vif in network_info: - self._networkop.clean_mac_switch_host(new_inst._name) - - if not shared_image_repo: - # cleanup image bundle from source compute node - if not same_os: - utils.execute('ssh', source_host, 'rm', '-rf', - source_image_time_path) - else: - self._pathutils.clean_temp_folder(source_image_time_path) - - # Create a xCAT node poin - old_instance = self._copy_instance(instance) - old_instance['name'] = ''.join(('rsz', instance['name'])) - old_inst = ZVMInstance(self, old_instance) - - with self.cleanup_xcat_image_for_migration(image_name_xcat): - old_inst.copy_xcat_node(new_inst._name) - try: - new_inst.update_node_def(zhcp, new_userid) - except exception.ZVMBaseException: - with excutils.save_and_reraise_exception(): - old_inst.delete_xcat_node() - else: - # Different xCAT MN - new_inst.create_xcat_node(zhcp) - if new_userid != new_inst._name: - try: - new_inst.update_node_def(zhcp, new_userid) - except exception.ZVMBaseException: - with excutils.save_and_reraise_exception(): - old_inst.delete_xcat_node() - - if not same_os: - snapshot_time_path = self._pathutils.get_snapshot_time_path() - dest_image_path = os.path.join(snapshot_time_path, - image_name_xcat + '.tgz') - utils.execute('scp', source_image, snapshot_time_path) - utils.execute('ssh', source_host, - 'rm', '-rf', source_image_time_path) - else: - snapshot_time_path = source_image_time_path - dest_image_path = image_bundle - - try: - self._zvm_images.put_image_to_xcat(dest_image_path, profile) - except exception.ZVMImageError: - with excutils.save_and_reraise_exception(): - new_inst.delete_xcat_node() - - self._zvm_images.clean_up_snapshot_time_path(snapshot_time_path) - - try: - # Pre-config network and create zvm userid - self._preset_instance_network(new_inst._name, network_info) - new_inst.create_userid(block_device_info, image_meta, context) - - if disk_eph_size_old == 0 and disk_eph_size_new > 0: - # Punch ephemeral disk info to the new instance - zvmutils.process_eph_disk(new_inst._name) - - # Add nic and deploy the image - self._add_nic_to_table(new_inst._name, network_info) - self._deploy_root_and_ephemeral(new_inst, image_name_xcat) - self._wait_and_get_nic_direct(new_inst._name, instance) - except exception.ZVMBaseException: - with excutils.save_and_reraise_exception(): - self._zvm_images.delete_image_from_xcat(image_name_xcat) - - if not same_xcat_mn: - try: - for vif in network_info: - self._networkop.clean_mac_switch_host( - new_inst._name) - except exception.ZVMNetworkError as e: - emsg = zvmutils.format_exception_msg(e) - LOG.debug('clean_mac_switch_host error: %s', emsg) - - new_inst.delete_userid(self._get_hcp_info()['nodename'], - context) - new_inst.delete_xcat_node() - - if same_xcat_mn: - new_inst.copy_xcat_node(old_inst._name) - old_inst.delete_xcat_node() - - # re-configure the networking - self._reconfigure_networking(new_inst._name, network_info, - instance, userid=old_userid) - if power_on: - new_inst.power_on() - - # Cleanup image from xCAT image repository - self._zvm_images.delete_image_from_xcat(image_name_xcat) - - bdm = driver.block_device_info_get_mapping(block_device_info) - try: - if zvmutils.xcat_support_iucv(self._xcat_version): - if same_xcat_mn: - zvmutils.punch_iucv_authorized_file(old_inst._name, - new_inst._name, zhcp_userid) - else: - zvmutils.punch_iucv_authorized_file(new_inst._name, - new_inst._name, zhcp_userid) - new_inst.power_on() - self._attach_volume_to_instance(context, instance, bdm) - - if not power_on: - new_inst.power_off() - except exception.ZVMBaseException: - with excutils.save_and_reraise_exception(): - self.destroy(context, instance, network_info, - block_device_info) - if same_xcat_mn: - new_inst.copy_xcat_node(old_inst._name) - old_inst.delete_xcat_node() - - def _reconfigure_networking(self, inst_name, network_info, instance, - userid=None): - self._preset_instance_network(inst_name, network_info) - self._add_nic_to_table(inst_name, network_info) - self._wait_and_get_nic_direct(inst_name, instance) - - def _copy_instance(self, instance): - return zvminstance.CopiedInstance(instance) - - def _attach_volume_to_instance(self, context, instance, - block_device_mapping): - for bd in block_device_mapping: - connection_info = bd['connection_info'] - mountpoint = bd['mount_device'] - self.attach_volume(context, connection_info, instance, mountpoint) - - def _add_nic_to_table(self, inst_name, network_info): - nic_vdev = CONF.zvm_default_nic_vdev - zhcpnode = self._get_hcp_info()['nodename'] - for vif in network_info: - LOG.debug('Create xcat table value about nic: ' - 'ID is %(id)s, address is %(address)s, ' - 'vdev is %(vdev)s', - {'id': vif['id'], 'address': vif['address'], - 'vdev': nic_vdev}) - self._networkop.create_xcat_table_about_nic(zhcpnode, - inst_name, - vif['id'], - vif['address'], - nic_vdev) - nic_vdev = str(hex(int(nic_vdev, 16) + 3))[2:] - - def _deploy_root_and_ephemeral(self, instance, image_name_xcat): - - # Update the nodetype - instance.update_node_info_resize(image_name_xcat) - - # Deploy - instance.deploy_node(image_name_xcat) - - self._zvm_images.update_last_use_date(image_name_xcat) - - def confirm_migration(self, context, migration, instance, network_info): - """Confirms a resize, destroying the source VM.""" - # Point to old instance - old_instance = self._copy_instance(instance) - old_instance['name'] = ''.join(('rsz', instance['name'])) - old_inst = ZVMInstance(self, old_instance) - - if self._instance_exists(old_inst._name): - # Same xCAT MN: - self.destroy(context, old_instance) - else: - # Different xCAT MN: - self.destroy(context, instance) - self._zvm_images.cleanup_image_after_migration(instance['name']) - - def finish_revert_migration(self, context, instance, network_info, - block_device_info=None, power_on=True): - """Finish reverting a resize, powering back on the instance.""" - new_instance = self._copy_instance(instance) - new_instance['name'] = ''.join(('rsz', instance['name'])) - zvm_inst = ZVMInstance(self, new_instance) - bdm = driver.block_device_info_get_mapping(block_device_info) - - if self._instance_exists(zvm_inst._name): - # Same xCAT MN: - old_inst = ZVMInstance(self, instance) - old_inst.copy_xcat_node(new_instance['name']) - if zvmutils.xcat_support_iucv(self._xcat_version): - zvmutils.copy_zvm_table_status(instance['name'], - new_instance['name']) - zvm_inst.delete_xcat_node() - - self._reconfigure_networking(instance['name'], network_info, - instance) - else: - # Different xCAT MN: - self._zvm_images.cleanup_image_after_migration(instance['name']) - - self._attach_volume_to_instance({}, instance, bdm) - - if power_on: - self.power_on({}, instance, []) - - def _get_nic_switch_info(self, inst_name): - url = self._xcat_url.tabdump("/switch") - with zvmutils.expect_invalid_xcat_resp_data(): - switch_info = zvmutils.xcat_request("GET", url)['data'][0] - switch_info.pop(0) - switch_dict = {} - for item in switch_info: - switch_list = item.split(',') - if switch_list[0].strip('"') == inst_name: - switch_dict[switch_list[4].strip('"')] = \ - switch_list[1].strip('"') - - LOG.debug("Switch info the %(inst_name)s is %(switch_dict)s", - {"inst_name": inst_name, "switch_dict": switch_dict}) - return switch_dict - - def _get_user_directory(self, inst_name): - url = self._xcat_url.lsvm('/' + inst_name) - user_dict = zvmutils.xcat_request("GET", url) - - with zvmutils.expect_invalid_xcat_resp_data(user_dict): - dict_str = user_dict['info'][0][0] - - return dict_str.split("\n") - - def set_admin_password(self, instance, new_pass=None): - """Set the root password on the specified instance. - - The first parameter is an instance of nova.compute.service.Instance, - and so the instance is being specified as instance.name. The second - parameter is the value of the new password. - """ - if new_pass is not None: - self._set_admin_password(instance['name'], new_pass) - - def _set_admin_password(self, inst_name, password): - command = "echo 'root:%s' | chpasswd" % password - try: - if zvmutils.xcat_support_iucv(self._xcat_version): - # After support IUCV, will use execcmdonvm to replace xdsh. - zvmutils.execcmdonvm(inst_name, command) - else: - zvmutils.xdsh(inst_name, command) - except exception.ZVMXCATXdshFailed as err: - LOG.error(_("Setting root password for instance %(instance)s " - "failed with reason: %(err)s"), - {'instance': inst_name, 'err': err.format_message()}) - raise err - - def _wait_and_get_nic_direct(self, inst_name, instance): - """Wait until neutron zvm-agent add NICs into user direct done.""" - def _wait_for_nic_add_in_direct(inst_name, expiration): - if (CONF.zvm_reachable_timeout and - timeutils.utcnow() > expiration): - msg = _("NIC update check failed " - "on instance:%s") % instance.uuid - raise exception.ZVMNetworkError(msg=msg) - - try: - switch_dict = self._get_nic_switch_info(inst_name) - if switch_dict and '' not in switch_dict.values(): - for key, value in switch_dict.items(): - args = '&checknics=' + key - url = self._xcat_url.lsvm('/' + inst_name) - url = url + args - res_info = zvmutils.xcat_request("GET", url) - with zvmutils.expect_invalid_xcat_resp_data(res_info): - if ("errorcode" in res_info and - (len(res_info["errorcode"]) > 0) and - res_info["errorcode"][0] != '0'): - # we didn't found the definition - return - else: - # in this case, the nic switch info is not ready yet - # need another loop to check until time out or find it - return - - except exception.ZVMBaseException as e: - # Ignore any zvm driver exceptions - LOG.info(_('encounter error %s during get vswitch info'), - e.format_message(), instance=instance) - return - - # Enter here means all NIC granted - LOG.info(_("All NICs are added in user direct for " - "instance %s."), inst_name, instance=instance) - raise loopingcall.LoopingCallDone() - - expiration = timeutils.utcnow() + datetime.timedelta( - seconds=CONF.zvm_reachable_timeout) - LOG.info(_("Wait neturon-zvm-agent to add NICs to %s user direct."), - inst_name, instance=instance) - timer = loopingcall.FixedIntervalLoopingCall( - _wait_for_nic_add_in_direct, inst_name, expiration) - timer.start(interval=10).wait() + self._hypervisor.guest_reset(instance.name) def get_console_output(self, context, instance): - """Get console output for an instance.""" - - def append_to_log(log_data, log_path): - LOG.debug('log_data: %(log_data)r, log_path: %(log_path)r', - {'log_data': log_data, 'log_path': log_path}) - fp = open(log_path, 'a+') - fp.write(log_data) - fp.close() - return log_path - - zvm_inst = ZVMInstance(self, instance) - logsize = CONF.zvm_console_log_size * units.Ki - console_log = "" - - try: - console_log = zvm_inst.get_console_log(logsize) - except exception.ZVMXCATInternalError: - # Ignore no console log avaiable error - LOG.info(_("No new console log avaiable.")) - log_path = self._pathutils.get_console_log_path(CONF.zvm_host, - zvm_inst._name) - # TODO(jichenjc): need consider shrink log file size - append_to_log(console_log, log_path) - - log_fp = file(log_path, 'rb') - log_data, remaining = utils.last_bytes(log_fp, logsize) - if remaining > 0: - LOG.info(_('Truncated console log returned, %d bytes ignored'), - remaining, instance=instance) - - return log_data - - def get_host_uptime(self): - """Get host uptime.""" - with zvmutils.expect_invalid_xcat_resp_data(self._host_stats): - return self._host_stats[0]['ipl_time'] - - def get_available_nodes(self, refresh=False): - return [d['hypervisor_hostname'] for d in self._host_stats - if (d.get('hypervisor_hostname') is not None)] - - def _extract_volume_id(self, bdm, root_device): - for bd in bdm: - mount_point = bd['mount_device'] - is_root = zvmutils.is_volume_root(root_device, mount_point) - if is_root: - return bd['connection_info']['serial'] - - errmsg = _("Failed to extract volume id from block device mapping." - "%s") % str(bdm) - raise exception.ZVMDriverError(msg=errmsg) - - def _exclude_root_volume_bdm(self, bdm, root_mount_device): - for bd in bdm: - mountpoint = bd['mount_device'] - is_root = zvmutils.is_volume_root(root_mount_device, mountpoint) - if is_root: - bdm.remove(bd) - return bdm - - def _get_xcat_version(self): - url = self._xcat_url.version() - version_info = zvmutils.xcat_request("GET", url) - with zvmutils.expect_invalid_xcat_resp_data(version_info): - dict_str = version_info['data'][0][0] - version = dict_str.split()[1] - return version - - def _version_check(self, req_ver=None, op=operator.lt): - try: - if req_ver is not None: - cur = versionutils.convert_version_to_int(self._xcat_version) - req = versionutils.convert_version_to_int(req_ver) - if op(cur, req): - return False - return True - except Exception: - return False - - def has_min_version(self, req_ver=None): - return self._version_check(req_ver=req_ver, op=operator.lt) - - def has_version(self, req_ver=None): - return self._version_check(req_ver=req_ver, op=operator.ne) - - def default_device_names_for_instance(self, instance, root_device_name, - *block_device_lists): - """Generate missing device names for an instance. - Default are /dev/vdN. - """ - compute_utils.default_device_names_for_instance( - instance, root_device_name, *block_device_lists) - for bdm in itertools.chain(*block_device_lists): - bdm.device_name = self._format_mountpoint(bdm.device_name) - bdm.save() - - def get_device_name_for_instance(self, instance, bdms, block_device_obj): - """Validates (or generates) a device name for instance. - Default are /dev/vdN. - """ - device_name = block_device_obj.get("device_name") - device_name = compute_utils.get_device_name_for_instance( - instance, bdms, device_name) - return self._format_mountpoint(device_name) + return self._hypervisor.guest_get_console_output(instance.name) diff --git a/nova_zvm/virt/zvm/exception.py b/nova_zvm/virt/zvm/exception.py index 95217d2..638e104 100644 --- a/nova_zvm/virt/zvm/exception.py +++ b/nova_zvm/virt/zvm/exception.py @@ -1,4 +1,4 @@ -# Copyright 2013 IBM Corp. +# Copyright 2018 IBM Corp. # # 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 @@ -17,66 +17,22 @@ from nova import exception from nova.i18n import _ -class ZVMBaseException(exception.NovaException): - """Base z/VM exception.""" - pass +class ZVMDriverException(exception.NovaException): + msg_fmt = _("ZVM Driver has error: %(error)s") -class ZVMDriverError(ZVMBaseException): - msg_fmt = _('z/VM driver error: %(msg)s') +class ZVMConnectorError(ZVMDriverException): + msg_fmt = _("zVM Cloud Connector request failed: %(results)s") + def __init__(self, message=None, **kwargs): + """Exception for zVM ConnectorClient calls. -class ZVMXCATRequestFailed(ZVMBaseException): - msg_fmt = _('Request to xCAT server %(xcatserver)s failed: %(msg)s') + :param results: The object returned from ZVMConnector.send_request. + """ + super(ZVMConnectorError, self).__init__(message=message, **kwargs) - -class ZVMInvalidXCATResponseDataError(ZVMBaseException): - msg_fmt = _('Invalid data returned from xCAT: %(msg)s') - - -class ZVMXCATInternalError(ZVMBaseException): - msg_fmt = _('Error returned from xCAT: %(msg)s') - - -class ZVMVolumeError(ZVMBaseException): - msg_fmt = _('Volume error: %(msg)s') - - -class ZVMImageError(ZVMBaseException): - msg_fmt = _("Image error: %(msg)s") - - -class ZVMGetImageFromXCATFailed(ZVMBaseException): - msg_fmt = _('Get image from xCAT failed: %(msg)s') - - -class ZVMNetworkError(ZVMBaseException): - msg_fmt = _("z/VM network error: %(msg)s") - - -class ZVMXCATXdshFailed(ZVMBaseException): - msg_fmt = _('Execute xCAT xdsh command failed: %(msg)s') - - -class ZVMXCATCreateNodeFailed(ZVMBaseException): - msg_fmt = _('Create xCAT node %(node)s failed: %(msg)s') - - -class ZVMXCATCreateUserIdFailed(ZVMBaseException): - msg_fmt = _('Create xCAT user id %(instance)s failed: %(msg)s') - - -class ZVMXCATUpdateNodeFailed(ZVMBaseException): - msg_fmt = _('Update node %(node)s info failed: %(msg)s') - - -class ZVMXCATDeployNodeFailed(ZVMBaseException): - msg_fmt = _('Deploy image on node %(node)s failed: %(msg)s') - - -class ZVMConfigDriveError(ZVMBaseException): - msg_fmt = _('Create configure drive failed: %(msg)s') - - -class ZVMRetryException(ZVMBaseException): - pass + results = kwargs.get('results', {}) + self.overallRC = results.get('overallRC') + self.rc = results.get('rc') + self.rs = results.get('rs') + self.errmsg = results.get('errmsg') diff --git a/nova_zvm/virt/zvm/guest.py b/nova_zvm/virt/zvm/guest.py new file mode 100644 index 0000000..f791155 --- /dev/null +++ b/nova_zvm/virt/zvm/guest.py @@ -0,0 +1,51 @@ +# Copyright 2017,2018 IBM Corp. +# +# 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. + +from oslo_log import log as logging + +from nova.compute import power_state as compute_power_state +from nova.virt import hardware +from nova_zvm.virt.zvm import conf + + +LOG = logging.getLogger(__name__) +CONF = conf.CONF + + +ZVM_POWER_STATE = { + 'on': compute_power_state.RUNNING, + 'off': compute_power_state.SHUTDOWN, + } + + +class Guest(object): + """z/VM implementation of ComputeDriver.""" + + def __init__(self, hypervisor, instance, virtapi=None): + super(Guest, self).__init__() + + self.virtapi = virtapi + self._hypervisor = hypervisor + self._instance = instance + + def _mapping_power_state(self, power_state): + """Translate power state to OpenStack defined constants.""" + return ZVM_POWER_STATE.get(power_state, compute_power_state.NOSTATE) + + def get_info(self): + """Get the current status of an instance.""" + power_state = self._mapping_power_state( + self._hypervisor.guest_get_power_state( + self._instance.name)) + return hardware.InstanceInfo(power_state) diff --git a/nova_zvm/virt/zvm/hypervisor.py b/nova_zvm/virt/zvm/hypervisor.py new file mode 100644 index 0000000..cd4c544 --- /dev/null +++ b/nova_zvm/virt/zvm/hypervisor.py @@ -0,0 +1,183 @@ +# Copyright 2017,2018 IBM Corp. +# +# 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. + +import os +import pwd + +from oslo_log import log as logging + +from nova.compute import power_state as compute_power_state +from nova import exception +from nova_zvm.virt.zvm import conf +from nova_zvm.virt.zvm import exception as zvm_exception +from nova_zvm.virt.zvm import utils as zvmutils + + +LOG = logging.getLogger(__name__) +CONF = conf.CONF + + +class Hypervisor(object): + """z/VM implementation of Hypervisor.""" + + def __init__(self, zcc_url, ca_file=None): + super(Hypervisor, self).__init__() + + self._reqh = zvmutils.ConnectorClient(zcc_url, + ca_file=ca_file) + host_info = self._get_host_info() + + # Very very unlikely the hostname will be changed, so when create + # hypervisor object, store the information in the cache and after + # that we can use it directly without query again from connectorclient + self._hypervisor_hostname = host_info['hypervisor_hostname'] + + self._rhost = ''.join([pwd.getpwuid(os.geteuid()).pw_name, '@', + CONF.my_ip]) + + def _get_host_info(self): + host_stats = {} + try: + host_stats = self._reqh.call('host_get_info') + except zvm_exception.ZVMConnectorError as e: + LOG.warning("Failed to get host stats: %s", e) + return host_stats + + def get_available_resource(self): + return self._get_host_info() + + def get_available_nodes(self, refresh=False): + # It's not expected that the hostname change, no need to take + # 'refresh' into account. + return [self._hypervisor_hostname] + + def list_names(self): + """list names of the servers in the hypervisor""" + return self._reqh.call('guest_list') + + def get_host_uptime(self): + host_info = self._get_host_info() + + return host_info['ipl_time'] + + def guest_exists(self, instance): + return instance.name.upper() in self.list_names() + + def guest_get_power_state(self, name): + power_state = compute_power_state.NOSTATE + try: + power_state = self._reqh.call('guest_get_power_state', name) + except zvm_exception.ZVMConnectorError as err: + if err.overallRC == 404: + # instance does not exist + LOG.warning("Failed to get power state due to nonexistent " + "instance: %s", name) + raise exception.InstanceNotFound(instance_id=name) + else: + raise + + return power_state + + def guest_create(self, name, vcpus, memory_mb, disk_list): + self._reqh.call('guest_create', name, vcpus, memory_mb, + disk_list=disk_list) + + def guest_deploy(self, name, image_name, transportfiles): + self._reqh.call('guest_deploy', name, image_name, + transportfiles=transportfiles, remotehost=self._rhost) + + def guest_delete(self, name): + self._reqh.call('guest_delete', name) + + def guest_start(self, name): + self._reqh.call('guest_start', name) + + def guest_create_network_interface(self, name, distro, nets): + self._reqh.call('guest_create_network_interface', + name, distro, nets) + + def guest_get_definition_info(self, name): + """Get user direct info + + :returns: User direct is server definition, it will be + returned in a string format + """ + return self._reqh.call('guest_get_definition_info', name) + + def guest_get_nic_vswitch_info(self, name): + """Get the nic and vswitch info + + :returns: Return the nic and vswitch info in dict + """ + return self._reqh.call('guest_get_nic_vswitch_info', name) + + def guest_config_minidisks(self, name, disk_list): + self._reqh.call('guest_config_minidisks', name, disk_list) + + def guest_capture(self, name, image_id): + self._reqh.call('guest_capture', name, image_id) + + def guest_softstop(self, name, timeout=0, retry_interval=0): + self._reqh.call('guest_softstop', name, timeout=timeout, + poll_interval=retry_interval) + + def guest_pause(self, name): + self._reqh.call('guest_pause', name) + + def guest_unpause(self, name): + self._reqh.call('guest_unpause', name) + + def guest_reboot(self, name): + self._reqh.call('guest_reboot', name) + + def guest_reset(self, name): + self._reqh.call('guest_reset', name) + + def guest_get_console_output(self, name): + """get console out put of the given instance + + :returns: The output of the console of the instace, in string format. + """ + return self._reqh.call('guest_get_console_output', name) + + def image_query(self, imagename): + """Check whether image is there or not + + :returns: Query the image and returns a dict of the image info + if the image exists or return {} + """ + return self._reqh.call('image_query', imagename=imagename) + + def image_get_root_disk_size(self, imagename): + """Get the root disk size of image + + :returns: return the size (in string) about the root disk of image + """ + return self._reqh.call('image_get_root_disk_size', imagename) + + def image_import(self, image_href, image_url, image_meta): + self._reqh.call('image_import', image_href, image_url, + image_meta, remote_host=self._rhost) + + def image_export(self, image_id, dest_path): + """export image to a given place + + :returns: a dict which represent the exported image information. + """ + resp = self._reqh.call('image_export', image_id, + dest_path, remote_host=self._rhost) + return resp + + def image_delete(self, image_id): + self._reqh.call('image_delete', image_id) diff --git a/nova_zvm/virt/zvm/imageop.py b/nova_zvm/virt/zvm/imageop.py deleted file mode 100644 index 3aa644f..0000000 --- a/nova_zvm/virt/zvm/imageop.py +++ /dev/null @@ -1,824 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - - -import datetime -import os -import re -import shutil -import tarfile -import xml.dom.minidom as Dom - -from nova import exception as nova_exception -from nova.i18n import _ -from nova.image import glance -from nova import utils -from nova.virt import images -from oslo_concurrency import processutils -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import excutils - -from nova_zvm.virt.zvm import exception -from nova_zvm.virt.zvm import utils as zvmutils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF -QUEUE_BUFFER_SIZE = 10 - - -class ZVMImages(object): - - def __init__(self): - self._xcat_url = zvmutils.get_xcat_url() - self._pathutils = zvmutils.PathUtils() - - def create_zvm_image(self, instance, image_name, image_href): - """Create z/VM image from z/VM instance by invoking xCAT REST API - imgcapture. - """ - nodename = instance['name'] - profile = image_name + "_" + image_href.replace('-', '_') - body = ['nodename=' + nodename, - 'profile=' + profile] - if CONF.zvm_image_compression_level: - if CONF.zvm_image_compression_level.isdigit() and ( - int(CONF.zvm_image_compression_level) in range(0, 10)): - body.append('compress=%s' % CONF.zvm_image_compression_level) - else: - msg = _("Invalid zvm_image_compression_level detected, please" - "specify it with a integer between 0 and 9 in your nova.conf") - raise exception.ZVMImageError(msg=msg) - - url = self._xcat_url.imgcapture() - LOG.debug('Capturing %s start', instance['name']) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMImageError): - res = zvmutils.xcat_request("POST", url, body) - - os_image = self._get_os_image(res) - - return os_image - - def _get_os_image(self, response): - """Return the image_name by parsing the imgcapture rest api - response. - """ - image_name_xcat = "" - if len(response['info']) > 0: - for info in response['info']: - for info_element in info: - if "Completed capturing the image" in info_element: - start_index = info_element.find('(') - end_index = info_element.find(')') - image_name_xcat = info_element[start_index + 1: - end_index] - return image_name_xcat - if len(image_name_xcat) == 0: - msg = _("Capture image failed.") - LOG.error(msg) - raise exception.ZVMImageError(msg=msg) - else: - msg = _("Capture image returns bad response. res is %s") % response - LOG.error(msg) - raise exception.ZVMImageError(msg=msg) - - def get_snapshot_time_path(self): - return self._pathutils.get_snapshot_time_path() - - def get_image_from_xcat(self, image_name_xcat, image_name, - snapshot_time_path): - """Import image from xCAT to nova, by invoking the imgexport - REST API. - """ - LOG.debug("Getting image from xCAT") - destination = os.path.join(snapshot_time_path, image_name + '.tgz') - host = zvmutils.get_host() - body = ['osimage=' + image_name_xcat, - 'destination=' + destination, - 'remotehost=' + host] - url = self._xcat_url.imgexport() - - try: - zvmutils.xcat_request("POST", url, body) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError) as err: - msg = (_("Transfer image to compute node failed: %s") % - err.format_message()) - raise exception.ZVMImageError(msg=msg) - return destination - - def delete_image_glance(self, image_service, context, image_href): - """Delete the image from glance database and image repository. - - Can be used for a rollback step if operations to image fail. - """ - image_service.delete(context, image_href) - - def clean_up_snapshot_time_path(self, snapshot_time_path): - """Clean up the time_path and its contents under "snapshot_tmp" after - image uploaded to glance. - - Also be used for a rollback step if operations to the image file fail. - """ - if os.path.exists(snapshot_time_path): - LOG.debug("Cleaning up nova local image file") - shutil.rmtree(snapshot_time_path) - - def _delete_image_file_from_xcat(self, image_name_xcat): - """Delete image file from xCAT MN. - - When capturing, image in the xCAT MN's repository will be removed after - it is imported to nova compute node. - """ - LOG.debug("Removing image files from xCAT MN image repository") - url = self._xcat_url.rmimage('/' + image_name_xcat) - try: - zvmutils.xcat_request("DELETE", url) - except (exception.ZVMXCATInternalError, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATRequestFailed): - LOG.warning(_("Failed to delete image file %s from xCAT"), - image_name_xcat) - - def _delete_image_object_from_xcat(self, image_name_xcat): - """Delete image object from xCAT MN. - - After capturing, image definition in the xCAT MN's table osimage and - linuximage will be removed after it is imported to nova compute node. - - """ - LOG.debug("Deleting the image object") - url = self._xcat_url.rmobject('/' + image_name_xcat) - try: - zvmutils.xcat_request("DELETE", url) - except (exception.ZVMXCATInternalError, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATRequestFailed): - LOG.warning(_("Failed to delete image definition %s from xCAT"), - image_name_xcat) - - def get_image_file_path_from_image_name(self, image_name_xcat): - return zvmutils.xcat_cmd_gettab("linuximage", "imagename", - image_name_xcat, "rootimgdir") - - def delete_image_from_xcat(self, image_name_xcat): - self._delete_image_file_from_xcat(image_name_xcat) - self._delete_image_object_from_xcat(image_name_xcat) - - def _getxmlnode(self, node, name): - return node.getElementsByTagName(name)[0] if node else [] - - def _getnode(self, node_root, tagname): - """For parse manifest.""" - nodename = node_root.getElementsByTagName(tagname)[0] - nodevalue = nodename.childNodes[0].data - return nodevalue - - def parse_manifest_xml(self, image_package_path): - """Return the image properties from manifest.xml.""" - LOG.debug("Parsing the manifest.xml") - manifest_xml = os.path.join(image_package_path, "manifest.xml") - - manifest = {} - - if os.path.exists(manifest_xml): - xml_file = Dom.parse(manifest_xml) # nosec - else: - LOG.warning(_('manifest.xml does not exist')) - manifest['imagename'] = '' - manifest['imagetype'] = '' - manifest['osarch'] = '' - manifest['osname'] = '' - manifest['osvers'] = '' - manifest['profile'] = '' - manifest['provmethod'] = '' - return manifest - - node_root = xml_file.documentElement - node_root = self._getxmlnode(node_root, 'osimage') - manifest['imagename'] = self._getnode(node_root, "imagename") - manifest['imagetype'] = self._getnode(node_root, "imagetype") - manifest['osarch'] = self._getnode(node_root, "osarch") - manifest['osname'] = self._getnode(node_root, "osname") - manifest['osvers'] = self._getnode(node_root, "osvers") - manifest['profile'] = self._getnode(node_root, "profile") - manifest['provmethod'] = self._getnode(node_root, "provmethod") - - return manifest - - def untar_image_bundle(self, snapshot_time_path, image_bundle): - """Untar the image bundle *.tgz from xCAT and remove the *.tgz.""" - if os.path.exists(image_bundle): - LOG.debug("Untarring the image bundle ... ") - tarobj = tarfile.open(image_bundle, "r:gz") - for tarinfo in tarobj: - tarobj.extract(tarinfo.name, path=snapshot_time_path) - tarobj.close() - os.remove(image_bundle) - else: - self.clean_up_snapshot_time_path(snapshot_time_path) - msg = _("Image bundle does not exist %s") % image_bundle - raise exception.ZVMImageError(msg=msg) - - def get_image_file_name(self, image_package_path): - if os.path.exists(image_package_path): - file_contents = os.listdir(image_package_path) - for f in file_contents: - if f.endswith('.img'): - return f - msg = _("Can not find image file under %s") % image_package_path - else: - msg = _("Image path %s not exist") % image_package_path - raise exception.ZVMImageError(msg=msg) - - def image_exist_xcat(self, image_id): - """To see if the specific image exist in xCAT MN's image - repository. - """ - LOG.debug("Checking if the image %s exists or not in xCAT " - "MN's image repository ", image_id) - image_uuid = image_id.replace('-', '_') - parm = '&criteria=profile=~' + image_uuid - url = self._xcat_url.lsdef_image(addp=parm) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMImageError): - res = zvmutils.xcat_request("GET", url) - - res_image = res['info'] - - if '_' in str(res_image): - return True - else: - return False - - def fetch_image(self, context, image_id, target, user, project): - LOG.debug("Downloading image %s from glance image server", - image_id) - try: - images.fetch(context, image_id, target) - except Exception as err: - emsg = zvmutils.format_exception_msg(err) - msg = _("Download image file of image %(id)s failed with reason:" - " %(err)s") % {'id': image_id, 'err': emsg} - raise exception.ZVMImageError(msg=msg) - - def generate_manifest_file(self, image_meta, image_name, disk_file, - manifest_path): - """Generate the manifest.xml file from glance's image metadata - as a part of the image bundle. - """ - image_id = image_meta['id'] - image_type = image_meta['properties']['image_type_xcat'] - os_version = image_meta['properties']['os_version'] - os_name = image_meta['properties']['os_name'] - os_arch = image_meta['properties']['architecture'] - prov_method = image_meta['properties']['provisioning_method'] - - image_profile = '_'.join((image_name, image_id.replace('-', '_'))) - image_name_xcat = '-'.join((os_version, os_arch, - prov_method, image_profile)) - rootimgdir_str = ('/install', prov_method, os_version, - os_arch, image_profile) - rootimgdir = '/'.join(rootimgdir_str) - today_date = datetime.date.today() - last_use_date_string = today_date.strftime("%Y-%m-%d") - is_deletable = "auto:last_use_date:" + last_use_date_string - - doc = Dom.Document() - xcatimage = doc.createElement('xcatimage') - doc.appendChild(xcatimage) - - # Add linuximage section - imagename = doc.createElement('imagename') - imagename_value = doc.createTextNode(image_name_xcat) - imagename.appendChild(imagename_value) - rootimagedir = doc.createElement('rootimgdir') - rootimagedir_value = doc.createTextNode(rootimgdir) - rootimagedir.appendChild(rootimagedir_value) - linuximage = doc.createElement('linuximage') - linuximage.appendChild(imagename) - linuximage.appendChild(rootimagedir) - xcatimage.appendChild(linuximage) - - # Add osimage section - osimage = doc.createElement('osimage') - manifest = {'imagename': image_name_xcat, - 'imagetype': image_type, - 'isdeletable': is_deletable, - 'osarch': os_arch, - 'osname': os_name, - 'osvers': os_version, - 'profile': image_profile, - 'provmethod': prov_method} - - if 'image_comments' in image_meta['properties']: - manifest['comments'] = image_meta['properties']['image_comments'] - - for item in list(manifest.keys()): - itemkey = doc.createElement(item) - itemvalue = doc.createTextNode(manifest[item]) - itemkey.appendChild(itemvalue) - osimage.appendChild(itemkey) - xcatimage.appendChild(osimage) - f = open(manifest_path + '/manifest.xml', 'w') - f.write(doc.toprettyxml(indent='')) - f.close() - - # Add the rawimagefiles section - rawimagefiles = doc.createElement('rawimagefiles') - xcatimage.appendChild(rawimagefiles) - - files = doc.createElement('files') - files_value = doc.createTextNode(rootimgdir + '/' + disk_file) - files.appendChild(files_value) - - rawimagefiles.appendChild(files) - - f = open(manifest_path + '/manifest.xml', 'w') - f.write(doc.toprettyxml(indent=' ')) - f.close() - - self._rewr(manifest_path) - - return manifest_path + '/manifest.xml' - - def _rewr(self, manifest_path): - f = open(manifest_path + '/manifest.xml', 'r') - lines = f.read() - f.close() - - lines = lines.replace('\n', '') - lines = re.sub(r'>(\s*)<', r'>\n\1<', lines) - lines = re.sub(r'>[ \t]*(\S+)[ \t]*<', r'>\1<', lines) - - f = open(manifest_path + '/manifest.xml', 'w') - f.write(lines) - f.close() - - def generate_image_bundle(self, spawn_path, tmp_file_fn, image_name): - """Generate the image bundle which is used to import to xCAT MN's - image repository. - """ - image_bundle_name = image_name + '.tar' - tar_file = spawn_path + '/' + tmp_file_fn + '_' + image_bundle_name - LOG.debug("The generate the image bundle file is %s", tar_file) - - os.chdir(spawn_path) - tarFile = tarfile.open(tar_file, mode='w') - - try: - tarFile.add(tmp_file_fn) - tarFile.close() - except Exception as err: - msg = (_("Generate image bundle failed: %s") % err) - LOG.error(msg) - if os.path.isfile(tar_file): - os.remove(tar_file) - raise exception.ZVMImageError(msg=msg) - finally: - self._pathutils.clean_temp_folder(tmp_file_fn) - - return tar_file - - def check_space_imgimport_xcat(self, context, instance, tar_file, - xcat_free_space_threshold, zvm_xcat_master): - image_href = instance['image_ref'] - try: - free_space_xcat = self.get_free_space_xcat( - xcat_free_space_threshold, zvm_xcat_master) - img_transfer_needed = self._get_transfer_needed_space_xcat(context, - image_href, tar_file) - larger = max(xcat_free_space_threshold, img_transfer_needed) - if img_transfer_needed > free_space_xcat: - larger = max(xcat_free_space_threshold, img_transfer_needed) - size_needed = float(larger - free_space_xcat) - self.prune_image_xcat(context, size_needed, - img_transfer_needed) - else: - LOG.debug("Image transfer needed space satisfied in xCAT") - except exception.ZVMImageError: - with excutils.save_and_reraise_exception(): - os.remove(tar_file) - - def put_image_to_xcat(self, image_bundle_package, image_profile): - """Import the image bundle from compute node to xCAT MN's image - repository. - """ - remote_host_info = zvmutils.get_host() - body = ['osimage=%s' % image_bundle_package, - 'profile=%s' % image_profile, - 'remotehost=%s' % remote_host_info, - 'nozip'] - url = self._xcat_url.imgimport() - - try: - zvmutils.xcat_request("POST", url, body) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError) as err: - msg = (_("Import the image bundle to xCAT MN failed: %s") % - err.format_message()) - raise exception.ZVMImageError(msg=msg) - finally: - os.remove(image_bundle_package) - - def get_imgname_xcat(self, image_id): - """Get the xCAT deployable image name by image id.""" - image_uuid = image_id.replace('-', '_') - parm = '&criteria=profile=~' + image_uuid - url = self._xcat_url.lsdef_image(addp=parm) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMImageError): - res = zvmutils.xcat_request("GET", url) - with zvmutils.expect_invalid_xcat_resp_data(res): - res_image = res['info'][0][0] - res_img_name = res_image.strip().split(" ")[0] - - if res_img_name: - return res_img_name - else: - LOG.error(_("Fail to find the right image to deploy")) - - def _get_image_list_xcat(self): - """Get an image list from xcat osimage table. - - criteria: osarch=s390x and provmethod=netboot|raw|sysclone and - isdeletable field - - """ - display_field = '&field=isdeletable' - isdeletable_criteria = '&criteria=isdeletable=~^auto:last_use_date:' - osarch_criteria = '&criteria=osarch=s390x' - provmethod_criteria = '&criteria=provmethod=~netboot|raw|sysclone' - - addp = ''.join([isdeletable_criteria, osarch_criteria, - provmethod_criteria, display_field]) - url = self._xcat_url.lsdef_image(addp=addp) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMImageError): - output = zvmutils.xcat_request("GET", url) - - image_list = [] - if len(output['info']) <= 0: - return image_list - - if len(output['info'][0]) > 0: - i = 0 - while i < len(output['info'][0]): - if "Object name:" in output['info'][0][i]: - sub_list = [] - len_objectname = len("Object name: ") - is_deletable = output['info'][0][i + 1] - last_use_date = self._validate_last_use_date( - output['info'][0][i][len_objectname:], - is_deletable) - if last_use_date is None: - i += 2 - continue - sub_list.append(output['info'][0][i][len_objectname:]) - sub_list.append(last_use_date) - image_list.append(sub_list) - i += 2 - - return image_list - - def update_last_use_date(self, image_name_xcat): - """Update the last_use_date in xCAT osimage table after a - successful deploy. - """ - LOG.debug("Update the last_use_date in xCAT osimage table " - "after a successful deploy") - - today_date = datetime.date.today() - last_use_date_string = today_date.strftime("%Y-%m-%d") - url = self._xcat_url.tabch('/osimage') - is_deletable = "auto:last_use_date:" + last_use_date_string - body = ["imagename=" + image_name_xcat, - "osimage.isdeletable=" + is_deletable] - - try: - zvmutils.xcat_request("PUT", url, body) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError) as err: - LOG.warning(_("Illegal date for last_use_date %s"), - err.format_message()) - - return last_use_date_string - - def _validate_last_use_date(self, image_name, is_deletable): - """Validate the isdeletable date format.""" - last_use_date_string = is_deletable.split(":")[2] - timere = ("^\d{4}[-]((0([1-9]{1}))|" - "(1[0|1|2]))[-](([0-2]([0-9]{1}))|(3[0|1]))$") - - if (len(last_use_date_string) == 10) and ( - re.match(timere, last_use_date_string)): - LOG.debug("The format for last_use_date is valid ") - else: - LOG.warning(_("The format for image %s record in xcat table " - "osimage's last_used_date is not valid. The correct " - "format is auto:last_use_date:yyyy-mm-dd"), image_name) - return - - try: - last_use_date_datetime = datetime.datetime.strptime( - last_use_date_string, '%Y-%m-%d') - except Exception as err: - LOG.warning(_("Illegal date for last_use_date %(msg)s"), err) - return - - return last_use_date_datetime.date() - - def _verify_is_deletable_periodic(self, last_use_date, clean_period): - """Check the last_use_date of an image to determine if the image - need to be cleaned. - """ - now = datetime.date.today() - delta = (now - last_use_date).days - if (delta - clean_period) >= 0: - return True - else: - return False - - def clean_image_cache_xcat(self, clean_period): - """Clean the old image.""" - image_list = self._get_image_list_xcat() - if len(image_list) <= 0: - return - else: - i = 0 - while i < len(image_list): - image_name_xcat = image_list[i][0] - last_use_date = image_list[i][1] - if self._verify_is_deletable_periodic(last_use_date, - clean_period): - LOG.debug('Delete the image %s', image_name_xcat) - self.delete_image_from_xcat(image_name_xcat) - else: - LOG.debug("Keep the image") - i += 1 - - def _get_image_bundle_size(self, tar_file): - size_byte = os.path.getsize(tar_file) - return float(size_byte) / 1024 / 1024 / 1024 - - def _get_image_size_glance(self, context, image_href): - (image_service, image_id) = glance.get_remote_image_service( - context, image_href) - - try: - image_meta = image_service.show(context, image_href) - except nova_exception.ImageNotFound: - image_meta = {} - return 0 - - size_byte = image_meta['size'] - return float(size_byte) / 1024 / 1024 / 1024 - - def get_free_space_xcat(self, xcat_free_space_threshold, zvm_xcat_master): - """Get the free space in xCAT MN /install.""" - LOG.debug("Get the xCAT MN /install free space") - addp = "&field=--freerepospace" - if isinstance(zvm_xcat_master, str): - url = self._xcat_url.rinv("/" + zvm_xcat_master, addp=addp) - else: - msg = _("zvm_xcat_master should be specified as a string") - LOG.error(msg) - raise exception.ZVMImageError(msg=msg) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMImageError): - result = zvmutils.xcat_request("GET", url) - with zvmutils.expect_invalid_xcat_resp_data(result): - if len(result['info']) == 0: - msg = _("'rinv --freerepospace' returns " - "null, please check 'df -h /install', there may " - "be something wrong with the mount of /install") - raise exception.ZVMImageError(msg=msg) - free_space_line = result['info'][0][0] - free_space = free_space_line.split()[4] - if free_space.endswith("G"): - free_disk_value = free_space.rstrip("G") - return float(free_disk_value) - elif free_space.endswith("M"): - free_disk_value = free_space.rstrip("M") - return float(free_disk_value) / 1024 - elif free_space == "0": - return 0 - else: - return xcat_free_space_threshold - - def get_imgcapture_needed(self, instance, user_dict): - """Get the space needed on xCAT MN for an image capture.""" - LOG.debug("Getting %s root disk size as image capture max needed.", - instance['name']) - try: - rdisk = ''.join(['MDISK ', CONF.zvm_user_root_vdev]) - rdisk_dict = [mdisk for mdisk in user_dict - if (mdisk.__contains__(rdisk))] - if not rdisk_dict or not rdisk_dict[0]: - raise exception.ZVMImageError(msg="User direct info error") - - rdisk_list = rdisk_dict[0].split(' ') - # will get similar result USERID: MDISK 0100 3390 1 3338 0E3B MR - if rdisk_list[3] == '3390': - return int(rdisk_list[5]) * 737280 / 1024.0 / 1024 / 1024 * 2 - elif rdisk_list[3] == '9336': - return int(rdisk_list[5]) * 512 / 1024.0 / 1024 / 1024 * 2 - else: - raise exception.ZVMImageError(msg="Unknown disk type") - except (IndexError, ValueError, TypeError) as err: - raise exception.ZVMImageError(msg=err) - - def _get_transfer_needed_space_xcat(self, context, image_href, tar_file): - """To transfer an image bundle from glance to xCAT, the needed size is - image_bundle_size + image_size. - """ - image_bundle_size = self._get_image_bundle_size(tar_file) - image_size = self._get_image_size_glance(context, image_href) - return image_bundle_size + image_size - - def _get_image_href_by_osimage(self, image_name_xcat): - """If we have the xCAT.osimage.imagename, we want to get the - image_href. - """ - try: - image_profile = image_name_xcat.split("-") - if len(image_profile) >= 4: - return image_profile[3] - else: - return image_name_xcat - except (TypeError, IndexError): - LOG.error(_("xCAT imagename format for %s is not as expected"), - image_name_xcat) - - def _sort_image_by_use_date(self, image_list, left, right): - """Sort the image_list by last_use_date from oldest image to latest.""" - if (left < right): - i = left - j = right - x = image_list[left] - while (i < j): - while (i < j and image_list[j][1] >= x[1]): - j -= 1 - if(i < j): - image_list[i] = image_list[j] - i += 1 - while(i < j and image_list[i][1] < x[1]): - i += 1 - if(i < j): - image_list[j] = image_list[i] - j -= 1 - image_list[i] = x - self._sort_image_by_use_date(image_list, left, i - 1) - self._sort_image_by_use_date(image_list, i + 1, right) - - def _get_to_be_deleted_images_xcat(self, context, size_needed, - current_needed): - """To get a list of images which is to be removed from xCAT image - repository because it cannot provide enough space for image operations - from OpenStack. - """ - image_list = self._get_image_list_xcat() - size_sum = 0 - to_be_deleted_image_profile = [] - - if len(image_list) <= 0: - msg = _("No image to be deleted, please create space manually " - "on xcat(%s).") % CONF.zvm_xcat_server - LOG.warning(msg) - else: - self._sort_image_by_use_date(image_list, 0, len(image_list) - 1) - for img in image_list: - image_name_xcat = img[0] - image_profile = self._get_image_href_by_osimage( - image_name_xcat) - image_uuid = image_profile.partition('_')[2].replace("_", "-") - image_size = self._get_image_size_glance(context, - image_uuid) - if image_size > 0: - to_be_deleted_image_profile.append(image_profile) - size_sum += image_size - if size_sum >= size_needed: - return to_be_deleted_image_profile - - if size_sum < current_needed: - msg = _("xCAT MN space not enough for current image operation: " - "%(n)d G needed,%(a)d G available") % {'n': current_needed, - 'a': size_sum} - LOG.warning(msg) - - return to_be_deleted_image_profile - - def prune_image_xcat(self, context, size_needed, current_needed): - """Remove the images which meet remove criteria from xCAT.""" - LOG.debug("Clear up space by clean images in xCAT") - to_be_deleted_image_profile = self._get_to_be_deleted_images_xcat( - context, size_needed, current_needed) - if len(to_be_deleted_image_profile) > 0: - for image_profile in to_be_deleted_image_profile: - image_name_xcat = self.get_imgname_xcat(image_profile) - self.delete_image_from_xcat(image_name_xcat) - - def zimage_check(self, image_meta): - """Do a brief check to see if the image is a valid zVM image.""" - property_ = ['image_file_name', 'image_type_xcat', 'architecture', - 'os_name', 'provisioning_method', 'os_version'] - missing_prop = [] - for prop in property_: - if prop not in list(image_meta['properties'].keys()): - missing_prop.append(prop) - - if len(missing_prop) > 0: - msg = (_("The image %(id)s is not a valid zVM image, " - "property %(prop)s are missing") % {'id': image_meta['id'], - 'prop': missing_prop}) - LOG.error(msg) - raise nova_exception.ImageUnacceptable(image_id=image_meta['id'], - reason=msg) - - def cleanup_image_after_migration(self, inst_name): - """Cleanup osimages in xCAT image repository while confirm migration - or revert migration at source compute. - """ - image_list = self._get_image_list_xcat() - matchee = ''.join(['rsz', inst_name]) - for img in image_list: - img_name = img[0] - if matchee in img_name: - self.delete_image_from_xcat(img_name) - - def get_root_disk_units(self, image_file_path): - """use 'hexdump' to get the root_disk_units.""" - try: - (output, _toss) = utils.execute('hexdump', '-n', '48', - '-C', image_file_path) - except processutils.ProcessExecutionError: - msg = (_("Get image property failed," - " please check whether the image file exists!")) - raise exception.ZVMImageError(msg=msg) - - LOG.debug("hexdump result is %s", output) - try: - root_disk_size = int(output[144:156]) - disk_units = output[220:223] - root_disk_units = ':'.join([str(root_disk_size), disk_units]) - except ValueError: - msg = (_("Image file at %s is missing built-in disk size " - "metadata, it was probably not captured with xCAT") - % image_file_path) - raise exception.ZVMImageError(msg=msg) - - if 'FBA' not in output and 'CKD' not in output: - msg = (_("The image's disk type is not valid. Currently we only" - " support FBA and CKD disk")) - raise exception.ZVMImageError(msg=msg) - - LOG.debug("The image's root_disk_units is %s", root_disk_units) - return root_disk_units - - def set_image_root_disk_units(self, context, image_meta, image_file_path): - """Set the property 'root_disk_units'to image.""" - new_image_meta = image_meta - root_disk_units = self.get_root_disk_units(image_file_path) - LOG.debug("The image's root_disk_units is %s", root_disk_units) - - (glance_image_service, image_id) = glance.get_remote_image_service( - context, image_meta['id']) - new_image_meta = glance_image_service.show(context, image_id) - new_image_meta['properties']['root_disk_units'] = root_disk_units - - try: - glance_image_service.update(context, image_id, - new_image_meta, None) - except nova_exception.ImageNotAuthorized: - msg = _('Not allowed to modify attributes for image %s') % image_id - LOG.info(msg) - - return new_image_meta - - def get_image_menifest(self, image_name_xcat): - """Get image manifest info from xcat osimage table.""" - attr_list = ['imagetype', 'osarch', 'imagename', 'osname', - 'osvers', 'profile', 'provmethod'] - menifest = zvmutils.xcat_cmd_gettab_multi_attr('osimage', 'imagename', - image_name_xcat, attr_list) - return menifest diff --git a/nova_zvm/virt/zvm/instance.py b/nova_zvm/virt/zvm/instance.py deleted file mode 100644 index 7da4769..0000000 --- a/nova_zvm/virt/zvm/instance.py +++ /dev/null @@ -1,909 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - - -import binascii -import six -import time - -from nova.compute import power_state -from nova import exception as nova_exception -from nova.i18n import _ -from nova.virt import hardware -from oslo_config import cfg -from oslo_log import log as logging - -from nova_zvm.virt.zvm import const -from nova_zvm.virt.zvm import dist -from nova_zvm.virt.zvm import exception -from nova_zvm.virt.zvm import utils as zvmutils -from nova_zvm.virt.zvm import volumeop - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -CONF.import_opt('default_ephemeral_format', 'nova.conf') - - -class CopiedInstance(object): - _KWS = ('name', 'image_ref', 'uuid', 'user_id', 'project_id', - 'power_state', 'system_metadata', 'memory_mb', 'vcpus', - 'root_gb', 'ephemeral_gb') - - def __init__(self, inst_obj): - """Dictionary compatible instance object for zVM driver internal use. - - :inst_obj: instance object of nova.objects.instance.Instance. - """ - for k in self._KWS: - if inst_obj.get(k, None): - setattr(self, k, inst_obj[k]) - - def get(self, name, default=None): - return getattr(self, name, default) - - def __setitem__(self, name, value): - setattr(self, name, value) - - def __getitem__(self, name): - return getattr(self, name, None) - - -class ZVMInstance(object): - '''OpenStack instance that running on of z/VM hypervisor.''' - - def __init__(self, driver, instance=None): - """Initialize instance attributes for database.""" - instance = instance or {} - self._xcat_url = zvmutils.get_xcat_url() - self._instance = instance - self._name = instance['name'] - self._volumeop = volumeop.VolumeOperator() - self._dist_manager = dist.ListDistManager() - self._driver = driver - - def power_off(self, timeout=0, retry_interval=10): - """Power off z/VM instance.""" - try: - self._power_state("PUT", "softoff") - except exception.ZVMXCATInternalError as err: - err_str = err.format_message() - if ("Return Code: 200" in err_str and - "Reason Code: 12" in err_str): - # Instance already not active - LOG.warning(_("z/VM instance %s not active"), self._name) - return - else: - msg = _("Failed to power off instance: %s") % err_str - LOG.error(msg) - raise nova_exception.InstancePowerOffFailure(reason=msg) - - timeout = timeout or CONF.shutdown_timeout - retry_interval = retry_interval or 10 - retry_count = timeout // retry_interval - while (retry_count > 0): - if self._check_power_stat() == power_state.SHUTDOWN: - # In shutdown state already - return - else: - time.sleep(retry_interval) - retry_count -= 1 - - LOG.warning(_("Failed to shutdown instance %(inst)s in %(time)d " - "seconds"), {'inst': self._name, 'time': timeout}) - - def power_on(self): - """"Power on z/VM instance.""" - try: - self._power_state("PUT", "on") - except exception.ZVMXCATInternalError as err: - err_str = err.format_message() - if ("Return Code: 200" in err_str and - "Reason Code: 8" in err_str): - # Instance already active - LOG.warning(_("z/VM instance %s already active"), self._name) - return - raise nova_exception.InstancePowerOnFailure(reason=err_str) - - self._wait_for_reachable() - if not self._reachable: - LOG.error(_("Failed to power on instance %s: timeout"), self._name) - raise nova_exception.InstancePowerOnFailure(reason="timeout") - - def is_powered_off(self): - """Return True if the instance is powered off.""" - return self._check_power_stat() == power_state.SHUTDOWN - - def reset(self): - """Hard reboot z/VM instance.""" - try: - self._power_state("PUT", "reset") - except exception.ZVMXCATInternalError as err: - err_str = err.format_message() - if ("Return Code: 200" in err_str and - "Reason Code: 12" in err_str): - # Be able to reset in power state of SHUTDOWN - LOG.warning(_("Reset z/VM instance %s from SHUTDOWN state"), - self._name) - return - else: - raise err - self._wait_for_reachable() - - def reboot(self): - """Soft reboot z/VM instance.""" - self._power_state("PUT", "reboot") - self._wait_for_reachable() - - def pause(self): - """Pause the z/VM instance.""" - self._power_state("PUT", "pause") - - def unpause(self): - """Unpause the z/VM instance.""" - self._power_state("PUT", "unpause") - self._wait_for_reachable() - - def attach_volume(self, volumeop, context, connection_info, instance, - mountpoint, is_active, rollback=True): - volumeop.attach_volume_to_instance(context, connection_info, - instance, mountpoint, - is_active, rollback) - - def detach_volume(self, volumeop, connection_info, instance, mountpoint, - is_active, rollback=True): - volumeop.detach_volume_from_instance(connection_info, - instance, mountpoint, - is_active, rollback) - - def get_info(self): - cpumempowerstat_version = const.XCAT_RINV_SUPPORT_CPUMEMPOWERSTAT - # new version has cpumempowerstat support in order gain performance - if self._driver.has_min_version(cpumempowerstat_version): - return self._get_info_cpumempowerstat() - else: - return self._get_info_cpumem() - - def _get_info_cpumempowerstat(self): - """Get current status of an z/VM instance through cpumempowerstat.""" - _instance_info = hardware.InstanceInfo() - max_mem_kb = int(self._instance['memory_mb']) * 1024 - - try: - rec_list = self._get_rinv_info('cpumempowerstat') - except exception.ZVMXCATInternalError: - raise nova_exception.InstanceNotFound(instance_id=self._name) - - mem = self._get_current_memory(rec_list) - num_cpu = self._get_guest_cpus(rec_list) - cpu_time = self._get_cpu_used_time(rec_list) - power_stat = self._get_power_stat(rec_list) - - if ((power_stat == power_state.RUNNING) and - (self._instance['power_state'] == power_state.PAUSED)): - # return paused state only previous power state is paused - _instance_info.state = power_state.PAUSED - else: - _instance_info.state = power_stat - - # TODO(jichenjc): set max mem through SMAPI result - _instance_info.max_mem_kb = max_mem_kb - _instance_info.mem_kb = mem - _instance_info.num_cpu = num_cpu - _instance_info.cpu_time_ns = cpu_time - return _instance_info - - def _get_info_cpumem(self): - """Get current status of an z/VM instance through cpumem.""" - _instance_info = hardware.InstanceInfo() - - power_stat = self._check_power_stat() - is_reachable = self.is_reachable() - - max_mem_kb = int(self._instance['memory_mb']) * 1024 - if is_reachable: - try: - rec_list = self._get_rinv_info('cpumem') - except exception.ZVMXCATInternalError: - raise nova_exception.InstanceNotFound(instance_id=self._name) - - try: - mem = self._get_current_memory(rec_list) - num_cpu = self._get_cpu_count(rec_list) - cpu_time = self._get_cpu_used_time(rec_list) - _instance_info.state = power_stat - _instance_info.max_mem_kb = max_mem_kb - _instance_info.mem_kb = mem - _instance_info.num_cpu = num_cpu - _instance_info.cpu_time_ns = cpu_time - - except exception.ZVMInvalidXCATResponseDataError: - LOG.warning(_("Failed to get inventory info for %s"), - self._name) - _instance_info.state = power_stat - _instance_info.max_mem_kb = max_mem_kb - _instance_info.mem_kb = max_mem_kb - _instance_info.num_cpu = self._instance['vcpus'] - _instance_info.cpu_time_ns = 0 - - else: - # Since xCAT rinv can't get info from a server that in power state - # of SHUTDOWN or PAUSED - if ((power_stat == power_state.RUNNING) and - (self._instance['power_state'] == power_state.PAUSED)): - # return paused state only previous power state is paused - _instance_info.state = power_state.PAUSED - _instance_info.max_mem_kb = max_mem_kb - _instance_info.mem_kb = max_mem_kb - _instance_info.num_cpu = self._instance['vcpus'] - _instance_info.cpu_time_ns = 0 - else: - # otherwise return xcat returned state - _instance_info.state = power_stat - _instance_info.max_mem_kb = max_mem_kb - _instance_info.mem_kb = 0 - _instance_info.num_cpu = self._instance['vcpus'] - _instance_info.cpu_time_ns = 0 - return _instance_info - - def create_xcat_node(self, zhcp, userid=None): - """Create xCAT node for z/VM instance.""" - LOG.debug("Creating xCAT node for %s", self._name) - - user_id = userid or self._name - body = ['userid=%s' % user_id, - 'hcp=%s' % zhcp, - 'mgt=zvm', - 'groups=%s' % CONF.zvm_xcat_group] - url = self._xcat_url.mkdef('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATCreateNodeFailed, node=self._name): - zvmutils.xcat_request("POST", url, body) - - def _create_user_id_body(self, boot_from_volume): - kwprofile = 'profile=%s' % CONF.zvm_user_profile - body = [kwprofile, - 'cpu=%i' % self._instance['vcpus'], - 'memory=%im' % self._instance['memory_mb'], - 'privilege=%s' % CONF.zvm_user_default_privilege] - - # if we don't set admin userid, then we will used old password - # field, otherwise, will use logon by method. - if not CONF.zvm_default_admin_userid: - body.append('password=%s' % CONF.zvm_user_default_password) - else: - body.append('password=LBYONLY') - body.append('logonby=%s' % CONF.zvm_default_admin_userid) - - # if mkvm in lower version xcat won't support it - # they will ignore this param. - if not boot_from_volume: - body.append('ipl=%s' % CONF.zvm_user_root_vdev) - - return body - - def _check_set_ipl(self): - xcat_version = self._driver._xcat_version - - if not zvmutils.xcat_support_mkvm_ipl_param(xcat_version): - self._set_ipl(CONF.zvm_user_root_vdev) - - def create_userid(self, block_device_info, image_meta, context, - os_image=None): - """Create z/VM userid into user directory for a z/VM instance.""" - # We do not support boot from volume currently - LOG.debug("Creating the z/VM user entry for instance %s", self._name) - - boot_from_volume = zvmutils.is_boot_from_volume(block_device_info)[1] - - eph_disks = block_device_info.get('ephemerals', []) - - body = self._create_user_id_body(boot_from_volume) - - if not boot_from_volume: - # image_meta passed from spawn is a dict, in resize is a object - if isinstance(image_meta, dict): - if 'name' in image_meta.keys(): - kwimage = 'imagename=%s' % image_meta['name'] - body.append(kwimage) - else: - image_name = getattr(image_meta, 'name') - if image_name: - kwimage = 'imagename=%s' % image_name - body.append(kwimage) - - if os_image: - kwimage = 'osimage=%s' % os_image - body.append(kwimage) - - # Versions of xCAT that do not understand the instance ID and - # request ID will silently ignore them. - url = self._xcat_url.mkvm('/' + self._name, self._instance.uuid, - context) - - # Note: driver.py:spawn() has already checked that the root disk units - # and the type of disks in the pool are compatible. - try: - zvmutils.xcat_request("POST", url, body) - - if not boot_from_volume: - size = '%ig' % self._instance['root_gb'] - # If the flavor specifies 0 for the root disk size, use the - # size in the image's metadata - if size == '0g': - root_disk_units = image_meta['properties'][ - 'root_disk_units'] - size = root_disk_units.split(":")[0] - - # Add root disk and set ipl - self.add_mdisk(CONF.zvm_diskpool, - CONF.zvm_user_root_vdev, - size) - self._check_set_ipl() - - # Add additional ephemeral disk - if self._instance['ephemeral_gb'] != 0: - if eph_disks == []: - # Create ephemeral disk according to flavor - fmt = (CONF.default_ephemeral_format or - const.DEFAULT_EPH_DISK_FMT) - self.add_mdisk(CONF.zvm_diskpool, - CONF.zvm_user_adde_vdev, - '%ig' % self._instance['ephemeral_gb'], - fmt) - else: - # Create ephemeral disks according --ephemeral option - for idx, eph in enumerate(eph_disks): - vdev = (eph.get('vdev') or - zvmutils.generate_eph_vdev(idx)) - size = eph['size'] - size_in_units = eph.get('size_in_units', False) - if not size_in_units: - size = '%ig' % size - fmt = (eph.get('guest_format') or - CONF.default_ephemeral_format or - const.DEFAULT_EPH_DISK_FMT) - self.add_mdisk(CONF.zvm_diskpool, vdev, size, fmt) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMDriverError) as err: - msg = _("Failed to create z/VM userid: %s") % err.format_message() - LOG.error(msg) - raise exception.ZVMXCATCreateUserIdFailed(instance=self._name, - msg=msg) - - def prepare_volume_boot(self, context, instance, block_device_mapping, - root_device, volume_meta): - try: - connection_info = self._volumeop.get_root_volume_connection_info( - block_device_mapping, root_device) - (lun, wwpn, size, - fcp) = self._volumeop.extract_connection_info(context, - connection_info) - if len(wwpn) > 16: - # a wwpn list similar to: - # 5005076800aa0001;5005076800aa0002;5005076800aa0003 - wwpn = wwpn.split(';')[0] - - (kernel_parm_string, scpdata) = self._forge_hex_scpdata(fcp, - wwpn, lun, volume_meta) - - loaddev_str = "%(wwpn)s %(lun)s 1 %(scpdata)s" % {'wwpn': wwpn, - 'lun': lun, 'scpdata': scpdata} - - self._volumeop.volume_boot_init(instance, fcp) - self._set_ipl(fcp) - self._set_loaddev(loaddev_str) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMVolumeError, - exception.ZVMDriverError) as err: - msg = _("Failed to prepare volume to boot") % err.format_message() - LOG.error(msg) - raise exception.ZVMVolumeError(msg=msg) - - return (lun, wwpn, size, fcp) - - def clean_volume_boot(self, context, instance, block_device_mapping, - root_device): - try: - connection_info = self._volumeop.get_root_volume_connection_info( - block_device_mapping, root_device) - (lun, wwpn, size, - fcp) = self._volumeop.extract_connection_info(context, - connection_info) - self._volumeop.volume_boot_cleanup(instance, fcp) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMVolumeError, - exception.ZVMDriverError) as err: - emsg = err.format_message() - msg = _("Failed to clean boot from volume " - "preparations: %s") % emsg - LOG.warning(msg) - raise exception.ZVMVolumeError(msg=msg) - - def _forge_hex_scpdata(self, fcp, wwpn, lun, volume_meta): - """Forge scpdata in string form and HEX form.""" - root = volume_meta['root'] - os_version = volume_meta['os_version'] - linux_dist = self._dist_manager.get_linux_dist(os_version)() - scp_string = linux_dist.get_scp_string(root, fcp, wwpn, lun) - - # Convert to HEX string - # Without Encode / Decode it will still work for python 2.6 but not for - # Python 3 - try: - scp_string_ascii = scp_string.encode('ascii') - scp_string_hex = binascii.hexlify(scp_string_ascii) - scp_data = scp_string_hex.decode('ascii') - except Exception as err: - errmsg = _("Failed to forge hex scpdata: %s") % err - LOG.error(errmsg) - raise exception.ZVMDriverError(msg=errmsg) - - return (scp_string, scp_data) - - def _set_ipl(self, ipl_state): - body = ["--setipl %s" % ipl_state] - url = self._xcat_url.chvm('/' + self._name) - zvmutils.xcat_request("PUT", url, body) - - def _set_loaddev(self, loaddev): - body = ["--setloaddev %s" % loaddev] - url = self._xcat_url.chvm('/' + self._name) - zvmutils.xcat_request("PUT", url, body) - - def get_userid(self): - return zvmutils.get_userid(self._name) - - def unlock_userid(self, zhcp_node): - _uid = self.get_userid() - cmd = "/opt/zhcp/bin/smcli Image_Unlock_DM -T %s" % _uid - zvmutils.xdsh(zhcp_node, cmd) - - def unlock_devices(self, zhcp_node): - _uid = self.get_userid() - cmd = "/opt/zhcp/bin/smcli Image_Lock_Query_DM -T %s" % _uid - resp = zvmutils.xdsh(zhcp_node, cmd) - with zvmutils.expect_invalid_xcat_resp_data(resp): - resp_str = resp['data'][0][0] - - if resp_str.__contains__("is Unlocked..."): - # unlocked automatically, do nothing - return - - def _unlock_device(vdev): - cmd = ("/opt/zhcp/bin/smcli Image_Unlock_DM -T %(uid)s -v %(vdev)s" - % {'uid': _uid, 'vdev': vdev}) - zvmutils.xdsh(zhcp_node, cmd) - - resp_list = resp_str.split('\n') - for s in resp_list: - if s.__contains__('Device address:'): - vdev = s.rpartition(':')[2].strip() - _unlock_device(vdev) - - def _delete_userid(self, url): - try: - zvmutils.xcat_request("DELETE", url) - except exception.ZVMXCATInternalError as err: - emsg = err.format_message() - LOG.debug("error emsg in delete_userid: %s", emsg) - if (emsg.__contains__("Return Code: 400") and - emsg.__contains__("Reason Code: 4")): - # zVM user definition not found, delete xCAT node directly - self.delete_xcat_node() - else: - raise - - def delete_userid(self, zhcp_node, context): - """Delete z/VM userid for the instance.This will remove xCAT node - at same time. - """ - # Versions of xCAT that do not understand the instance ID and - # request ID will silently ignore them. - url = self._xcat_url.rmvm('/' + self._name, self._instance.uuid, - context) - - try: - self._delete_userid(url) - except exception.ZVMXCATInternalError as err: - emsg = err.format_message() - if (emsg.__contains__("Return Code: 400") and - emsg.__contains__("Reason Code: 12")): - # The vm was locked. Unlock before deleting - self.unlock_userid(zhcp_node) - elif (emsg.__contains__("Return Code: 408") and - emsg.__contains__("Reason Code: 12")): - # The vm device was locked. Unlock the device before deleting - self.unlock_devices(zhcp_node) - else: - LOG.debug("exception not able to handle in delete_userid " - "%s", self._name) - raise err - # delete the vm after unlock - self._delete_userid(url) - except exception.ZVMXCATRequestFailed as err: - emsg = err.format_message() - if (emsg.__contains__("Invalid nodes and/or groups") and - emsg.__contains__("Forbidden")): - # Assume neither zVM userid nor xCAT node exist in this case - return - else: - raise err - - def delete_xcat_node(self): - """Remove xCAT node for z/VM instance.""" - url = self._xcat_url.rmdef('/' + self._name) - try: - zvmutils.xcat_request("DELETE", url) - except exception.ZVMXCATInternalError as err: - if err.format_message().__contains__("Could not find an object"): - # The xCAT node not exist - return - else: - raise err - - def add_mdisk(self, diskpool, vdev, size, fmt=None): - """Add a 3390 mdisk for a z/VM user. - - NOTE: No read, write and multi password specified, and - access mode default as 'MR'. - - """ - disk_type = CONF.zvm_diskpool_type - if (disk_type == 'ECKD'): - action = '--add3390' - elif (disk_type == 'FBA'): - action = '--add9336' - else: - errmsg = _("Disk type %s is not supported.") % disk_type - LOG.error(errmsg) - raise exception.ZVMDriverError(msg=errmsg) - - if fmt: - body = [" ".join([action, diskpool, vdev, size, "MR", "''", "''", - "''", fmt])] - else: - body = [" ".join([action, diskpool, vdev, size])] - url = self._xcat_url.chvm('/' + self._name) - zvmutils.xcat_request("PUT", url, body) - - def _power_state(self, method, state): - """Invoke xCAT REST API to set/get power state for a instance.""" - body = [state] - url = self._xcat_url.rpower('/' + self._name) - return zvmutils.xcat_request(method, url, body) - - def _check_power_stat(self): - """Get power status of a z/VM instance.""" - LOG.debug('Query power stat of %s', self._name) - res_dict = self._power_state("GET", "stat") - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _get_power_string(d): - tempstr = d['info'][0][0] - return tempstr[(tempstr.find(':') + 2):].strip() - - power_stat = _get_power_string(res_dict) - return zvmutils.mapping_power_stat(power_stat) - - def _get_rinv_info(self, command): - """get rinv result and return in a list.""" - field = '&field=%s' % command - url = self._xcat_url.rinv('/' + self._name, field) - LOG.debug('Remote inventory of %s', self._name) - res_info = zvmutils.xcat_request("GET", url)['info'] - - with zvmutils.expect_invalid_xcat_resp_data(res_info): - rinv_info = res_info[0][0].split('\n') - - return rinv_info - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _modify_storage_format(self, mem): - """modify storage from 'G' ' M' to 'K'.""" - # Only special case for 0 - if mem == '0': - return 0 - - new_mem = 0 - if mem.endswith('G'): - new_mem = int(mem[:-1]) * 1024 * 1024 - elif mem.endswith('M'): - new_mem = int(mem[:-1]) * 1024 - elif mem.endswith('K'): - new_mem = int(mem[:-1]) - else: - exp = "ending with a 'G', 'M' or 'K'" - errmsg = _("Invalid memory format: %(invalid)s; Expected: " - "%(exp)s") % {'invalid': mem, 'exp': exp} - LOG.error(errmsg) - raise exception.ZVMInvalidXCATResponseDataError(msg=errmsg) - return new_mem - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _get_current_memory(self, rec_list): - """Return the max memory can be used.""" - _mem = None - - for rec in rec_list: - if rec.__contains__("Total Memory: "): - tmp_list = rec.split() - _mem = tmp_list[3] - - _mem = self._modify_storage_format(_mem) - return _mem - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _get_cpu_count(self, rec_list): - """Return the virtual cpu count.""" - _cpu_flag = False - num_cpu = 0 - - for rec in rec_list: - if (_cpu_flag is True): - tmp_list = rec.split() - if (len(tmp_list) > 1): - if (tmp_list[1] == "CPU"): - num_cpu += 1 - else: - _cpu_flag = False - if rec.__contains__("Processors: "): - _cpu_flag = True - - return num_cpu - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _get_cpu_used_time(self, rec_list): - """Return the cpu used time in.""" - cpu_time = 0 - - for rec in rec_list: - if rec.__contains__("CPU Used Time: "): - tmp_list = rec.split() - cpu_time = tmp_list[4] - - return float(cpu_time) - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _get_guest_cpus(self, rec_list): - """Return the processer count, used by cpumempowerstat""" - guest_cpus = 0 - - for rec in rec_list: - if rec.__contains__("Guest CPUs: "): - tmp_list = rec.split() - guest_cpus = tmp_list[3] - - return int(guest_cpus) - - @zvmutils.wrap_invalid_xcat_resp_data_error - def _get_power_stat(self, rec_list): - """Return the power stat, used by cpumempowerstat""" - power_stat = None - - for rec in rec_list: - if rec.__contains__("Power state: "): - tmp_list = rec.split() - power_stat = tmp_list[3] - - return zvmutils.mapping_power_stat(power_stat) - - @zvmutils.wrap_invalid_xcat_resp_data_error - def is_reachable(self): - """Check whether IUCV connection works well.""" - if zvmutils.xcat_support_iucv(self._driver._xcat_version): - LOG.debug("Check whether VM %s is reachable.", self._name) - result = self._power_state("PUT", "isreachable") - if ': reachable' in result['info'][0][0]: - - return True - else: - url = self._xcat_url.nodestat('/' + self._name) - LOG.debug('Get instance status of %s', self._name) - res_dict = zvmutils.xcat_request("GET", url) - - with zvmutils.expect_invalid_xcat_resp_data(): - status = res_dict['node'][0][0]['data'][0] - - if status is not None: - if status.__contains__('sshd'): - return True - return False - - def _wait_for_reachable(self): - """Called at an interval until the instance is reachable.""" - self._reachable = False - - def _check_reachable(): - if not self.is_reachable(): - raise exception.ZVMRetryException() - else: - self._reachable = True - - zvmutils.looping_call(_check_reachable, 5, 5, 30, - CONF.zvm_reachable_timeout, - exception.ZVMRetryException) - - def update_node_info(self, image_meta): - LOG.debug("Update the node info for instance %s", self._name) - - image_name = ''.join(i for i in image_meta['name'] if i.isalnum()) - image_id = image_meta['id'] - os_type = image_meta['properties']['os_version'] - os_arch = image_meta['properties']['architecture'] - prov_method = image_meta['properties']['provisioning_method'] - profile_name = '_'.join((image_name, image_id.replace('-', '_'))) - - body = ['noderes.netboot=%s' % const.HYPERVISOR_TYPE, - 'nodetype.os=%s' % os_type, - 'nodetype.arch=%s' % os_arch, - 'nodetype.provmethod=%s' % prov_method, - 'nodetype.profile=%s' % profile_name] - url = self._xcat_url.chtab('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATUpdateNodeFailed, node=self._name): - zvmutils.xcat_request("PUT", url, body) - - def update_node_info_resize(self, image_name_xcat): - LOG.debug("Update the nodetype for instance %s", self._name) - - name_section = image_name_xcat.split("-") - os_type = name_section[0] - os_arch = name_section[1] - profile_name = name_section[3] - - body = ['noderes.netboot=%s' % const.HYPERVISOR_TYPE, - 'nodetype.os=%s' % os_type, - 'nodetype.arch=%s' % os_arch, - 'nodetype.provmethod=%s' % 'sysclone', - 'nodetype.profile=%s' % profile_name] - - url = self._xcat_url.chtab('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATUpdateNodeFailed, node=self._name): - zvmutils.xcat_request("PUT", url, body) - - def get_provmethod(self): - addp = "&col=node=%s&attribute=provmethod" % self._name - url = self._xcat_url.gettab('/nodetype', addp) - res_info = zvmutils.xcat_request("GET", url) - return res_info['data'][0][0] - - def update_node_provmethod(self, provmethod): - LOG.debug("Update the nodetype for instance %s", self._name) - - body = ['nodetype.provmethod=%s' % provmethod] - - url = self._xcat_url.chtab('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATUpdateNodeFailed, node=self._name): - zvmutils.xcat_request("PUT", url, body) - - def update_node_def(self, hcp, userid): - """Update xCAT node definition.""" - - body = ['zvm.hcp=%s' % hcp, - 'zvm.userid=%s' % userid] - url = self._xcat_url.chtab('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATUpdateNodeFailed, node=self._name): - zvmutils.xcat_request("PUT", url, body) - - def deploy_node(self, image_name, transportfiles=None, vdev=None): - LOG.debug("Begin to deploy image on instance %s", self._name) - vdev = vdev or CONF.zvm_user_root_vdev - remote_host_info = zvmutils.get_host() - body = ['netboot', - 'device=%s' % vdev, - 'osimage=%s' % image_name] - - if transportfiles: - body.append('transport=%s' % transportfiles) - body.append('remotehost=%s' % remote_host_info) - - url = self._xcat_url.nodeset('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATDeployNodeFailed, node=self._name): - zvmutils.xcat_request("PUT", url, body) - - def copy_xcat_node(self, source_node_name): - """Create xCAT node from an existing z/VM instance.""" - LOG.debug("Creating xCAT node %s from existing node", self._name) - - url = self._xcat_url.lsdef_node('/' + source_node_name) - res_info = zvmutils.xcat_request("GET", url)['info'][0] - - body = [] - for info in res_info: - if ("=" in info and ("postbootscripts" not in info) and - ("postscripts" not in info) and ("hostnames" not in info)): - body.append(info.lstrip()) - - url = self._xcat_url.mkdef('/' + self._name) - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMXCATCreateNodeFailed, node=self._name): - zvmutils.xcat_request("POST", url, body) - - def get_console_log(self, logsize): - """get console log.""" - url = self._xcat_url.rinv('/' + self._name, '&field=--consoleoutput' - '&field=%s') % logsize - - # Because we might have logs in the console, we need ignore the warning - res_info = zvmutils.xcat_request("GET", url, ignore_warning=True) - - with zvmutils.expect_invalid_xcat_resp_data(res_info): - log_data = res_info['info'][0][0] - - return log_data - - def collect_diagnostics(self, context, reason): - xcat_version = self._driver._xcat_version - - if zvmutils.xcat_support_deployment_failure_diagnostics(xcat_version): - # Diagnostics request is only supported >= xCAT 2.3.8.16 - # On older versions of xCAT, do nothing. If the request is issued - # on an older version, xCAT will treat it as an error. - url = self._xcat_url.mkdiag('/', self._name, self._instance.uuid, - context) - # Some body properties will appear to duplicate information - # carried elsewhere, for example the request ID which is a URL - # query parameter as well. This is intentional. The request body - # is considered opaque to xCAT, data there is simply passed through - # into the diagnostics blob as "upstream context". Other parts - # of the request, such as the URL query parameters, are NOT opaque - # and xCAT uses them to filter the data that is captured. - body = ['reason=%s' % reason, - 'openstack_nova_instance_uuid=%s' % self._instance.uuid] - if context is not None: - try: - body.append('openstack_request_id=%s' % context.request_id) - except Exception as err: - # Cannot use format_message() in this context, because the - # Exception class does not implement that method. - msg = _("Failed to add request ID to message body: %(err)s" - ) % {'err': six.text_type(err)} - LOG.error(msg) - # Continue and return the original URL once the error is - # logged. Failing the request over this is NOT desired. - try: - zvmutils.xcat_request("POST", url, body) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMDriverError) as err: - msg = _("Failed to collect deployment timeout diagnostics: %s" - ) % err.format_message() - LOG.error(msg) - else: - msg = _("Skipping diagnostics collection; xCAT version %(actual)s " - "does not support that function. The first xCAT version " - "supporting that function is %(required)s." - ) % {'actual': xcat_version, 'required': - const.XCAT_SUPPORT_COLLECT_DIAGNOSTICS_DEPLOYFAILED} - LOG.debug(msg) diff --git a/nova_zvm/virt/zvm/networkop.py b/nova_zvm/virt/zvm/networkop.py deleted file mode 100644 index e539589..0000000 --- a/nova_zvm/virt/zvm/networkop.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - - -from oslo_config import cfg - -from nova_zvm.virt.zvm import exception -from nova_zvm.virt.zvm import utils as zvmutils - - -CONF = cfg.CONF - -NetworkUtils = zvmutils.NetworkUtils() - - -class NetworkOperator(object): - """Configuration check and manage MAC address.""" - - def __init__(self): - self._xcat_url = zvmutils.get_xcat_url() - - def add_xcat_host(self, node, ip, host_name): - """Add/Update hostname/ip bundle in xCAT MN nodes table.""" - commands = "node=%s" % node + " hosts.ip=%s" % ip - commands += " hosts.hostnames=%s" % host_name - body = [commands] - url = self._xcat_url.tabch("/hosts") - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - result_data = zvmutils.xcat_request("PUT", url, body)['data'] - - return result_data - - def _delete_xcat_host(self, node_name): - """Remove xcat hosts table rows where node name is node_name.""" - commands = "-d node=%s hosts" % node_name - body = [commands] - url = self._xcat_url.tabch("/hosts") - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def add_xcat_mac(self, node, interface, mac, zhcp=None): - """Add node name, interface, mac address into xcat mac table.""" - commands = "mac.node=%s" % node + " mac.mac=%s" % mac - commands += " mac.interface=%s" % interface - if zhcp is not None: - commands += " mac.comments=%s" % zhcp - url = self._xcat_url.tabch("/mac") - body = [commands] - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def add_xcat_switch(self, node, nic_name, interface, zhcp=None): - """Add node name and nic name address into xcat switch table.""" - commands = "switch.node=%s" % node - commands += " switch.port=%s" % nic_name - commands += " switch.interface=%s" % interface - if zhcp is not None: - commands += " switch.comments=%s" % zhcp - url = self._xcat_url.tabch("/switch") - body = [commands] - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def _delete_xcat_mac(self, node_name): - """Remove node mac record from xcat mac table.""" - commands = "-d node=%s mac" % node_name - url = self._xcat_url.tabch("/mac") - body = [commands] - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def _delete_xcat_switch(self, node_name): - """Remove node switch record from xcat switch table.""" - commands = "-d node=%s switch" % node_name - url = self._xcat_url.tabch("/switch") - body = [commands] - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def update_xcat_mac(self, node, interface, mac, zhcp=None): - """Add node name, interface, mac address into xcat mac table.""" - commands = "node=%s" % node + " interface=%s" % interface - commands += " mac.mac=%s" % mac - if zhcp is not None: - commands += " mac.comments=%s" % zhcp - url = self._xcat_url.tabch("/mac") - body = [commands] - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def update_xcat_switch(self, node, nic_name, interface, zhcp=None): - """Add node name and nic name address into xcat switch table.""" - commands = "node=%s" % node - commands += " interface=%s" % interface - commands += " switch.port=%s" % nic_name - if zhcp is not None: - commands += " switch.comments=%s" % zhcp - url = self._xcat_url.tabch("/switch") - body = [commands] - - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url, body)['data'] - - def clean_mac_switch_host(self, node_name): - """Clean node records in xCAT mac, host and switch table.""" - self.clean_mac_switch(node_name) - self._delete_xcat_host(node_name) - - def clean_mac_switch(self, node_name): - """Clean node records in xCAT mac and switch table.""" - self._delete_xcat_mac(node_name) - self._delete_xcat_switch(node_name) - - def makehosts(self): - """Update xCAT MN /etc/hosts file.""" - url = self._xcat_url.network("/makehosts") - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url)['data'] - - def makeDNS(self): - """Update xCAT MN DNS.""" - url = self._xcat_url.network("/makedns") - with zvmutils.except_xcat_call_failed_and_reraise( - exception.ZVMNetworkError): - return zvmutils.xcat_request("PUT", url)['data'] - - def config_xcat_mac(self, instance_name): - """Hook xCat to prevent assign MAC for instance.""" - fake_mac_addr = "00:00:00:00:00:00" - nic_name = "fake" - self.add_xcat_mac(instance_name, nic_name, fake_mac_addr) - - def create_xcat_table_about_nic(self, zhcpnode, inst_name, - nic_name, mac_address, vdev): - self._delete_xcat_mac(inst_name) - self.add_xcat_mac(inst_name, vdev, mac_address, zhcpnode) - self.add_xcat_switch(inst_name, nic_name, vdev, zhcpnode) diff --git a/nova_zvm/virt/zvm/opts.py b/nova_zvm/virt/zvm/opts.py deleted file mode 100644 index 141114e..0000000 --- a/nova_zvm/virt/zvm/opts.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2016 IBM Corp. -# -# 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. - - -import itertools - -from nova_zvm.virt.zvm import conf as zvm_conf - - -def list_opts(): - return [ - # Actually it should be [zvm], but for backward compatible issue, - # we keep this into DEFAULT. - ('DEFAULT', - itertools.chain( - zvm_conf.zvm_image_opts, - zvm_conf.zvm_opts, - zvm_conf.zvm_user_opts, - )), - ] diff --git a/nova_zvm/virt/zvm/utils.py b/nova_zvm/virt/zvm/utils.py index ef845d6..dbf5586 100644 --- a/nova_zvm/virt/zvm/utils.py +++ b/nova_zvm/virt/zvm/utils.py @@ -1,4 +1,4 @@ -# Copyright 2013 IBM Corp. +# Copyright 2017,2018 IBM Corp. # # 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 @@ -12,1268 +12,116 @@ # License for the specific language governing permissions and limitations # under the License. -import contextlib -import functools import os -import pwd -import shutil -import six -from six.moves import http_client as httplib -import socket -import ssl -import time - -from nova import block_device -from nova.compute import power_state -from nova import exception as nova_exception -from nova.i18n import _ -from nova.virt import driver -from oslo_config import cfg from oslo_log import log as logging -from oslo_serialization import jsonutils -from oslo_utils import excutils +import six +import six.moves.urllib.parse as urlparse +from zvmconnector import connector -from nova_zvm.virt.zvm import const -from nova_zvm.virt.zvm import dist -from nova_zvm.virt.zvm import exception +from oslo_utils import fileutils + +from nova.api.metadata import base as instance_metadata +from nova import exception +from nova.virt import configdrive +from nova_zvm.virt.zvm import conf +from nova_zvm.virt.zvm import exception as zvm_exception +CONF = conf.CONF LOG = logging.getLogger(__name__) -CONF = cfg.CONF -CONF.import_opt('instances_path', 'nova.compute.manager') +class ConnectorClient(object): + """Request handler to zVM cloud connector""" -_XCAT_URL = None + def __init__(self, zcc_url, ca_file=None): + _url = urlparse.urlparse(zcc_url) + _ssl_enabled = False -class XCATUrl(object): - """To return xCAT URLs for invoking xCAT REST APIs.""" + if _url.scheme == 'https': + _ssl_enabled = True + elif ca_file: + LOG.warning("url is %(url) which is not https " + "but ca_file is configured to %(ca_file)s", + {'url': zcc_url, 'ca_file': ca_file}) - def __init__(self): - """Set constants used to form xCAT URLs.""" - self.PREFIX = '/xcatws' - self.SUFFIX = ('?userName=' + CONF.zvm_xcat_username + - '&password=' + CONF.zvm_xcat_password + - '&format=json') - - self.NODES = '/nodes' - self.VMS = '/vms' - self.IMAGES = '/images' - self.OBJECTS = '/objects/osimage' - self.OS = '/OS' - self.TABLES = '/tables' - self.HV = '/hypervisor' - self.NETWORK = '/networks' - - self.POWER = '/power' - self.INVENTORY = '/inventory' - self.STATUS = '/status' - self.MIGRATE = '/migrate' - self.CAPTURE = '/capture' - self.EXPORT = '/export' - self.IMGIMPORT = '/import' - self.BOOTSTAT = '/bootstate' - self.XDSH = '/dsh' - self.VERSION = '/version' - self.PCONTEXT = '&requestid=' - self.PUUID = '&objectid=' - self.DIAGLOGS = '/logs/diagnostics' - self.PNODERANGE = '&nodeRange=' - self.EXECCMDONVM = '/execcmdonvm' - - def _nodes(self, arg=''): - return self.PREFIX + self.NODES + arg + self.SUFFIX - - def _vms(self, arg='', vmuuid='', context=None): - rurl = self.PREFIX + self.VMS + arg + self.SUFFIX - rurl = self._append_context(rurl, context) - rurl = self._append_instanceid(rurl, vmuuid) - return rurl - - def _append_context(self, rurl, context=None): - # The context is always optional, to allow incremental exploitation of - # the new parameter. When it is present and it has a request ID, xCAT - # logs the request ID so it's easier to link xCAT log entries to - # OpenStack log entries. - if context is not None: - try: - rurl = rurl + self.PCONTEXT + context.request_id - except Exception as err: - # Cannot use format_message() in this context, because the - # Exception class does not implement that method. - msg = _("Failed to append request ID to URL %(url)s : %(err)s" - ) % {'url': rurl, 'err': six.text_type(err)} - LOG.error(msg) - # Continue and return the original URL once the error is logged - # Failing the request over this is NOT desired. - return rurl - - def _append_instanceid(self, rurl, vmuuid): - # The instance ID is always optional. When it is present, xCAT logs it - # so it's easier to link xCAT log entries to OpenStack log entries. - if vmuuid: - rurl = rurl + self.PUUID + vmuuid - return rurl - - def _append_nodeRange(self, rurl, nodeRange): - if nodeRange: - rurl = rurl + self.PNODERANGE + nodeRange - return rurl - - def _hv(self, arg=''): - return self.PREFIX + self.HV + arg + self.SUFFIX - - def _diag_logs(self, arg='', nodeRange='', vmuuid='', context=None): - rurl = self.PREFIX + self.DIAGLOGS + arg + self.SUFFIX - rurl = self._append_nodeRange(rurl, nodeRange) - rurl = self._append_context(rurl, context) - rurl = self._append_instanceid(rurl, vmuuid) - return rurl - - def rpower(self, arg=''): - return self.PREFIX + self.NODES + arg + self.POWER + self.SUFFIX - - def nodels(self, arg=''): - return self._nodes(arg) - - def rinv(self, arg='', addp=None): - rurl = self.PREFIX + self.NODES + arg + self.INVENTORY + self.SUFFIX - return self._append_addp(rurl, addp) - - def mkdef(self, arg=''): - return self._nodes(arg) - - def rmdef(self, arg=''): - return self._nodes(arg) - - def nodestat(self, arg=''): - return self.PREFIX + self.NODES + arg + self.STATUS + self.SUFFIX - - def chvm(self, arg=''): - return self._vms(arg) - - def lsvm(self, arg=''): - return self._vms(arg) - - def chhv(self, arg=''): - return self._hv(arg) - - def mkvm(self, arg='', vmuuid='', context=None): - rurl = self._vms(arg, vmuuid, context) - return rurl - - def rmvm(self, arg='', vmuuid='', context=None): - rurl = self._vms(arg, vmuuid, context) - return rurl - - def tabdump(self, arg='', addp=None): - rurl = self.PREFIX + self.TABLES + arg + self.SUFFIX - return self._append_addp(rurl, addp) - - def _append_addp(self, rurl, addp=None): - if addp is not None: - return rurl + addp + if _ssl_enabled and ca_file: + self._conn = connector.ZVMConnector(_url.hostname, _url.port, + ssl_enabled=_ssl_enabled, + verify=ca_file) else: - return rurl + self._conn = connector.ZVMConnector(_url.hostname, _url.port, + ssl_enabled=_ssl_enabled, + verify=False) - def imgcapture(self, arg=''): - return self.PREFIX + self.IMAGES + arg + self.CAPTURE + self.SUFFIX + def call(self, func_name, *args, **kwargs): + results = self._conn.send_request(func_name, *args, **kwargs) - def imgexport(self, arg=''): - return self.PREFIX + self.IMAGES + arg + self.EXPORT + self.SUFFIX + if results['overallRC'] != 0: + LOG.error("zVM Cloud Connector request %(api)s failed with " + "parameters: %(args)s %(kwargs)s . Results: %(results)s", + {'api': func_name, 'args': six.text_type(args), + 'kwargs': six.text_type(kwargs), + 'results': six.text_type(results)}) + raise zvm_exception.ZVMConnectorError(results=results) - def rmimage(self, arg=''): - return self.PREFIX + self.IMAGES + arg + self.SUFFIX + return results['output'] - def rmobject(self, arg=''): - return self.PREFIX + self.OBJECTS + arg + self.SUFFIX - def lsdef_node(self, arg='', addp=None): - rurl = self.PREFIX + self.NODES + arg + self.SUFFIX - return self._append_addp(rurl, addp) +def _get_instance_path(instance_uuid): + instance_folder = os.path.join(os.path.normpath(CONF.instances_path), + instance_uuid) + fileutils.ensure_tree(instance_folder) + return instance_folder - def lsdef_image(self, arg='', addp=None): - rurl = self.PREFIX + self.IMAGES + arg + self.SUFFIX - return self._append_addp(rurl, addp) - def imgimport(self, arg=''): - return self.PREFIX + self.IMAGES + self.IMGIMPORT + arg + self.SUFFIX +def _create_config_drive(context, instance_path, instance, + injected_files, network_info, admin_password): + if CONF.config_drive_format != 'iso9660': + raise exception.ConfigDriveUnsupportedFormat( + format=CONF.config_drive_format) - def chtab(self, arg=''): - return self.PREFIX + self.NODES + arg + self.SUFFIX + LOG.debug('Using config drive', instance=instance) - def nodeset(self, arg=''): - return self.PREFIX + self.NODES + arg + self.BOOTSTAT + self.SUFFIX + extra_md = {} + if admin_password: + extra_md['admin_pass'] = admin_password - def rmigrate(self, arg=''): - return self.PREFIX + self.NODES + arg + self.MIGRATE + self.SUFFIX + inst_md = instance_metadata.InstanceMetadata(instance, + content=injected_files, + extra_md=extra_md, + network_info=network_info, + request_context=context) - def gettab(self, arg='', addp=None): - rurl = self.PREFIX + self.TABLES + arg + self.SUFFIX - return self._append_addp(rurl, addp) + configdrive_iso = os.path.join(instance_path, 'cfgdrive.iso') + LOG.debug('Creating config drive at %s', configdrive_iso, + instance=instance) + with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb: + cdb.make_drive(configdrive_iso) - def tabch(self, arg='', addp=None): - """Add/update/delete row(s) in table arg, with attribute addp.""" - rurl = self.PREFIX + self.TABLES + arg + self.SUFFIX - return self._append_addp(rurl, addp) + return configdrive_iso - def xdsh(self, arg=''): - """Run shell command.""" - return self.PREFIX + self.NODES + arg + self.XDSH + self.SUFFIX - def execcmdonvm(self, arg=''): - """Run shell command after support IUCV.""" - return self.PREFIX + self.NODES + arg + self.EXECCMDONVM + self.SUFFIX +# Prepare and create configdrive for instance +def generate_configdrive(context, instance, injected_files, + network_info, admin_password): + # Create network configuration files + LOG.debug('Creating config drive configuration files ' + 'for instance: %s', instance.name, instance=instance) - def network(self, arg='', addp=None): - rurl = self.PREFIX + self.NETWORK + arg + self.SUFFIX - if addp is not None: - return rurl + addp - else: - return rurl + instance_path = _get_instance_path(instance.uuid) - def version(self): - return self.PREFIX + self.VERSION + self.SUFFIX + transportfiles = None + if configdrive.required_by(instance): + transportfiles = _create_config_drive(context, instance_path, + instance, + injected_files, + network_info, + admin_password) + return transportfiles - def mkdiag(self, arg='', nodeRange='', vmuuid='', context=None): - rurl = self._diag_logs(arg, nodeRange, vmuuid, context) - return rurl - -class HTTPSClientAuthConnection(httplib.HTTPSConnection): - """For https://wiki.openstack.org/wiki/OSSN/OSSN-0033""" - - def __init__(self, host, port, ca_file, timeout=None, key_file=None, - cert_file=None): - httplib.HTTPSConnection.__init__(self, host, port, - key_file=key_file, - cert_file=cert_file) - self.key_file = key_file - self.cert_file = cert_file - self.ca_file = ca_file - self.timeout = timeout - self.use_ca = True - - if self.ca_file is None: - LOG.debug("no xCAT CA file specified, this is considered " - "not secure") - self.use_ca = False - - def connect(self): - sock = socket.create_connection((self.host, self.port), self.timeout) - if self._tunnel_host: - self.sock = sock - self._tunnel() - - if (self.ca_file is not None and - not os.path.exists(self.ca_file)): - LOG.warning(_("the CA file %(ca_file) does not exist!"), - {'ca_file': self.ca_file}) - self.use_ca = False - - if not self.use_ca: - self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, - cert_reqs=ssl.CERT_NONE) - else: - self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, - ca_certs=self.ca_file, - cert_reqs=ssl.CERT_REQUIRED) - - -def get_xcat_url(): - global _XCAT_URL - - if _XCAT_URL is not None: - return _XCAT_URL - - _XCAT_URL = XCATUrl() - return _XCAT_URL - - -def remove_prefix_of_unicode(str_unicode): - str_unicode = str_unicode.encode('unicode_escape') - str_unicode = str_unicode.replace('\u', '') - str_unicode = str_unicode.decode('utf-8') - return str_unicode - - -class XCATConnection(object): - """Https requests to xCAT web service.""" - - def __init__(self): - """Initialize https connection to xCAT service.""" - self.port = 443 - self.host = CONF.zvm_xcat_server - self.conn = HTTPSClientAuthConnection(self.host, self.port, - CONF.zvm_xcat_ca_file, - timeout=CONF.zvm_xcat_connection_timeout) - - def request(self, method, url, body=None, headers=None): - """Send https request to xCAT server. - - Will return a python dictionary including: - {'status': http return code, - 'reason': http reason, - 'message': response message} - - """ - headers = headers or {} - if body is not None: - body = jsonutils.dumps(body) - headers = {'content-type': 'text/plain', - 'content-length': len(body)} - - _rep_ptn = ''.join(('&password=', CONF.zvm_xcat_password)) - LOG.debug("Sending request to xCAT. xCAT-Server:%(xcat_server)s " - "Request-method:%(method)s " - "URL:%(url)s " - "Headers:%(headers)s " - "Body:%(body)s", - {'xcat_server': CONF.zvm_xcat_server, - 'method': method, - 'url': url.replace(_rep_ptn, ''), # hide password in log - 'headers': str(headers), - 'body': body}) - - try: - self.conn.request(method, url, body, headers) - except socket.gaierror as err: - msg = _("Failed to find address: %s") % err - raise exception.ZVMXCATRequestFailed(xcatserver=self.host, msg=msg) - except (socket.error, socket.timeout) as err: - msg = _("Communication error: %s") % err - raise exception.ZVMXCATRequestFailed(xcatserver=self.host, msg=msg) - - try: - res = self.conn.getresponse() - except Exception as err: - msg = _("Failed to get response from xCAT: %s") % err - raise exception.ZVMXCATRequestFailed(xcatserver=self.host, msg=msg) - - msg = res.read() - resp = { - 'status': res.status, - 'reason': res.reason, - 'message': msg} - - LOG.debug("xCAT response: %s", str(resp)) - - # Only "200" or "201" returned from xCAT can be considered - # as good status. - err = None - need_retry = 0 - - # 503 is special handle here, we need change it and logs below - # if additional retry condition need to be added. - if res.status != 503: - if method == "POST": - if res.status != 201: - err = str(resp) - else: - if res.status != 200: - err = str(resp) - else: - need_retry = 1 - - if err is not None: - raise exception.ZVMXCATRequestFailed(xcatserver=self.host, - msg=err) - - return need_retry, resp - - -def xcat_request(method, url, body=None, headers=None, ignore_warning=False): - headers = headers or {} - conn = XCATConnection() - - # Maximum allow 5 retry for service unavailable - _rep_ptn = ''.join(('&password=', CONF.zvm_xcat_password)) - - max_attempts = 5 - retry_attempts = max_attempts - - while (retry_attempts > 0): - need_retry, resp = conn.request(method, url, body, headers) - if not need_retry: - ret = load_xcat_resp(resp['message'], - ignore_warning=ignore_warning) - # Yes, we finished the request, let's return or handle error - return ret - - LOG.info(_("xCAT encounter service handling error (http 503), " - "Attempt %(retry)s of %(max_retry)s " - "request: xCAT-Server: %(xcat_server)s " - "Request-method: %(method)s " - "URL: %(url)s."), - {'retry': max_attempts - retry_attempts + 1, - 'max_retry': max_attempts, - 'xcat_server': CONF.zvm_xcat_server, - 'method': method, - 'url': url.replace(_rep_ptn, '')}) - - retry_attempts -= 1 - if retry_attempts > 0: - time.sleep(2) - - LOG.warning(_("xCAT encounter service handling error (http 503), " - "Retried %(max_retry)s times but still failed. " - "request: xCAT-Server: %(xcat_server)s " - "Request-method: %(method)s " - "URL: %(url)s."), - {'max_retry': max_attempts, - 'xcat_server': CONF.zvm_xcat_server, - 'method': method, - 'url': url.replace(_rep_ptn, '')}) - ret = load_xcat_resp(resp['message'], - ignore_warning=ignore_warning) - - # ok, we tried all we can do here, let upper layer handle this error. - return ret - - -def jsonloads(jsonstr): - try: - return jsonutils.loads(jsonstr) - except ValueError: - errmsg = _("xCAT response data is not in JSON format") - LOG.error(errmsg) - raise exception.ZVMDriverError(msg=errmsg) - - -@contextlib.contextmanager -def expect_invalid_xcat_resp_data(data=''): - """Catch exceptions when using xCAT response data.""" - try: - yield - except (ValueError, TypeError, IndexError, AttributeError, - KeyError) as err: - LOG.error(_('Parse %s encounter error'), data) - raise exception.ZVMInvalidXCATResponseDataError(msg=err) - - -def wrap_invalid_xcat_resp_data_error(function): - """Catch exceptions when using xCAT response data.""" - - @functools.wraps(function) - def decorated_function(*arg, **kwargs): - try: - return function(*arg, **kwargs) - except (ValueError, TypeError, IndexError, AttributeError, - KeyError) as err: - raise exception.ZVMInvalidXCATResponseDataError(msg=err) - - return decorated_function - - -@contextlib.contextmanager -def ignore_errors(): - """Only execute the clauses and ignore the results.""" - - try: - yield - except Exception as err: - emsg = format_exception_msg(err) - LOG.debug("Ignore an error: %s", emsg) - pass - - -@contextlib.contextmanager -def except_xcat_call_failed_and_reraise(exc, **kwargs): - """Catch all kinds of xCAT call failure and reraise. - - exc: the exception that would be raised. - """ - try: - yield - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError) as err: - msg = err.format_message() - kwargs['msg'] = msg - LOG.error(_('XCAT response return error: %s'), msg) - raise exc(**kwargs) - - -def convert_to_mb(s): - """Convert memory size from GB to MB.""" - s = s.upper() - try: - if s.endswith('G'): - return float(s[:-1].strip()) * 1024 - elif s.endswith('T'): - return float(s[:-1].strip()) * 1024 * 1024 - else: - return float(s[:-1].strip()) - except (IndexError, ValueError, KeyError, TypeError) as e: - errmsg = _("Invalid memory format: %s") % e - raise exception.ZVMDriverError(msg=errmsg) - - -@wrap_invalid_xcat_resp_data_error -def translate_xcat_resp(rawdata, dirt): - """Translate xCAT response JSON stream to a python dictionary. - - xCAT response example: - node: keyword1: value1\n - node: keyword2: value2\n - ... - node: keywordn: valuen\n - - Will return a python dictionary: - {keyword1: value1, - keyword2: value2, - ... - keywordn: valuen,} - - """ - data_list = rawdata.split("\n") - - data = {} - - for ls in data_list: - for k in list(dirt.keys()): - if ls.__contains__(dirt[k]): - data[k] = ls[(ls.find(dirt[k]) + len(dirt[k])):].strip() - break - - if data == {}: - msg = _("No value matched with keywords. Raw Data: %(raw)s; " - "Keywords: %(kws)s") % {'raw': rawdata, 'kws': str(dirt)} - raise exception.ZVMInvalidXCATResponseDataError(msg=msg) - - return data - - -def mapping_power_stat(power_stat): - """Translate power state to OpenStack defined constants.""" - return const.ZVM_POWER_STAT.get(power_stat, power_state.NOSTATE) - - -@wrap_invalid_xcat_resp_data_error -def load_xcat_resp(message, ignore_warning=False): - """Abstract information from xCAT REST response body. - - As default, xCAT response will in format of JSON and can be - converted to Python dictionary, would looks like: - {"data": [{"info": [info,]}, {"data": [data,]}, ..., {"error": [error,]}]} - - Returns a Python dictionary, looks like: - {'info': [info,], - 'data': [data,], - ... - 'error': [error,]} - - """ - resp_list = jsonloads(message)['data'] - keys = const.XCAT_RESPONSE_KEYS - - resp = {} - - for k in keys: - resp[k] = [] - - for d in resp_list: - for k in keys: - if d.get(k) is not None: - resp[k].append(d.get(k)) - - err = resp.get('error') - if err != []: - for e in err: - if _is_warning_or_recoverable_issue(str(e)): - # ignore known warnings or errors: - continue - else: - raise exception.ZVMXCATInternalError(msg=message) - - if not ignore_warning: - _log_warnings(resp) - - return resp - - -def _log_warnings(resp): - for msg in (resp['info'], resp['node'], resp['data']): - msgstr = str(msg) - if 'warn' in msgstr.lower(): - LOG.info(_("Warning from xCAT: %s"), msgstr) - - -def _is_warning_or_recoverable_issue(err_str): - return _is_warning(err_str) or _is_recoverable_issue(err_str) - - -def _is_recoverable_issue(err_str): - dirmaint_request_counter_save = ['Return Code: 596', 'Reason Code: 1185'] - dirmaint_request_limit = ['Return Code: 596', 'Reason Code: 6312'] - recoverable_issues = [dirmaint_request_counter_save, - dirmaint_request_limit] - for issue in recoverable_issues: - # Search all matchs in the return value - # any mismatch leads to recoverable not empty - recoverable = [t for t in issue if t not in err_str] - if recoverable == []: - return True - - return False - - -def _is_warning(err_str): - ignore_list = ( - 'Warning: the RSA host key for', - 'Warning: Permanently added', - 'WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED', - ) - - for im in ignore_list: - if im in err_str: - return True - - return False - - -def _volume_in_mapping(mount_device, block_device_info): - block_device_list = [block_device.strip_dev(vol['mount_device']) - for vol in - driver.block_device_info_get_mapping( - block_device_info)] - LOG.debug("block_device_list %s", block_device_list) - return block_device.strip_dev(mount_device) in block_device_list - - -def is_volume_root(root_device, mountpoint): - """This judges if the moutpoint equals the root_device.""" - return block_device.strip_dev(mountpoint) == block_device.strip_dev( - root_device) - - -def is_boot_from_volume(block_device_info): - root_mount_device = driver.block_device_info_get_root(block_device_info) - boot_from_volume = _volume_in_mapping(root_mount_device, - block_device_info) - return root_mount_device, boot_from_volume - - -def get_host(): - return ''.join([pwd.getpwuid(os.geteuid()).pw_name, '@', CONF.my_ip]) - - -def get_xcat_version(): - """Return the version of xCAT""" - url = get_xcat_url().version() - data = xcat_request('GET', url)['data'] - - with expect_invalid_xcat_resp_data(data): - version = data[0][0].split()[1] - version = version.strip() - return version - - -def xcat_support_chvm_smcli(): - """Return true if xCAT version support clone""" - xcat_version = get_xcat_version() - return map(int, xcat_version.split('.')) >= map(int, - const.XCAT_SUPPORT_CHVM_SMCLI_VERSION.split('.')) - - -def xcat_support_mkvm_ipl_param(xcat_version): - return map(int, xcat_version.split('.')) >= map(int, - const.XCAT_MKVM_SUPPORT_IPL.split('.')) - - -def xcat_support_deployment_failure_diagnostics(xcat_version): - """Return true if xCAT version supports deployment failure diagnostics""" - return map(int, xcat_version.split('.')) >= map(int, - const.XCAT_SUPPORT_COLLECT_DIAGNOSTICS_DEPLOYFAILED.split('.')) - - -def xcat_support_iucv(xcat_version=None): - if xcat_version is None: - xcat_version = get_xcat_version() - return map(int, xcat_version.split('.')) >= map(int, - const.XCAT_SUPPORT_IUCV.split('.')) - - -def get_userid(node_name): - """Returns z/VM userid for the xCAT node.""" - url = get_xcat_url().lsdef_node(''.join(['/', node_name])) - info = xcat_request('GET', url) - with expect_invalid_xcat_resp_data(info): - for s in info['info'][0]: - if s.__contains__('userid='): - return s.strip().rpartition('=')[2] - - -def xdsh(node, commands): - """"Run command on xCAT node.""" - LOG.debug('Run command %(cmd)s on xCAT node %(node)s', - {'cmd': commands, 'node': node}) - - def xdsh_execute(node, commands): - """Invoke xCAT REST API to execute command on node.""" - xdsh_commands = 'command=%s' % commands - # Add -q (quiet) option to ignore ssh warnings and banner msg - opt = 'options=-q' - body = [xdsh_commands, opt] - url = get_xcat_url().xdsh('/' + node) - return xcat_request("PUT", url, body) - - with except_xcat_call_failed_and_reraise( - exception.ZVMXCATXdshFailed): - res_dict = xdsh_execute(node, commands) - - return res_dict - - -def execcmdonvm(node, commands): - """"Run command on VM.""" - LOG.debug('Run command %(cmd)s on VM %(node)s', - {'cmd': commands, 'node': node}) - - body = [commands] - url = get_xcat_url().execcmdonvm('/' + node) - return xcat_request("PUT", url, body) - - -def punch_file(node, fn, fclass, remote_host=None, del_src=True): - """punch file to reader. """ - if remote_host: - body = [" ".join(['--punchfile', fn, fclass, remote_host])] - else: - body = [" ".join(['--punchfile', fn, fclass])] - url = get_xcat_url().chvm('/' + node) - - try: - xcat_request("PUT", url, body) - except Exception as err: - emsg = format_exception_msg(err) - with excutils.save_and_reraise_exception(): - LOG.error(_('Punch file to %(node)s failed: %(msg)s'), - {'node': node, 'msg': emsg}) - finally: - if del_src: - os.remove(fn) - - -def punch_adminpass_file(instance_path, instance_name, admin_password, - linuxdist): - adminpass_fn = ''.join([instance_path, '/adminpwd.sh']) - _generate_adminpass_file(adminpass_fn, admin_password, linuxdist) - punch_file(instance_name, adminpass_fn, 'X', remote_host=get_host()) - - -def _generate_iucv_cmd_file(iucv_cmd_file_path, cmd): - lines = ['#!/bin/bash\n', cmd] - with open(iucv_cmd_file_path, 'w') as f: - f.writelines(lines) - - -def add_iucv_in_zvm_table(instance_name): - result = xcat_cmd_gettab('zvm', 'node', instance_name, "status") - if not result: - status = "IUCV=1" - else: - status = result + ';IUCV=1' - xcat_cmd_settab('zvm', 'node', instance_name, "status", status) - - -def copy_zvm_table_status(des_inst_name, src_inst_name): - status = xcat_cmd_gettab('zvm', 'node', src_inst_name, "status") - xcat_cmd_settab('zvm', 'node', des_inst_name, "status", status) - - -def punch_iucv_file(os_ver, zhcp, zhcp_userid, instance_name, - instance_path): - """put iucv server and service files to reader.""" - xcat_iucv_path = '/opt/zhcp/bin/IUCV' - # generate iucvserver file - iucv_server_fn = ''.join([xcat_iucv_path, '/iucvserv']) - iucv_server_path = '/usr/bin/iucvserv' - punch_path = '/var/opt/xcat/transport' - # generate iucvserver service and script file - iucv_service_path = '' - start_iucv_service_sh = '' - (distro, release) = dist.ListDistManager().parse_dist(os_ver) - if((distro == "rhel" and release < '7') or ( - distro == "sles" and release < '12')): - iucv_serverd_fn = ''.join([xcat_iucv_path, '/iucvserd']) - iucv_service_name = 'iucvserd' - iucv_service_path = '/etc/init.d/' + iucv_service_name - start_iucv_service_sh = 'chkconfig --add iucvserd 2>&1\n' - start_iucv_service_sh += 'service iucvserd start 2>&1\n' - else: - iucv_serverd_fn = ''.join([xcat_iucv_path, '/iucvserd.service']) - iucv_service_name = 'iucvserd.service' - start_iucv_service_sh = 'systemctl enable iucvserd 2>&1\n' - start_iucv_service_sh += 'systemctl start iucvserd 2>&1\n' - if((distro == "rhel" and release >= '7') or (distro == "ubuntu")): - iucv_service_path = '/lib/systemd/system/' + iucv_service_name - if(distro == "sles" and release >= '12'): - iucv_service_path = '/usr/lib/systemd/system/' + iucv_service_name - - iucv_cmd_file_path = instance_path + '/iucvexec.sh' - cmd = '\n'.join(( - # if when execute this script, conf4z hasn't received iucv file. - "spoolid=`vmur li | awk '/iucvserv/{print \$2}'|tail -1`", - "vmur re -f $spoolid /usr/bin/iucvserv 2>&1 >/var/log/messages", - "spoolid=`vmur li | awk '/iucvserd/{print \$2}'|tail -1`", - "vmur re -f $spoolid %s 2>&1 >/var/log/messages" % iucv_service_path, - # if conf4z has received iucv files first. - "cp -rf %s/iucvserv %s 2>&1 >/var/log/messages" % (punch_path, - iucv_server_path), - "cp -rf %s/%s %s 2>&1 >/var/log/messages" % (punch_path, - iucv_service_name, iucv_service_path), - "chmod +x %s %s" % (iucv_server_path, iucv_service_path), - "echo -n %s >/etc/iucv_authorized_userid 2>&1" % zhcp_userid, - start_iucv_service_sh - )) - _generate_iucv_cmd_file(iucv_cmd_file_path, cmd) - punch_file(instance_name, iucv_server_fn, 'X', - remote_host=zhcp, del_src=False) - punch_file(instance_name, iucv_serverd_fn, 'X', - remote_host=zhcp, del_src=False) - punch_file(instance_name, iucv_cmd_file_path, 'X', remote_host=get_host(), - del_src=False) - # set VM's communicate type is IUCV - add_iucv_in_zvm_table(instance_name) - - -def punch_iucv_authorized_file(old_inst_name, new_inst_name, zhcp_userid): - cmd = "echo -n %s >/etc/iucv_authorized_userid 2>&1" % zhcp_userid - iucv_cmd_file_path = '/tmp/%s.sh' % new_inst_name[-8:] # nosec - _generate_iucv_cmd_file(iucv_cmd_file_path, cmd) - punch_file(new_inst_name, iucv_cmd_file_path, 'X', remote_host=get_host()) - if old_inst_name != new_inst_name: - # set VM's communicate type the same as original VM - copy_zvm_table_status(old_inst_name, new_inst_name) - - -def process_eph_disk(instance_name, vdev=None, fmt=None, mntdir=None): - if not fmt: - fmt = CONF.default_ephemeral_format or const.DEFAULT_EPH_DISK_FMT - vdev = vdev or CONF.zvm_user_adde_vdev - mntdir = mntdir or CONF.zvm_default_ephemeral_mntdir - - eph_parms = _generate_eph_parmline(vdev, fmt, mntdir) - aemod_handler(instance_name, const.DISK_FUNC_NAME, eph_parms) - - -def aemod_handler(instance_name, func_name, parms): - url = get_xcat_url().chvm('/' + instance_name) - body = [" ".join(['--aemod', func_name, parms])] - - try: - xcat_request("PUT", url, body) - except Exception as err: - emsg = format_exception_msg(err) - with excutils.save_and_reraise_exception(): - LOG.error(_('Invoke AE method function: %(func)s on %(node)s ' - 'failed with reason: %(msg)s'), - {'func': func_name, 'node': instance_name, 'msg': emsg}) - - -def punch_configdrive_file(transportfiles, instance_name): - punch_file(instance_name, transportfiles, 'X', remote_host=get_host()) - - -def punch_zipl_file(instance_path, instance_name, lun, wwpn, fcp, volume_meta): - zipl_fn = ''.join([instance_path, '/ziplset.sh']) - _generate_zipl_file(zipl_fn, lun, wwpn, fcp, volume_meta) - punch_file(instance_name, zipl_fn, 'X', remote_host=get_host()) - - -def generate_vdev(base, offset=1): - """Generate virtual device number base on base vdev. - - :param base: base virtual device number, string of 4 bit hex. - :param offset: offset to base, integer. - - :output: virtual device number, string of 4 bit hex. - """ - vdev = hex(int(base, 16) + offset)[2:] - return vdev.rjust(4, '0') - - -def generate_eph_vdev(offset=1): - """Generate virtual device number for ephemeral disks. - - :parm offset: offset to zvm_user_adde_vdev. - - :output: virtual device number, string of 4 bit hex. - """ - vdev = generate_vdev(CONF.zvm_user_adde_vdev, offset + 1) - if offset >= 0 and offset < 254: - return vdev - else: - msg = _("Invalid virtual device number for ephemeral disk: %s") % vdev - LOG.error(msg) - raise exception.ZVMDriverError(msg=msg) - - -def _generate_eph_parmline(vdev, fmt, mntdir): - - parms = [ - 'action=addMdisk', - 'vaddr=' + vdev, - 'filesys=' + fmt, - 'mntdir=' + mntdir - ] - parmline = ' '.join(parms) - return parmline - - -def _generate_auth_file(fn, pub_key): - lines = ['#!/bin/bash\n', - 'echo "%s" >> /root/.ssh/authorized_keys' % pub_key] - with open(fn, 'w') as f: - f.writelines(lines) - - -def _generate_adminpass_file(fn, admin_password, linuxdist): - pwd_str = linuxdist.get_change_passwd_command(admin_password) - lines = ['#!/bin/bash\n', pwd_str] - with open(fn, 'w') as f: - f.writelines(lines) - - -def _generate_zipl_file(fn, lun, wwpn, fcp, volume_meta): - image = volume_meta['image'] - ramdisk = volume_meta['ramdisk'] - root = volume_meta['root'] - os_version = volume_meta['os_version'] - dist_manager = dist.ListDistManager() - linux_dist = dist_manager.get_linux_dist(os_version)() - zipl_script_lines = linux_dist.get_zipl_script_lines( - image, ramdisk, root, fcp, wwpn, lun) - with open(fn, 'w') as f: - f.writelines(zipl_script_lines) - - -@wrap_invalid_xcat_resp_data_error -def get_mn_pub_key(): - cmd = 'cat /root/.ssh/id_rsa.pub' - resp = xdsh(CONF.zvm_xcat_master, cmd) - key = resp['data'][0][0] - start_idx = key.find('ssh-rsa') - key = key[start_idx:] - return key - - -def parse_os_version(os_version): - """Separate os and version from os_version. - Possible return value are only: - ('rhel', x.y) and ('sles', x.y) where x.y may not be digits - """ - supported = {'rhel': ['rhel', 'redhat', 'red hat'], - 'sles': ['suse', 'sles']} - os_version = os_version.lower() - for distro, patterns in supported.items(): - for i in patterns: - if os_version.startswith(i): - # Not guarrentee the version is digital - return distro, os_version.split(i, 2)[1] - else: - raise exception.ZVMImageError(msg='Unknown os_version property') - - -def xcat_cmd_gettab(table, col, col_value, attr): - addp = ("&col=%(col)s=%(col_value)s&attribute=%(attr)s" % - {'col': col, 'col_value': col_value, 'attr': attr}) - url = get_xcat_url().gettab('/%s' % table, addp) - res_info = xcat_request("GET", url) - with expect_invalid_xcat_resp_data(res_info): - if res_info['data']: - return res_info['data'][0][0] - else: - return '' - - -def xcat_cmd_settab(table, col, col_value, attr, value): - """Add status value for node in database table.""" - commands = ("%(col)s=%(col_value)s %(table)s.%(attr)s=%(value)s" % - {'col': col, 'col_value': col_value, - 'attr': attr, 'table': table, 'value': value}) - url = get_xcat_url().tabch("/%s" % table) - body = [commands] - with expect_invalid_xcat_resp_data(): - return xcat_request("PUT", url, body)['data'] - - -def xcat_cmd_gettab_multi_attr(table, col, col_value, attr_list): - attr_str = ''.join(["&attribute=%s" % attr for attr in attr_list]) - addp = ("&col=%(col)s=%(col_value)s&%(attr)s" % - {'col': col, 'col_value': col_value, 'attr': attr_str}) - url = get_xcat_url().gettab('/%s' % table, addp) - res_data = xcat_request("GET", url)['data'] - - outp = {} - with expect_invalid_xcat_resp_data(res_data): - for attr in attr_list: - for data in res_data: - if attr in data[0]: - outp[attr] = data[0].rpartition(':')[2].strip() - res_data.remove(data) - break - - return outp - - -def format_exception_msg(exc_obj): - """Return message string from nova exceptions and common exceptions.""" - if isinstance(exc_obj, nova_exception.NovaException): - return exc_obj.format_message() - else: - return str(exc_obj) - - -def looping_call(f, sleep=5, inc_sleep=0, max_sleep=60, timeout=600, - exceptions=(), *args, **kwargs): - """Helper function that to run looping call with fixed/dynamical interval. - - :param f: the looping call function or method. - :param sleep: initial interval of the looping calls. - :param inc_sleep: sleep time increment, default as 0. - :param max_sleep: max sleep time. - :param timeout: looping call timeout in seconds, 0 means no timeout. - :param exceptions: exceptions that trigger re-try. - - """ - time_start = time.time() - expiration = time_start + timeout - - retry = True - while retry: - expired = timeout and (time.time() > expiration) - - try: - f(*args, **kwargs) - except exceptions: - retry = not expired - if retry: - LOG.debug("Will re-try %(fname)s in %(itv)d seconds", - {'fname': f.__name__, 'itv': sleep}) - time.sleep(sleep) - sleep = min(sleep + inc_sleep, max_sleep) - else: - LOG.debug("Looping call %s timeout", f.__name__) - continue - retry = False - - -class PathUtils(object): - def open(self, path, mode): - """Wrapper on six.moves.builtins.open used to simplify unit testing.""" - return six.moves.builtins.open(path, mode) - - def _get_image_tmp_path(self): - image_tmp_path = os.path.normpath(CONF.zvm_image_tmp_path) - if not os.path.exists(image_tmp_path): - LOG.debug('Creating folder %s for image temp files', - image_tmp_path) - os.makedirs(image_tmp_path) - return image_tmp_path - - def get_bundle_tmp_path(self, tmp_file_fn): - bundle_tmp_path = os.path.join(self._get_image_tmp_path(), "spawn_tmp", - tmp_file_fn) - if not os.path.exists(bundle_tmp_path): - LOG.debug('Creating folder %s for image bundle temp file', - bundle_tmp_path) - os.makedirs(bundle_tmp_path) - return bundle_tmp_path - - def get_img_path(self, bundle_file_path, image_name): - return os.path.join(bundle_file_path, image_name) - - def _get_snapshot_path(self): - snapshot_folder = os.path.join(self._get_image_tmp_path(), - "snapshot_tmp") - if not os.path.exists(snapshot_folder): - LOG.debug("Creating the snapshot folder %s", snapshot_folder) - os.makedirs(snapshot_folder) - return snapshot_folder - - def _get_punch_path(self): - punch_folder = os.path.join(self._get_image_tmp_path(), "punch_tmp") - if not os.path.exists(punch_folder): - LOG.debug("Creating the punch folder %s", punch_folder) - os.makedirs(punch_folder) - return punch_folder - - def get_spawn_folder(self): - spawn_folder = os.path.join(self._get_image_tmp_path(), "spawn_tmp") - if not os.path.exists(spawn_folder): - LOG.debug("Creating the spawn folder %s", spawn_folder) - os.makedirs(spawn_folder) - return spawn_folder - - def make_time_stamp(self): - tmp_file_fn = time.strftime('%Y%m%d%H%M%S', - time.localtime(time.time())) - return tmp_file_fn - - def get_snapshot_time_path(self): - snapshot_time_path = os.path.join(self._get_snapshot_path(), - self.make_time_stamp()) - if not os.path.exists(snapshot_time_path): - LOG.debug('Creating folder %s for image bundle temp file', - snapshot_time_path) - os.makedirs(snapshot_time_path) - return snapshot_time_path - - def clean_temp_folder(self, tmp_file_fn): - if os.path.isdir(tmp_file_fn): - LOG.debug('Removing existing folder %s ', tmp_file_fn) - shutil.rmtree(tmp_file_fn) - - def _get_instances_path(self): - return os.path.normpath(CONF.instances_path) - - def get_instance_path(self, os_node, instance_name): - instance_folder = os.path.join(self._get_instances_path(), os_node, - instance_name) - if not os.path.exists(instance_folder): - LOG.debug("Creating the instance path %s", instance_folder) - os.makedirs(instance_folder) - return instance_folder - - def get_console_log_path(self, os_node, instance_name): - return os.path.join(self.get_instance_path(os_node, instance_name), - "console.log") - - -class NetworkUtils(object): - """Utilities for z/VM network operator.""" - - def validate_ip_address(self, ip_address): - """Check whether ip_address is valid.""" - # TODO(Leon): check IP address format - pass - - def validate_network_mask(self, mask): - """Check whether mask is valid.""" - # TODO(Leon): check network mask format - pass - - def create_network_configuration_files(self, file_path, network_info, - base_vdev, os_type): - """Generate network configuration files to instance.""" - device_num = 0 - cfg_files = [] - cmd_strings = '' - udev_cfg_str = '' - dns_cfg_str = '' - route_cfg_str = '' - cmd_str = None - cfg_str = '' - # Red Hat - file_path_rhel = '/etc/sysconfig/network-scripts/' - # SuSE - file_path_sles = '/etc/sysconfig/network/' - file_name_route = file_path_sles + 'routes' - - # Check the OS type - if (os_type == 'sles'): - file_path = file_path_sles - else: - file_path = file_path_rhel - file_name_dns = '/etc/resolv.conf' - for vif in network_info: - file_name = 'ifcfg-eth' + str(device_num) - network = vif['network'] - (cfg_str, cmd_str, dns_str, - route_str) = self.generate_network_configration(network, - base_vdev, device_num, os_type) - LOG.debug('Network configure file content is: %s', cfg_str) - target_net_conf_file_name = file_path + file_name - cfg_files.append((target_net_conf_file_name, cfg_str)) - udev_cfg_str += self.generate_udev_configuration(device_num, - '0.0.' + str(base_vdev).zfill(4)) - if cmd_str is not None: - cmd_strings += cmd_str - if len(dns_str) > 0: - dns_cfg_str += dns_str - if len(route_str) > 0: - route_cfg_str += route_str - base_vdev = str(hex(int(base_vdev, 16) + 3))[2:] - device_num += 1 - - if len(dns_cfg_str) > 0: - cfg_files.append((file_name_dns, dns_cfg_str)) - if os_type == 'sles': - udev_file_name = '/etc/udev/rules.d/70-persistent-net.rules' - cfg_files.append((udev_file_name, udev_cfg_str)) - if len(route_cfg_str) > 0: - cfg_files.append((file_name_route, route_cfg_str)) - - return cfg_files, cmd_strings - - def generate_network_configration(self, network, vdev, device_num, - os_type): - """Generate network configuration items.""" - ip_v4 = netmask_v4 = gateway_v4 = broadcast_v4 = '' - subchannels = None - device = None - cidr_v4 = None - cmd_str = None - dns_str = '' - route_str = '' - - subnets_v4 = [s for s in network['subnets'] if s['version'] == 4] - - if len(subnets_v4[0]['ips']) > 0: - ip_v4 = subnets_v4[0]['ips'][0]['address'] - if len(subnets_v4[0]['dns']) > 0: - for dns in subnets_v4[0]['dns']: - dns_str += 'nameserver ' + dns['address'] + '\n' - - netmask_v4 = str(subnets_v4[0].as_netaddr().netmask) - gateway_v4 = subnets_v4[0]['gateway']['address'] or '' - broadcast_v4 = str(subnets_v4[0].as_netaddr().broadcast) - device = "eth" + str(device_num) - address_read = str(vdev).zfill(4) - address_write = str(hex(int(vdev, 16) + 1))[2:].zfill(4) - address_data = str(hex(int(vdev, 16) + 2))[2:].zfill(4) - subchannels = '0.0.%s' % address_read.lower() - subchannels += ',0.0.%s' % address_write.lower() - subchannels += ',0.0.%s' % address_data.lower() - - cfg_str = 'DEVICE=\"' + device + '\"\n' + 'BOOTPROTO=\"static\"\n' - cfg_str += 'BROADCAST=\"' + broadcast_v4 + '\"\n' - cfg_str += 'GATEWAY=\"' + gateway_v4 + '\"\nIPADDR=\"' + ip_v4 + '\"\n' - cfg_str += 'NETMASK=\"' + netmask_v4 + '\"\n' - cfg_str += 'NETTYPE=\"qeth\"\nONBOOT=\"yes\"\n' - cfg_str += 'PORTNAME=\"PORT' + address_read + '\"\n' - cfg_str += 'OPTIONS=\"layer2=1\"\n' - cfg_str += 'SUBCHANNELS=\"' + subchannels + '\"\n' - - if os_type == 'sles': - cidr_v4 = self._get_cidr_from_ip_netmask(ip_v4, netmask_v4) - cmd_str = 'qeth_configure -l 0.0.%s ' % address_read.lower() - cmd_str += '0.0.%(write)s 0.0.%(data)s 1\n' % {'write': - address_write.lower(), 'data': address_data.lower()} - cfg_str = "BOOTPROTO=\'static\'\nIPADDR=\'%s\'\n" % cidr_v4 - cfg_str += "BROADCAST=\'%s\'\n" % broadcast_v4 - cfg_str += "STARTMODE=\'onboot\'\n" - cfg_str += ("NAME=\'OSA Express Network card (%s)\'\n" % - address_read) - route_str += 'default %s - -\n' % gateway_v4 - - return cfg_str, cmd_str, dns_str, route_str - - def generate_udev_configuration(self, device, dev_channel): - cfg_str = 'SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"qeth\",' - cfg_str += ' KERNELS==\"%s\", ATTR{type}==\"1\",' % dev_channel - cfg_str += ' KERNEL==\"eth*\", NAME=\"eth%s\"\n' % device - - return cfg_str - - def _get_cidr_from_ip_netmask(self, ip, netmask): - netmask_fields = netmask.split('.') - bin_str = '' - for octet in netmask_fields: - bin_str += bin(int(octet))[2:].zfill(8) - mask = str(len(bin_str.rstrip('0'))) - cidr_v4 = ip + '/' + mask - return cidr_v4 +def clean_up_file(filepath): + if os.path.exists(filepath): + os.remove(filepath) diff --git a/nova_zvm/virt/zvm/volumeop.py b/nova_zvm/virt/zvm/volumeop.py deleted file mode 100644 index c247f9b..0000000 --- a/nova_zvm/virt/zvm/volumeop.py +++ /dev/null @@ -1,1088 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -import contextlib -import re -import six -import time - -import nova.context -from nova.i18n import _ -from nova.objects import block_device as block_device_obj -from nova.objects import instance as instance_obj -from nova.volume import cinder -from oslo_config import cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils -from oslo_utils import excutils - -from nova_zvm.virt.zvm import const -from nova_zvm.virt.zvm import dist -from nova_zvm.virt.zvm import exception -from nova_zvm.virt.zvm import utils as zvmutils - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class VolumeOperator(object): - """Volume operator on IBM z/VM platform.""" - - _svc_driver_obj = None - - def __init__(self): - if not VolumeOperator._svc_driver_obj: - VolumeOperator._svc_driver_obj = SVCDriver() - self._svc_driver = VolumeOperator._svc_driver_obj - - def init_host(self, host_stats): - try: - self._svc_driver.init_host(host_stats) - except (exception.ZVMDriverError, exception.ZVMVolumeError) as err: - LOG.warning(_("Initialize zhcp failed. Reason: %s"), - err.format_message()) - - def attach_volume_to_instance(self, context, connection_info, instance, - mountpoint, is_active, rollback=True): - """Attach a volume to an instance.""" - - if not connection_info: - errmsg = _("Missing required parameters: connection_info.") - raise exception.ZVMDriverError(msg=errmsg) - - if not instance: - errmsg = _("Missing required parameters: instance.") - raise exception.ZVMDriverError(msg=errmsg) - - LOG.debug("Attach a volume to an instance. conn_info: %(info)s; " + - "instance: %(name)s; mountpoint: %(point)s", - {'info': connection_info, 'name': instance['name'], - 'point': mountpoint}) - - if is_active: - self._svc_driver.attach_volume_active(context, connection_info, - instance, mountpoint, - rollback) - else: - self._svc_driver.attach_volume_inactive(context, connection_info, - instance, mountpoint, - rollback) - - def detach_volume_from_instance(self, connection_info, instance, - mountpoint, is_active, rollback=True): - """Detach a volume from an instance.""" - - if not connection_info: - errmsg = _("Missing required parameters: connection_info.") - raise exception.ZVMDriverError(msg=errmsg) - - if not instance: - errmsg = _("Missing required parameters: instance.") - raise exception.ZVMDriverError(msg=errmsg) - - LOG.debug("Detach a volume from an instance. conn_info: %(info)s; " + - "instance: %(name)s; mountpoint: %(point)s", - {'info': connection_info, 'name': instance['name'], - 'point': mountpoint}) - - if is_active: - self._svc_driver.detach_volume_active(connection_info, instance, - mountpoint, rollback) - else: - self._svc_driver.detach_volume_inactive(connection_info, instance, - mountpoint, rollback) - - def get_volume_connector(self, instance): - if not instance: - errmsg = _("Instance must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - return self._svc_driver.get_volume_connector(instance) - - def has_persistent_volume(self, instance): - if not instance: - errmsg = _("Instance must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - return self._svc_driver.has_persistent_volume(instance) - - def extract_connection_info(self, context, connection_info): - if not connection_info: - errmsg = _("Connection_info must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - return self._svc_driver._extract_connection_info(context, - connection_info) - - def get_root_volume_connection_info(self, bdm_list, root_device): - if not bdm_list: - errmsg = _("Block_device_mappings must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - - if not root_device: - errmsg = _("root_device must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - - for bdm in bdm_list: - if zvmutils.is_volume_root(bdm['mount_device'], root_device): - return bdm['connection_info'] - errmsg = _("Failed to get connection info of root volume.") - raise exception.ZVMDriverError(msg=errmsg) - - def volume_boot_init(self, instance, fcp): - if not instance: - errmsg = _("instance must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - if not fcp: - errmsg = _("fcp must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - self._svc_driver.volume_boot_init(instance, fcp) - - def volume_boot_cleanup(self, instance, fcp): - if not instance: - errmsg = _("instance must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - if not fcp: - errmsg = _("fcp must be provided.") - raise exception.ZVMDriverError(msg=errmsg) - self._svc_driver.volume_boot_cleanup(instance, fcp) - - -@contextlib.contextmanager -def wrap_internal_errors(): - """Wrap internal exceptions to ZVMVolumeError.""" - - try: - yield - except exception.ZVMBaseException: - raise - except Exception as err: - raise exception.ZVMVolumeError(msg=err) - - -class DriverAPI(object): - """DriverAPI for implement volume_attach on IBM z/VM platform.""" - - def init_host(self, host_stats): - """Initialize host environment.""" - raise NotImplementedError - - def get_volume_connector(self, instance): - """Get volume connector for current driver.""" - raise NotImplementedError - - def attach_volume_active(self, context, connection_info, instance, - mountpoint, rollback): - """Attach a volume to an running instance.""" - raise NotImplementedError - - def detach_volume_active(self, connection_info, instance, mountpoint, - rollback): - """Detach a volume from an running instance.""" - raise NotImplementedError - - def attach_volume_inactive(self, context, connection_info, instance, - mountpoint, rollback): - """Attach a volume to an shutdown instance.""" - raise NotImplementedError - - def detach_volume_inactive(self, connection_info, instance, mountpoint, - rollback): - """Detach a volume from an shutdown instance.""" - raise NotImplementedError - - def has_persistent_volume(self, instance): - """Decide if the specified instance has persistent volumes attached.""" - raise NotImplementedError - - -class SVCDriver(DriverAPI): - """SVC volume operator on IBM z/VM platform.""" - - class FCP(object): - """FCP adapter class.""" - - _DEV_NO_PATTERN = '[0-9a-f]{1,4}' - _WWPN_PATTERN = '[0-9a-f]{16}' - _CHPID_PATTERN = '[0-9A-F]{2}' - - def __init__(self, init_info): - """Initialize a FCP device object from several lines of string - describing properties of the FCP device. - Here is a sample: - opnstk1: FCP device number: B83D - opnstk1: Status: Free - opnstk1: NPIV world wide port number: NONE - opnstk1: Channel path ID: 59 - opnstk1: Physical world wide port number: 20076D8500005181 - The format comes from the response of xCAT, do not support - arbitrary format. - - """ - - self._dev_no = None - self._npiv_port = None - self._chpid = None - self._physical_port = None - - self._is_valid = True - # Sometimes nova issues get_volume_connector() for some reasons, - # which requires a FCP device being assigned to the instance. But - # nova will not attach any volume to the instance later. In this - # case we need to release the FCP device in later time. So a FCP - # device which is assigned to an instance doesn't means it's - # actually used by the instance. - self._is_reserved = False - self._is_in_use = False - self._reserve_time = 0 - - if isinstance(init_info, list) and (len(init_info) == 5): - self._dev_no = self._get_dev_number_from_line(init_info[0]) - self._npiv_port = self._get_wwpn_from_line(init_info[2]) - self._chpid = self._get_chpid_from_line(init_info[3]) - self._physical_port = self._get_wwpn_from_line(init_info[4]) - self._validate_device() - - def _get_wwpn_from_line(self, info_line): - wwpn = info_line.split(':')[-1].strip().lower() - return wwpn if (wwpn and wwpn.upper() != 'NONE') else None - - def _get_dev_number_from_line(self, info_line): - dev_no = info_line.split(':')[-1].strip().lower() - return dev_no if dev_no else None - - def _get_chpid_from_line(self, info_line): - chpid = info_line.split(':')[-1].strip().upper() - return chpid if chpid else None - - def _validate_device(self): - if not (self._dev_no and self._chpid): - self._is_valid = False - return - if not (self._npiv_port or self._physical_port): - self._is_valid = False - return - if not (re.match(self._DEV_NO_PATTERN, self._dev_no) and - re.match(self._CHPID_PATTERN, self._chpid)): - self._is_valid = False - return - if self._npiv_port and not re.match(self._WWPN_PATTERN, - self._npiv_port): - self._is_valid = False - return - if self._physical_port and not re.match(self._WWPN_PATTERN, - self._physical_port): - self._is_valid = False - return - - def is_valid(self): - return self._is_valid - - def get_dev_no(self): - return self._dev_no - - def get_npiv_port(self): - return self._npiv_port - - def get_chpid(self): - return self._chpid - - def get_physical_port(self): - return self._physical_port - - def reserve_device(self): - self._is_reserved = True - self._is_in_use = False - self._reserve_time = time.time() - - def is_reserved(self): - return self._is_reserved - - def set_in_use(self): - self._is_reserved = True - self._is_in_use = True - - def is_in_use(self): - return self._is_in_use - - def release_device(self): - self._is_reserved = False - self._is_in_use = False - self._reserve_time = 0 - - def get_reserve_time(self): - return self._reserve_time - - _RESERVE = 0 - _INCREASE = 1 - _DECREASE = 2 - _REMOVE = 3 - _FORCE_REMOVE = 4 - - def __init__(self): - self._xcat_url = zvmutils.get_xcat_url() - self._path_utils = zvmutils.PathUtils() - self._host = CONF.zvm_host - self._pool_name = CONF.zvm_scsi_pool - self._fcp_pool = {} - self._instance_fcp_map = {} - self._is_instance_fcp_map_locked = False - self._volume_api = cinder.API() - self._dist_manager = dist.ListDistManager() - - self._actions = {'attach_volume': 'addScsiVolume', - 'detach_volume': 'removeScsiVolume', - 'create_mountpoint': 'createfilesysnode', - 'remove_mountpoint': 'removefilesysnode'} - - def init_host(self, host_stats): - """Initialize host environment.""" - - if not host_stats: - errmsg = _("Can not obtain host stats.") - raise exception.ZVMDriverError(msg=errmsg) - - zhcp_fcp_list = CONF.zvm_zhcp_fcp_list - fcp_devices = self._expand_fcp_list(zhcp_fcp_list) - hcpnode = host_stats[0]['zhcp']['nodename'] - for _fcp in fcp_devices: - with zvmutils.ignore_errors(): - self._attach_device(hcpnode, _fcp) - with zvmutils.ignore_errors(): - self._online_device(hcpnode, _fcp) - - fcp_list = CONF.zvm_fcp_list - if (fcp_list is None): - errmsg = _("At least one fcp list should be given") - LOG.warning(errmsg) - raise exception.ZVMVolumeError(msg=errmsg) - self._init_fcp_pool(fcp_list) - self._init_instance_fcp_map(fcp_list) - - def _init_fcp_pool(self, fcp_list): - """The FCP infomation looks like this: - host: FCP device number: xxxx - host: Status: Active - host: NPIV world wide port number: xxxxxxxx - host: Channel path ID: xx - host: Physical world wide port number: xxxxxxxx - ...... - host: FCP device number: xxxx - host: Status: Active - host: NPIV world wide port number: xxxxxxxx - host: Channel path ID: xx - host: Physical world wide port number: xxxxxxxx - - """ - complete_fcp_set = self._expand_fcp_list(fcp_list) - fcp_info = self._get_all_fcp_info() - lines_per_item = 5 - num_fcps = len(fcp_info) / lines_per_item - for n in range(0, num_fcps): - fcp_init_info = fcp_info[(5 * n):(5 * (n + 1))] - fcp = SVCDriver.FCP(fcp_init_info) - dev_no = fcp.get_dev_no() - if dev_no in complete_fcp_set: - if fcp.is_valid(): - self._fcp_pool[dev_no] = fcp - else: - errmsg = _("Find an invalid FCP device with properties {" - "dev_no: %(dev_no)s, " - "NPIV_port: %(NPIV_port)s, " - "CHPID: %(CHPID)s, " - "physical_port: %(physical_port)s} !") % { - 'dev_no': fcp.get_dev_no(), - 'NPIV_port': fcp.get_npiv_port(), - 'CHPID': fcp.get_chpid(), - 'physical_port': fcp.get_physical_port()} - LOG.warning(errmsg) - - def _get_all_fcp_info(self): - fcp_info = [] - free_fcp_info = self._list_fcp_details('free') - active_fcp_info = self._list_fcp_details('active') - if free_fcp_info: - fcp_info.extend(free_fcp_info) - if active_fcp_info: - fcp_info.extend(active_fcp_info) - return fcp_info - - def _init_instance_fcp_map(self, fcp_list): - """Map all instances and their fcp devices. - One instance should use only one fcp device when multipath - is not enabled. - - """ - - complete_fcp_set = self._expand_fcp_list(fcp_list) - # All other functions should not modify _instance_fcp_map during - # FCP pool initialization - self._is_instance_fcp_map_locked = True - - compute_host_bdms = self._get_host_volume_bdms() - for instance_bdms in compute_host_bdms: - instance_name = instance_bdms['instance']['name'] - - for _bdm in instance_bdms['instance_bdms']: - connection_info = self._build_connection_info(_bdm) - try: - # Remove invalid FCP devices - fcp_list = connection_info['data']['zvm_fcp'] - invalid_fcp_list = [] - for fcp in fcp_list: - if fcp not in complete_fcp_set: - errmsg = _("FCP device %(dev)s is not configured " - "but is used by %(inst_name)s.") % { - 'dev': fcp, 'inst_name': instance_name} - LOG.warning(errmsg) - invalid_fcp_list.append(fcp) - for fcp in invalid_fcp_list: - fcp_list.remove(fcp) - # Map valid FCP devices - if fcp_list: - self._update_instance_fcp_map(instance_name, fcp_list, - self._INCREASE) - except (TypeError, KeyError): - pass - - self._is_instance_fcp_map_locked = False - - def _update_instance_fcp_map_if_unlocked(self, instance_name, fcp_list, - action): - while self._is_instance_fcp_map_locked: - time.sleep(1) - self._update_instance_fcp_map(instance_name, fcp_list, action) - - def _update_instance_fcp_map(self, instance_name, fcp_list, action): - if instance_name in self._instance_fcp_map: - current_list = self._instance_fcp_map[instance_name]['fcp_list'] - if fcp_list and (fcp_list != current_list): - errmsg = _("FCP conflict for instance %(ins_name)s! " - "Original set: %(origin)s, new set: %(new)s" - ) % {'ins_name': instance_name, - 'origin': current_list, 'new': fcp_list} - raise exception.ZVMVolumeError(msg=errmsg) - - if action == self._RESERVE: - if instance_name in self._instance_fcp_map: - count = self._instance_fcp_map[instance_name]['count'] - if count > 0: - errmsg = _("Try to reserve fcp devices on which there are " - "already volumes attached: " - "%(ins_name)s:%(fcp_list)s") % { - 'ins_name': instance_name, - 'fcp_list': fcp_list} - raise exception.ZVMVolumeError(msg=errmsg) - else: - for fcp_no in fcp_list: - fcp = self._fcp_pool.get(fcp_no) - fcp.reserve_device() - new_item = {instance_name: {'fcp_list': fcp_list, 'count': 0}} - self._instance_fcp_map.update(new_item) - - elif action == self._INCREASE: - for fcp_no in fcp_list: - fcp = self._fcp_pool.get(fcp_no) - fcp.set_in_use() - if instance_name in self._instance_fcp_map: - count = self._instance_fcp_map[instance_name]['count'] - new_item = {instance_name: {'fcp_list': fcp_list, - 'count': count + 1}} - self._instance_fcp_map.update(new_item) - else: - new_item = {instance_name: {'fcp_list': fcp_list, 'count': 1}} - self._instance_fcp_map.update(new_item) - - elif action == self._DECREASE: - if instance_name in self._instance_fcp_map: - count = self._instance_fcp_map[instance_name]['count'] - if count > 0: - new_item = {instance_name: {'fcp_list': fcp_list, - 'count': count - 1}} - self._instance_fcp_map.update(new_item) - if count == 1: - for fcp_no in fcp_list: - fcp = self._fcp_pool.get(fcp_no) - # The function 'reserve_device()' will do two jobs. - # The first one is to cancel the 'in_use' status of - # the FCP, so function _is_fcp_in_use() can return - # right result. The second one is to facilitate - # rollback process in case the FCP being assigned - # to another instance after it's released. - fcp.reserve_device() - else: - errmsg = _("Reference count falling down below 0 for map " - "item: %(ins_name)s:%(fcp_list)s") % { - 'ins_name': instance_name, - 'fcp_list': fcp_list} - raise exception.ZVMVolumeError(msg=errmsg) - else: - errmsg = _("Try to decrease an inexistent map item: " - "%(ins_name)s:%(fcp_list)s" - ) % {'ins_name': instance_name, - 'fcp_list': fcp_list} - raise exception.ZVMVolumeError(msg=errmsg) - - elif action == self._REMOVE: - if instance_name in self._instance_fcp_map: - count = self._instance_fcp_map[instance_name]['count'] - if count > 0: - errmsg = _("Try to remove a map item with volumes " - "attached on: %(ins_name)s:%(fcp_list)s" - ) % {'ins_name': instance_name, - 'fcp_list': fcp_list} - raise exception.ZVMVolumeError(msg=errmsg) - else: - for fcp_no in fcp_list: - fcp = self._fcp_pool.get(fcp_no) - fcp.release_device() - self._instance_fcp_map.pop(instance_name) - else: - errmsg = _("Try to remove an inexistent map item: " - "%(ins_name)s:%(fcp_list)s" - ) % {'ins_name': instance_name, - 'fcp_list': fcp_list} - raise exception.ZVMVolumeError(msg=errmsg) - - elif action == self._FORCE_REMOVE: - if instance_name in self._instance_fcp_map: - for fcp_no in fcp_list: - fcp = self._fcp_pool.get(fcp_no) - fcp.release_device() - self._instance_fcp_map.pop(instance_name) - - else: - errmsg = _("Unrecognized option: %s") % action - raise exception.ZVMVolumeError(msg=errmsg) - - def _get_host_volume_bdms(self): - """Return all block device mappings on a compute host.""" - - compute_host_bdms = [] - instances = self._get_all_instances() - for instance in instances: - instance_bdms = self._get_instance_bdms(instance) - compute_host_bdms.append(dict(instance=instance, - instance_bdms=instance_bdms)) - - return compute_host_bdms - - def _get_all_instances(self): - context = nova.context.get_admin_context() - return instance_obj.InstanceList.get_by_host(context, self._host) - - def _get_instance_bdms(self, instance): - context = nova.context.get_admin_context() - instance_bdms = [bdm for bdm in - (block_device_obj.BlockDeviceMappingList. - get_by_instance_uuid(context, instance['uuid'])) - if bdm.is_volume] - return instance_bdms - - def has_persistent_volume(self, instance): - return bool(self._get_instance_bdms(instance)) - - def _build_connection_info(self, bdm): - try: - connection_info = jsonutils.loads(bdm['connection_info']) - except (TypeError, KeyError, ValueError): - return None - - # The value of zvm_fcp is a string in former release. It will be set - # in future. We have to translate a string FCP to a single-element set - # in order to make code goes on. - fcp = connection_info['data']['zvm_fcp'] - if fcp and isinstance(fcp, six.string_types): - connection_info['data']['zvm_fcp'] = [fcp] - - return connection_info - - def get_volume_connector(self, instance): - empty_connector = {'zvm_fcp': [], 'wwpns': [], 'host': ''} - - try: - fcp_list = self._instance_fcp_map.get(instance['name'])['fcp_list'] - except Exception: - fcp_list = [] - if not fcp_list: - fcp_list = self._get_fcp_from_pool() - if fcp_list: - self._update_instance_fcp_map_if_unlocked( - instance['name'], fcp_list, self._RESERVE) - - if not fcp_list: - errmsg = _("No available FCP device found.") - LOG.warning(errmsg) - return empty_connector - - wwpns = [] - for fcp_no in fcp_list: - wwpn = self._get_wwpn(fcp_no) - if not wwpn: - errmsg = _("FCP device %s has no available WWPN.") % fcp_no - LOG.warning(errmsg) - else: - wwpns.append(wwpn) - if not wwpns: - errmsg = _("No available WWPN found.") - LOG.warning(errmsg) - return empty_connector - - return {'zvm_fcp': fcp_list, 'wwpns': wwpns, 'host': CONF.zvm_host} - - def _get_wwpn(self, fcp_no): - fcp = self._fcp_pool.get(fcp_no) - if not fcp: - return None - if fcp.get_npiv_port(): - return fcp.get_npiv_port() - if fcp.get_physical_port(): - return fcp.get_physical_port() - return None - - def _list_fcp_details(self, state): - fields = '&field=--fcpdevices&field=' + state + '&field=details' - rsp = self._xcat_rinv(fields) - try: - fcp_details = rsp['info'][0][0].splitlines() - return fcp_details - except (TypeError, KeyError): - return None - - def _get_fcp_from_pool(self): - fcp_list = [] - for fcp in list(self._fcp_pool.values()): - if not fcp.is_reserved(): - fcp_list.append(fcp.get_dev_no()) - break - - if not fcp_list: - self._release_fcps_reserved() - for fcp in list(self._fcp_pool.values()): - if not fcp.is_reserved(): - fcp_list.append(fcp.get_dev_no()) - break - - if not fcp_list: - return [] - - if not CONF.zvm_multiple_fcp: - return fcp_list - - primary_fcp = self._fcp_pool.get(fcp_list.pop()) - backup_fcp = None - for fcp in list(self._fcp_pool.values()): - if not fcp.is_reserved() and ( - fcp.get_chpid() != primary_fcp.get_chpid()): - backup_fcp = fcp - break - - fcp_list.append(primary_fcp.get_dev_no()) - if backup_fcp: - fcp_list.append(backup_fcp.get_dev_no()) - else: - errmsg = _("Can not find a backup FCP device for primary FCP " - "device %s") % primary_fcp.get_dev_no() - LOG.warning(errmsg) - return [] - return fcp_list - - def _release_fcps_reserved(self): - current = time.time() - for instance in list(self._instance_fcp_map.keys()): - if self._instance_fcp_map.get(instance)['count'] != 0: - continue - # Only release FCP devices which are reserved more than 30 secs - # in case concurrent assignment. - fcp_list = self._instance_fcp_map.get(instance)['fcp_list'] - fcp = self._fcp_pool.get(fcp_list[0]) - if current - fcp.get_reserve_time() > 30: - self._update_instance_fcp_map_if_unlocked(instance, fcp_list, - self._REMOVE) - - def _extract_connection_info(self, context, connection_info): - with wrap_internal_errors(): - LOG.debug("Extract connection_info: %s", connection_info) - - lun = connection_info['data']['target_lun'] - lun = "%04x000000000000" % int(lun) - wwpn = connection_info['data']['target_wwn'] - size = '0G' - # There is no context in detach case - if context: - volume_id = connection_info['data']['volume_id'] - volume = self._get_volume_by_id(context, volume_id) - size = str(volume['size']) + 'G' - fcp_list = connection_info['data']['zvm_fcp'] - - return (lun.lower(), self._format_wwpn(wwpn), size, - self._format_fcp_list(fcp_list)) - - def _format_wwpn(self, wwpn): - if isinstance(wwpn, six.string_types): - return wwpn.lower() - else: - new_wwpn = ';'.join(wwpn) - return new_wwpn.lower() - - def _format_fcp_list(self, fcp_list): - if len(fcp_list) == 1: - return fcp_list[0].lower() - else: - return ';'.join(fcp_list).lower() - - def _get_volume_by_id(self, context, volume_id): - volume = self._volume_api.get(context, volume_id) - return volume - - def attach_volume_active(self, context, connection_info, instance, - mountpoint, rollback=True): - """Attach a volume to an running instance.""" - - (lun, wwpn, size, fcp) = self._extract_connection_info(context, - connection_info) - fcp_list = fcp.split(';') - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, self._INCREASE) - try: - self._add_zfcp_to_pool(fcp, wwpn, lun, size) - self._add_zfcp(instance, fcp, wwpn, lun, size) - if mountpoint: - self._create_mountpoint(instance, fcp, wwpn, lun, mountpoint) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMVolumeError): - with excutils.save_and_reraise_exception(): - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, - self._DECREASE) - do_detach = not self._is_fcp_in_use(instance) - if rollback: - with zvmutils.ignore_errors(): - self._remove_mountpoint(instance, mountpoint) - with zvmutils.ignore_errors(): - self._remove_zfcp(instance, fcp, wwpn, lun) - with zvmutils.ignore_errors(): - self._remove_zfcp_from_pool(wwpn, lun) - with zvmutils.ignore_errors(): - if do_detach: - for dev_no in fcp_list: - self._detach_device(instance['name'], dev_no) - self._update_instance_fcp_map_if_unlocked( - instance['name'], fcp_list, self._REMOVE) - - def detach_volume_active(self, connection_info, instance, mountpoint, - rollback=True): - """Detach a volume from an running instance.""" - - (lun, wwpn, size, fcp) = self._extract_connection_info(None, - connection_info) - fcp_list = fcp.split(';') - self._update_instance_fcp_map_if_unlocked(instance['name'], fcp_list, - self._DECREASE) - try: - do_detach = not self._is_fcp_in_use(instance) - if mountpoint: - self._remove_mountpoint(instance, mountpoint) - self._remove_zfcp(instance, fcp, wwpn, lun) - self._remove_zfcp_from_pool(wwpn, lun) - if do_detach: - for dev_no in fcp_list: - self._detach_device(instance['name'], dev_no) - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, - self._REMOVE) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMVolumeError): - with excutils.save_and_reraise_exception(): - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, - self._INCREASE) - if rollback: - with zvmutils.ignore_errors(): - self._add_zfcp_to_pool(fcp, wwpn, lun, size) - with zvmutils.ignore_errors(): - self._add_zfcp(instance, fcp, wwpn, lun, size) - if mountpoint: - with zvmutils.ignore_errors(): - self._create_mountpoint(instance, fcp, wwpn, lun, - mountpoint) - - def attach_volume_inactive(self, context, connection_info, instance, - mountpoint, rollback=True): - """Attach a volume to an shutdown instance.""" - - (lun, wwpn, size, fcp) = self._extract_connection_info(context, - connection_info) - fcp_list = fcp.split(';') - do_attach = not self._is_fcp_in_use(instance) - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, self._INCREASE) - os_version = instance.system_metadata['image_os_version'] - try: - self._add_zfcp_to_pool(fcp, wwpn, lun, size) - self._allocate_zfcp(instance, fcp, size, wwpn, lun) - self._notice_attach(instance, fcp, wwpn, lun, mountpoint, - os_version) - if do_attach: - for dev_no in fcp_list: - self._attach_device(instance['name'], dev_no) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMVolumeError): - with excutils.save_and_reraise_exception(): - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, - self._DECREASE) - if rollback: - with zvmutils.ignore_errors(): - self._notice_detach(instance, fcp, wwpn, lun, - mountpoint, os_version) - with zvmutils.ignore_errors(): - self._remove_zfcp_from_pool(wwpn, lun) - with zvmutils.ignore_errors(): - if do_attach: - for dev_no in fcp_list: - self._detach_device(instance['name'], dev_no) - self._update_instance_fcp_map_if_unlocked( - instance['name'], fcp_list, self._REMOVE) - - def detach_volume_inactive(self, connection_info, instance, mountpoint, - rollback=True): - """Detach a volume from an shutdown instance.""" - - (lun, wwpn, size, fcp) = self._extract_connection_info(None, - connection_info) - fcp_list = fcp.split(';') - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, self._DECREASE) - do_detach = not self._is_fcp_in_use(instance) - os_version = instance.system_metadata['image_os_version'] - try: - self._remove_zfcp(instance, fcp, wwpn, lun) - self._remove_zfcp_from_pool(wwpn, lun) - self._notice_detach(instance, fcp, wwpn, lun, mountpoint, - os_version) - if do_detach: - for dev_no in fcp_list: - self._detach_device(instance['name'], dev_no) - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, - self._REMOVE) - except (exception.ZVMXCATRequestFailed, - exception.ZVMInvalidXCATResponseDataError, - exception.ZVMXCATInternalError, - exception.ZVMVolumeError): - with excutils.save_and_reraise_exception(): - self._update_instance_fcp_map_if_unlocked(instance['name'], - fcp_list, - self._INCREASE) - if rollback: - with zvmutils.ignore_errors(): - self._attach_device(instance['name'], fcp) - with zvmutils.ignore_errors(): - self._notice_attach(instance, fcp, wwpn, lun, - mountpoint, os_version) - with zvmutils.ignore_errors(): - self._add_zfcp_to_pool(fcp, wwpn, lun, size) - with zvmutils.ignore_errors(): - self._allocate_zfcp(instance, fcp, size, wwpn, lun) - - def volume_boot_init(self, instance, fcp): - self._update_instance_fcp_map_if_unlocked(instance['name'], [fcp], - self._INCREASE) - self._attach_device(instance['name'], fcp) - - def volume_boot_cleanup(self, instance, fcp): - self._update_instance_fcp_map_if_unlocked(instance['name'], [fcp], - self._FORCE_REMOVE) - self._detach_device(instance['name'], fcp) - - def _expand_fcp_list(self, fcp_list): - """Expand fcp list string into a python list object which contains - each fcp devices in the list string. A fcp list is composed of fcp - device addresses, range indicator '-', and split indicator ';'. - - For example, if fcp_list is - "0011-0013;0015;0017-0018", expand_fcp_list(fcp_list) will return - [0011, 0012, 0013, 0015, 0017, 0018]. - - """ - - LOG.debug("Expand FCP list %s", fcp_list) - - if not fcp_list: - return set() - - range_pattern = '[0-9a-fA-F]{1,4}(-[0-9a-fA-F]{1,4})?' - match_pattern = "^(%(range)s)(;%(range)s)*$" % {'range': range_pattern} - if not re.match(match_pattern, fcp_list): - errmsg = _("Invalid FCP address %s") % fcp_list - raise exception.ZVMDriverError(msg=errmsg) - - fcp_devices = set() - for _range in fcp_list.split(';'): - if '-' not in _range: - # single device - fcp_addr = int(_range, 16) - fcp_devices.add("%04x" % fcp_addr) - else: - # a range of address - (_min, _max) = _range.split('-') - _min = int(_min, 16) - _max = int(_max, 16) - for fcp_addr in range(_min, _max + 1): - fcp_devices.add("%04x" % fcp_addr) - - # remove duplicate entries - return fcp_devices - - def _attach_device(self, node, addr, mode='0'): - """Attach a device to a node.""" - - body = [' '.join(['--dedicatedevice', addr, addr, mode])] - self._xcat_chvm(node, body) - - def _detach_device(self, node, vdev): - """Detach a device from a node.""" - - body = [' '.join(['--undedicatedevice', vdev])] - self._xcat_chvm(node, body) - - def _online_device(self, node, dev): - """After attaching a device to a node, the device should be made - online before it being in use. - - """ - - body = ["command=cio_ignore -r %s" % dev] - self._xcat_xdsh(node, body) - - body = ["command=chccwdev -e %s" % dev] - self._xcat_xdsh(node, body) - - def _is_fcp_in_use(self, instance): - if instance['name'] in self._instance_fcp_map: - count = self._instance_fcp_map.get(instance['name'])['count'] - if count > 0: - return True - return False - - def _notice_attach(self, instance, fcp, wwpn, lun, mountpoint, os_version): - # Create and send volume file - action = self._actions['attach_volume'] - parms = self._get_volume_parms(action, fcp, wwpn, lun) - self._send_notice(instance, parms) - - # Create and send mount point file - action = self._actions['create_mountpoint'] - parms = self._get_mountpoint_parms(action, fcp, wwpn, lun, mountpoint, - os_version) - self._send_notice(instance, parms) - - def _notice_detach(self, instance, fcp, wwpn, lun, mountpoint, os_version): - # Create and send volume file - action = self._actions['detach_volume'] - parms = self._get_volume_parms(action, fcp, wwpn, lun) - self._send_notice(instance, parms) - - # Create and send mount point file - action = self._actions['remove_mountpoint'] - parms = self._get_mountpoint_parms(action, fcp, wwpn, lun, mountpoint, - os_version) - self._send_notice(instance, parms) - - def _get_volume_parms(self, action, fcp, wwpn, lun): - action = "action=%s" % action - # Replace the ';' in wwpn/fcp to ',' in script file since shell will - # treat ';' as a new line - fcp = fcp.replace(';', ',') - fcp = "fcpAddr=%s" % fcp - wwpn = wwpn.replace(';', ',') - wwpn = "wwpn=%s" % wwpn - lun = "lun=%s" % lun - parmline = ' '.join([action, fcp, wwpn, lun]) - return parmline - - def _get_mountpoint_parms(self, action, fcp, wwpn, lun, - mountpoint, os_version): - action_parm = "action=%s" % action - mountpoint = "tgtFile=%s" % mountpoint - # Replace the ';' in wwpn/fcp to ',' in script file since shell will - # treat ';' as a new line - wwpn = wwpn.replace(';', ',') - fcp = fcp.replace(';', ',') - if action == self._actions['create_mountpoint']: - dist = self._dist_manager.get_linux_dist(os_version)() - srcdev = dist.assemble_zfcp_srcdev(fcp, wwpn, lun) - srcfile = "srcFile=%s" % srcdev - parmline = ' '.join([action_parm, mountpoint, srcfile]) - else: - parmline = ' '.join([action_parm, mountpoint]) - return parmline - - def _send_notice(self, instance, parms): - zvmutils.aemod_handler(instance['name'], const.DISK_FUNC_NAME, parms) - - def _add_zfcp_to_pool(self, fcp, wwpn, lun, size): - body = [' '.join(['--addzfcp2pool', self._pool_name, 'free', wwpn, - lun, size, fcp])] - self._xcat_chhy(body) - - def _remove_zfcp_from_pool(self, wwpn, lun): - body = [' '.join(['--removezfcpfrompool', CONF.zvm_scsi_pool, lun, - wwpn])] - self._xcat_chhy(body) - - def _add_zfcp(self, instance, fcp, wwpn, lun, size): - body = [' '.join(['--addzfcp', CONF.zvm_scsi_pool, fcp, str(0), size, - str(0), wwpn, lun])] - self._xcat_chvm(instance['name'], body) - - def _remove_zfcp(self, instance, fcp, wwpn, lun): - body = [' '.join(['--removezfcp', fcp, wwpn, lun, '1'])] - self._xcat_chvm(instance['name'], body) - - def _create_mountpoint(self, instance, fcp, wwpn, lun, mountpoint): - os_version = instance.system_metadata['image_os_version'] - dist = self._dist_manager.get_linux_dist(os_version)() - srcdev = dist.assemble_zfcp_srcdev(fcp, wwpn, lun) - body = [" ".join(['--createfilesysnode', srcdev, mountpoint])] - self._xcat_chvm(instance['name'], body) - - def _remove_mountpoint(self, instance, mountpoint): - body = [' '.join(['--removefilesysnode', mountpoint])] - self._xcat_chvm(instance['name'], body) - - def _allocate_zfcp(self, instance, fcp, size, wwpn, lun): - body = [" ".join(['--reservezfcp', CONF.zvm_scsi_pool, 'used', - instance['name'], fcp, size, wwpn, lun])] - self._xcat_chhy(body) - - def _xcat_chvm(self, node, body): - url = self._xcat_url.chvm('/' + node) - zvmutils.xcat_request('PUT', url, body) - - def _xcat_chhy(self, body): - url = self._xcat_url.chhv('/' + self._host) - zvmutils.xcat_request('PUT', url, body) - - def _xcat_xdsh(self, node, body): - url = self._xcat_url.xdsh('/' + node) - zvmutils.xcat_request('PUT', url, body) - - def _xcat_rinv(self, fields): - url = self._xcat_url.rinv('/' + self._host, fields) - return zvmutils.xcat_request('GET', url) diff --git a/requirements.txt b/requirements.txt index b247458..e3c67e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,11 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -pbr>=1.8 +pbr>=2.0.0,!=2.1.0 # Apache-2.0 oslo.concurrency>=3.8.0 # Apache-2.0 -oslo.log>=3.11.0 # Apache-2.0 -oslo.serialization>=1.10.0 # Apache-2.0 +oslo.log>=3.22.0 # Apache-2.0 +oslo.serialization>=1.10.0,!=2.19.1 # Apache-2.0 oslo.service>=1.10.0 # Apache-2.0 oslo.utils>=3.20.0 # Apache-2.0 six>=1.9.0 +zvmcloudconnector>=1.1.1 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg index 5e12702..242cc03 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,4 +45,4 @@ output_file = nova_zvm/locale/nova-zvm.pot [entry_points] oslo.config.opts = - nova_zvm = nova_zvm.virt.zvm.opts:list_opts + nova_zvm = nova_zvm.virt.zvm.conf:list_opts diff --git a/test-requirements.txt b/test-requirements.txt index 12333e6..be2e0a6 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -16,3 +16,4 @@ testtools>=1.4.0 mock>=1.2 mox>=0.5.3 vine +wsgi_intercept>=1.4.1 # MIT License diff --git a/tools/tox_install.sh b/tools/tox_install.sh new file mode 100755 index 0000000..c3c9de6 --- /dev/null +++ b/tools/tox_install.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +# Base code from https://github.com/openstack/glance_store/blob/master/tools/tox_install.sh +# +# Library constraint file contains version pin that is in conflict with +# installing the library from source. We should replace the version pin in +# the constraints file before applying it for from-source installation. + +ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner +BRANCH_NAME=master +LIB_NAME=pypowervm +LIB_BRANCH=develop +LIB_LOCATION=git+https://github.com/powervm/$LIB_NAME@$LIB_BRANCH#egg=$LIB_NAME +requirements_installed=$(echo "import openstack_requirements" | python 2>/dev/null ; echo $?) + +set -e -x + +CONSTRAINTS_FILE=$1 +shift + +install_cmd="pip install" +mydir=$(mktemp -dt "$LIB_NAME-tox_install-XXXXXXX") +trap "rm -rf $mydir" EXIT +localfile=$mydir/upper-constraints.txt +if [[ $CONSTRAINTS_FILE != http* ]]; then + CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE +fi +curl $CONSTRAINTS_FILE -k -o $localfile +install_cmd="$install_cmd -c$localfile" + +if [ $requirements_installed -eq 0 ]; then + echo "ALREADY INSTALLED" > /tmp/tox_install.txt + echo "Requirements already installed; using existing package" +elif [ -x "$ZUUL_CLONER" ]; then + echo "ZUUL CLONER" > /tmp/tox_install.txt + pushd $mydir + $ZUUL_CLONER --cache-dir \ + /opt/git \ + --branch $BRANCH_NAME \ + git://git.openstack.org \ + openstack/requirements + cd openstack/requirements + $install_cmd -e . + popd +else + echo "PIP HARDCODE" > /tmp/tox_install.txt + if [ -z "$REQUIREMENTS_PIP_LOCATION" ]; then + REQUIREMENTS_PIP_LOCATION="git+https://git.openstack.org/openstack/requirements@$BRANCH_NAME#egg=requirements" + fi + $install_cmd -U -e ${REQUIREMENTS_PIP_LOCATION} +fi + +# This is the main purpose of the script: Allow local installation of +# the current repo. It is listed in constraints file and thus any +# install will be constrained and we need to unconstrain it. +edit-constraints $localfile -- $LIB_NAME "-e $LIB_LOCATION" + +if [ -z "$*" ]; then + echo "No packages to be installed." + exit 0 +fi + +$install_cmd -U $* +exit $? \ No newline at end of file diff --git a/tox.ini b/tox.ini index cefd3fa..828b646 100644 --- a/tox.ini +++ b/tox.ini @@ -5,12 +5,12 @@ skipsdist = True [testenv] usedevelop = True -install_command = pip install -U {opts} {packages} +install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/pike} {opts} {packages} setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt - -egit+https://github.com/openstack/nova#egg=nova + -egit+https://github.com/openstack/nova@stable/pike#egg=nova commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:pep8]