Sync patch from in tree
Change-Id: I9088ff34129feed57757d57824f65a3d784801dd
This commit is contained in:
parent
e26ae01680
commit
e72fbe637f
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2017 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
|
||||
|
@ -13,16 +13,14 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
"""Test suite for ZVM configure drive."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from oslo_utils import fileutils
|
||||
|
||||
from nova_zvm.virt.zvm import conf
|
||||
from nova_zvm.virt.zvm import configdrive as zvmconfigdrive
|
||||
from nova import conf
|
||||
from nova import test
|
||||
from nova.virt.zvm import configdrive as zvmconfigdrive
|
||||
|
||||
|
||||
CONF = conf.CONF
|
||||
|
@ -39,29 +37,20 @@ class ZVMConfigDriveTestCase(test.NoDBTestCase):
|
|||
super(ZVMConfigDriveTestCase, self).setUp()
|
||||
self.flags(config_drive_format='iso9660',
|
||||
tempdir='/tmp/os')
|
||||
|
||||
self._file_path = CONF.tempdir
|
||||
self._file_name = self._file_path + '/cfgdrive.tgz'
|
||||
self.inst_md = FakeInstMeta()
|
||||
|
||||
def tearDown(self):
|
||||
super(ZVMConfigDriveTestCase, self).tearDown()
|
||||
shutil.rmtree(self._file_path)
|
||||
|
||||
def test_create_configdrive_tgz(self):
|
||||
self._file_path = CONF.tempdir
|
||||
fileutils.ensure_tree(self._file_path)
|
||||
try:
|
||||
with zvmconfigdrive.ZVMConfigDriveBuilder(
|
||||
instance_md=self.inst_md) as c:
|
||||
c.make_drive(self._file_name)
|
||||
self._file_name = self._file_path + '/cfgdrive.tgz'
|
||||
|
||||
with zvmconfigdrive.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_drive_unknown_format(self):
|
||||
self.flags(config_drive_format='vfat')
|
||||
try:
|
||||
with zvmconfigdrive.ZVMConfigDriveBuilder(
|
||||
instance_md=self.inst_md) as c:
|
||||
self.assertRaises(exception.ConfigDriveUnknownFormat,
|
||||
c.make_drive, self._file_name)
|
||||
finally:
|
||||
fileutils.remove_path_on_error(self._file_path)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2017 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,541 +12,152 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import eventlet
|
||||
import mock
|
||||
import os
|
||||
import six
|
||||
if six.PY2:
|
||||
import __builtin__ as builtins
|
||||
elif six.PY3:
|
||||
import builtins
|
||||
|
||||
from nova.compute import power_state
|
||||
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_zvm.virt.zvm import conf
|
||||
from nova_zvm.virt.zvm import const
|
||||
from nova_zvm.virt.zvm import driver as zvmdriver
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
|
||||
CONF = conf.CONF
|
||||
from nova.virt.zvm import driver as zvmdriver
|
||||
|
||||
|
||||
class TestZVMDriver(test.NoDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestZVMDriver, self).setUp()
|
||||
self.flags(zvm_cloud_connector_url='https://1.1.1.1:1111',
|
||||
zvm_image_tmp_path='/test/image',
|
||||
zvm_reachable_timeout=300)
|
||||
self.flags(my_ip='192.168.1.1',
|
||||
instance_name_template='test%04x')
|
||||
with mock.patch('nova_zvm.virt.zvm.utils.'
|
||||
'zVMConnectorRequestHandler.call') as mcall:
|
||||
self.flags(instance_name_template='abc%05d')
|
||||
self.flags(cloud_connector_url='https://1.1.1.1:1111', group='zvm')
|
||||
with mock.patch('nova.virt.zvm.utils.'
|
||||
'ConnectorClient.call') as mcall:
|
||||
mcall.return_value = {'hypervisor_hostname': 'TESTHOST',
|
||||
'ipl_time': 'TESTTIME'}
|
||||
self.driver = zvmdriver.ZVMDriver('virtapi')
|
||||
'ipl_time': 'IPL at 11/14/17 10:47:44 EST'}
|
||||
self._driver = zvmdriver.ZVMDriver('virtapi')
|
||||
self._hypervisor = self._driver._hypervisor
|
||||
|
||||
self._context = context.RequestContext('fake_user', 'fake_project')
|
||||
self._uuid = uuidsentinel.foo
|
||||
self._image_id = uuidsentinel.foo
|
||||
self._instance_values = {
|
||||
'display_name': 'test',
|
||||
'uuid': self._uuid,
|
||||
'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._flavor = objects.Flavor(name='testflavor', memory_mb=512,
|
||||
vcpus=1, root_gb=3, ephemeral_gb=10,
|
||||
swap=0, extra_specs={})
|
||||
self._instance.flavor = self._flavor
|
||||
self._instance = fake_instance.fake_instance_obj(self._context)
|
||||
|
||||
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)
|
||||
])
|
||||
def test_driver_init_no_url(self):
|
||||
self.flags(cloud_connector_url=None, group='zvm')
|
||||
self.assertRaises(exception.ZVMDriverException,
|
||||
zvmdriver.ZVMDriver, 'virtapi')
|
||||
|
||||
self.mock_update_task_state = mock.Mock()
|
||||
|
||||
def test_driver_init(self):
|
||||
self.assertEqual(self.driver._hypervisor_hostname, 'TESTHOST')
|
||||
self.assertIsInstance(self.driver._reqh,
|
||||
zvmutils.zVMConnectorRequestHandler)
|
||||
self.assertIsInstance(self.driver._vmutils, zvmutils.VMUtils)
|
||||
self.assertIsInstance(self.driver._imageop_semaphore,
|
||||
eventlet.semaphore.Semaphore)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_list_instance(self, call):
|
||||
call.return_value = ['vm1', 'vm2']
|
||||
inst_list = self.driver.list_instances()
|
||||
self.assertEqual(['vm1', 'vm2'], inst_list)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_get_available_resource(self, call):
|
||||
host_info = {'disk_available': 1144,
|
||||
'ipl_time': u'IPL at 11/14/17 10:47:44 EST',
|
||||
'vcpus_used': 4,
|
||||
'hypervisor_type': u'zvm',
|
||||
'disk_total': 2000,
|
||||
'zvm_host': u'TESTHOST',
|
||||
'memory_mb': 78192.0,
|
||||
'cpu_info': {u'cec_model': u'2827',
|
||||
u'architecture': u's390x'},
|
||||
'vcpus': 84,
|
||||
'hypervisor_hostname': u'TESTHOST',
|
||||
'hypervisor_version': 640,
|
||||
'disk_used': 856,
|
||||
'memory_mb_used': 8192.0}
|
||||
call.return_value = host_info
|
||||
results = self.driver.get_available_resource()
|
||||
self.assertEqual(84, results['vcpus'])
|
||||
self.assertEqual(8192.0, results['memory_mb_used'])
|
||||
self.assertEqual(1144, results['disk_available_least'])
|
||||
self.assertEqual('TESTHOST', results['hypervisor_hostname'])
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_get_available_resource_err_case(self, call):
|
||||
call.side_effect = exception.NovaException
|
||||
results = self.driver.get_available_resource()
|
||||
call.side_effect = exception.ZVMDriverException(error='dummy')
|
||||
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('', results['hypervisor_hostname'])
|
||||
self.assertEqual('TESTHOST', results['hypervisor_hostname'])
|
||||
|
||||
def test_get_available_nodes(self):
|
||||
nodes = self.driver.get_available_nodes()
|
||||
self.assertEqual(['TESTHOST'], nodes)
|
||||
def test_driver_template_validation(self):
|
||||
self.flags(instance_name_template='abc%6d')
|
||||
self.assertRaises(exception.ZVMDriverException,
|
||||
self._driver._validate_options)
|
||||
|
||||
def test_private_mapping_power_stat(self):
|
||||
status = self.driver._mapping_power_stat('on')
|
||||
self.assertEqual(power_state.RUNNING, status)
|
||||
status = self.driver._mapping_power_stat('off')
|
||||
self.assertEqual(power_state.SHUTDOWN, status)
|
||||
status = self.driver._mapping_power_stat('bad')
|
||||
self.assertEqual(power_state.NOSTATE, status)
|
||||
@mock.patch('nova.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.zVMConnectorRequestHandler.call')
|
||||
def test_get_info_err_InstanceNotFound(self, call):
|
||||
call.side_effect = exception.NovaException(results={'overallRC': 404})
|
||||
self.assertRaises(exception.InstanceNotFound, self.driver.get_info,
|
||||
self._instance)
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.spawn')
|
||||
def test_spawn(self, mock_spawn):
|
||||
image_meta = {}
|
||||
injected_files = {}
|
||||
admin_password = 'dummy'
|
||||
allocations = {}
|
||||
self._driver.spawn(self._context, self._instance, image_meta,
|
||||
injected_files, admin_password, allocations)
|
||||
mock_spawn.assert_called_once_with(self._context,
|
||||
image_meta, injected_files, admin_password, allocations,
|
||||
network_info=None, block_device_info=None, flavor=None)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_get_info_err_general(self, call):
|
||||
call.side_effect = exception.NovaException(results={'overallRC': 500})
|
||||
self.assertRaises(exception.NovaException, self.driver.get_info,
|
||||
self._instance)
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.destroy')
|
||||
def test_destroy(self, mock_spawn):
|
||||
self._driver.destroy(self._context, self._instance)
|
||||
mock_spawn.assert_called_once_with(self._context,
|
||||
network_info=None, block_device_info=None, destroy_disks=False)
|
||||
|
||||
@mock.patch('nova.virt.hardware.InstanceInfo')
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_get_info(self, call, InstanceInfo):
|
||||
call.return_value = 'on'
|
||||
self.driver.get_info(self._instance)
|
||||
call.assert_called_once_with('guest_get_power_state',
|
||||
self._instance['name'])
|
||||
InstanceInfo.assert_called_once_with(power_state.RUNNING)
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.snapshot')
|
||||
def test_snapshot(self, mock_snapshot):
|
||||
def fake_update_task_state():
|
||||
pass
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver.list_instances')
|
||||
def test_private_instance_exists_True(self, list_instances):
|
||||
list_instances.return_value = ['VM1', 'VM2']
|
||||
res = self.driver._instance_exists('vm1')
|
||||
self.assertTrue(res)
|
||||
image_id = uuidsentinel.image_id
|
||||
self._driver.snapshot(self._context, self._instance, image_id,
|
||||
fake_update_task_state)
|
||||
mock_snapshot.assert_called_once_with(self._context, image_id,
|
||||
fake_update_task_state)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver.list_instances')
|
||||
def test_private_instance_exists_False(self, list_instances):
|
||||
list_instances.return_value = ['VM1', 'VM2']
|
||||
res = self.driver._instance_exists('vm3')
|
||||
self.assertFalse(res)
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_guest_power_action(self, call, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver._guest_power_action(self._instance, 'guest_start')
|
||||
call.assert_called_once_with('guest_start')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_exists')
|
||||
def test_instance_exists(self, is_exists):
|
||||
is_exists_response = []
|
||||
is_exists_response.append(True)
|
||||
is_exists_response.append(False)
|
||||
is_exists.side_effect = is_exists_response
|
||||
res = self.driver.instance_exists(self._instance)
|
||||
is_exists.assert_any_call(self._instance.name)
|
||||
self.assertTrue(res)
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_guest_power_action_not_exist(self, call, mock_exists):
|
||||
mock_exists.return_value = False
|
||||
self._driver._guest_power_action(self._instance, 'guest_start')
|
||||
self.assertEqual(0, call.called)
|
||||
|
||||
res = self.driver.instance_exists(self._instance)
|
||||
is_exists.assert_any_call(self._instance.name)
|
||||
self.assertFalse(res)
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_power_off(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.power_off(self._instance)
|
||||
ipa.assert_called_once_with('guest_softstop')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_private_get_image_info_err(self, call):
|
||||
call.side_effect = exception.NovaException(results={'overallRC': 500})
|
||||
self.assertRaises(exception.NovaException, self.driver._get_image_info,
|
||||
'context', 'image_meta_id', 'os_distro')
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_power_off_with_timeout_interval(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.power_off(self._instance, 60, 10)
|
||||
ipa.assert_called_once_with('guest_softstop',
|
||||
timeout=60, poll_interval=10)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._import_spawn_image')
|
||||
def test_private_get_image_info(self, image_import, call):
|
||||
call_response = []
|
||||
call_response.append(exception.NovaException(results=
|
||||
{'overallRC': 404}))
|
||||
call_response.append('Query_Result')
|
||||
call.side_effect = call_response
|
||||
self.driver._get_image_info('context', 'image_meta_id', 'os_distro')
|
||||
call.assert_any_call('image_query', imagename='image_meta_id')
|
||||
image_import.assert_called_once_with('context', 'image_meta_id',
|
||||
'os_distro')
|
||||
call.assert_any_call('image_query', imagename='image_meta_id')
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_power_on(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.power_on(None, self._instance, None)
|
||||
ipa.assert_called_once_with('guest_start')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_private_get_image_info_exist(self, call):
|
||||
call.return_value = 'image-info'
|
||||
res = self.driver._get_image_info('context', 'image_meta_id',
|
||||
'os_distro')
|
||||
call.assert_any_call('image_query', imagename='image_meta_id')
|
||||
self.assertEqual('image-info', res)
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_pause(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.pause(self._instance)
|
||||
ipa.assert_called_once_with('guest_pause')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.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'ext4', 'size': '2g'}]
|
||||
_inst = copy.copy(self._instance)
|
||||
_bdi = copy.copy(self._block_device_info)
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_unpause(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.unpause(self._instance)
|
||||
ipa.assert_called_once_with('guest_unpause')
|
||||
|
||||
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
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_reboot_soft(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.reboot(None, self._instance, None, 'SOFT')
|
||||
ipa.assert_called_once_with('guest_reboot')
|
||||
|
||||
if has_eph_disks:
|
||||
disk_list += eph_disk_list
|
||||
else:
|
||||
_bdi['ephemerals'] = []
|
||||
eph_disk_list = []
|
||||
@mock.patch('nova.virt.driver.ComputeDriver.instance_exists')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest.guest_power_action')
|
||||
def test_reboot_hard(self, ipa, mock_exists):
|
||||
mock_exists.return_value = True
|
||||
self._driver.reboot(None, self._instance, None, 'HARD')
|
||||
ipa.assert_called_once_with('guest_reset')
|
||||
|
||||
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.zVMConnectorRequestHandler.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_any_call('guest_create_network_interface',
|
||||
'vm_name', 'os_distro', inst_nets)
|
||||
|
||||
def test_private_nic_coupled(self):
|
||||
user_direct = {'user_direct':
|
||||
['User TEST',
|
||||
"NICDEF 1000 TYPE QDIO LAN SYSTEM TESTVS"]}
|
||||
res = self.driver._nic_coupled(user_direct, '1000', 'TESTVS')
|
||||
self.assertTrue(res)
|
||||
|
||||
res = self.driver._nic_coupled(user_direct, '2000', 'TESTVS')
|
||||
self.assertFalse(res)
|
||||
|
||||
res = self.driver._nic_coupled(user_direct, '1000', None)
|
||||
self.assertFalse(res)
|
||||
|
||||
@mock.patch('pwd.getpwuid')
|
||||
def test_private_get_host(self, getpwuid):
|
||||
class FakePwuid(object):
|
||||
def __init__(self):
|
||||
self.pw_name = 'test'
|
||||
getpwuid.return_value = FakePwuid()
|
||||
res = self.driver._get_host()
|
||||
self.assertEqual('test@192.168.1.1', res)
|
||||
|
||||
@mock.patch('nova.virt.images.fetch')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_host')
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_private_import_spawn_image(self, call, get_host, exists, fetch):
|
||||
get_host.return_value = 'test@192.168.1.1'
|
||||
exists.return_value = False
|
||||
|
||||
image_url = "file:///test/image/image_name"
|
||||
image_meta = {'os_version': 'os_version'}
|
||||
self.driver._import_spawn_image(self._context, 'image_name',
|
||||
'os_version')
|
||||
fetch.assert_called_once_with(self._context, 'image_name',
|
||||
"/test/image/image_name")
|
||||
get_host.assert_called_once_with()
|
||||
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.driver.ZVMDriver._instance_exists')
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_destroy(self, call, instance_exists):
|
||||
instance_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'])
|
||||
|
||||
def test_get_host_uptime(self):
|
||||
with mock.patch('nova_zvm.virt.zvm.utils.'
|
||||
'zVMConnectorRequestHandler.call') as mcall:
|
||||
mcall.return_value = {'hypervisor_hostname': 'TESTHOST',
|
||||
'ipl_time': 'TESTTIME'}
|
||||
time = self.driver.get_host_uptime()
|
||||
self.assertEqual('TESTTIME', time)
|
||||
|
||||
def test_spawn_invalid_userid(self):
|
||||
self.flags(instance_name_template='test%05x')
|
||||
self.addCleanup(self.flags, instance_name_template='test%04x')
|
||||
invalid_inst = fake_instance.fake_instance_obj(self._context,
|
||||
name='123456789')
|
||||
self.assertRaises(exception.InvalidInput, self.driver.spawn,
|
||||
self._context, invalid_inst, self._image_meta,
|
||||
injected_files=None, admin_password=None,
|
||||
allocations=None, network_info=self._network_info,
|
||||
block_device_info=self._block_device_info,
|
||||
flavor=self._flavor)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._wait_network_ready')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._setup_network')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_host')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._set_disk_list')
|
||||
@mock.patch.object(zvmutils.VMUtils, 'generate_configdrive')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_image_info')
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_spawn(self, call, get_image_info, gen_conf_file, set_disk_list,
|
||||
get_host, setup_network, wait_ready):
|
||||
_inst = copy.copy(self._instance)
|
||||
_bdi = copy.copy(self._block_device_info)
|
||||
get_image_info.return_value = [{'imagename': 'image_name'}]
|
||||
gen_conf_file.return_value = 'transportfiles'
|
||||
set_disk_list.return_value = 'disk_list', 'eph_list'
|
||||
get_host.return_value = 'test@192.168.1.1'
|
||||
setup_network.return_value = ''
|
||||
wait_ready.return_value = ''
|
||||
call_resp = ['', '', '', '']
|
||||
call.side_effect = call_resp
|
||||
|
||||
self.driver.spawn(self._context, _inst, self._image_meta,
|
||||
injected_files=None, admin_password=None,
|
||||
allocations=None, network_info=self._network_info,
|
||||
block_device_info=_bdi, flavor=self._flavor)
|
||||
gen_conf_file.assert_called_once_with(self._context, _inst,
|
||||
None, 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(_inst, 'image_name', _bdi)
|
||||
call.assert_any_call('guest_create', _inst['name'],
|
||||
1, 1024, disk_list='disk_list')
|
||||
get_host.assert_called_once_with()
|
||||
call.assert_any_call('guest_deploy', _inst['name'], 'image_name',
|
||||
transportfiles='transportfiles',
|
||||
remotehost='test@192.168.1.1')
|
||||
setup_network.assert_called_once_with(_inst['name'],
|
||||
self._image_meta.properties.os_distro,
|
||||
self._network_info, _inst)
|
||||
call.assert_any_call('guest_config_minidisks', _inst['name'],
|
||||
'eph_list')
|
||||
wait_ready.assert_called_once_with(_inst)
|
||||
call.assert_any_call('guest_start', _inst['name'])
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._nic_coupled')
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_private_wait_network_ready(self, call, nic_coupled):
|
||||
call_resp = []
|
||||
switch_dict = {'1000': 'TESTVM'}
|
||||
user_direct = {'user_direct':
|
||||
['User TEST',
|
||||
"NICDEF 1000 TYPE QDIO LAN SYSTEM TESTVS"]}
|
||||
call_resp.append(switch_dict)
|
||||
call_resp.append(user_direct)
|
||||
call.side_effect = call_resp
|
||||
nic_coupled.return_value = True
|
||||
self.driver._wait_network_ready(self._instance)
|
||||
call.assert_any_call('guest_get_nic_vswitch_info',
|
||||
self._instance['name'])
|
||||
|
||||
call.assert_any_call('guest_get_definition_info',
|
||||
self._instance['name'])
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_instance_power_action(self, call):
|
||||
call.side_effect = [['TEST0001', 'TEST0002'], None]
|
||||
self.driver._instance_power_action(self._instance, 'guest_start')
|
||||
call.assert_any_call('guest_list')
|
||||
call.assert_any_call('guest_start', 'test0001')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_instance_power_action_not_exist(self, call):
|
||||
call.return_value = ['test0002', 'test0003']
|
||||
self.driver._instance_power_action(self._instance, 'guest_start')
|
||||
call.assert_any_call('guest_list')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_power_action')
|
||||
def test_power_off(self, ipa):
|
||||
self.driver.power_off(self._instance)
|
||||
ipa.assert_called_once_with(self._instance, 'guest_softstop')
|
||||
self.driver.power_off(self._instance, 60, 10)
|
||||
ipa.assert_any_call(self._instance, 'guest_softstop',
|
||||
timeout=60, poll_interval=10)
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_power_action')
|
||||
def test_power_on(self, ipa):
|
||||
self.driver.power_on(None, self._instance, None)
|
||||
ipa.assert_called_once_with(self._instance, 'guest_start')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_power_action')
|
||||
def test_pause(self, ipa):
|
||||
self.driver.pause(self._instance)
|
||||
ipa.assert_called_once_with(self._instance, 'guest_pause')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_power_action')
|
||||
def test_unpause(self, ipa):
|
||||
self.driver.unpause(self._instance)
|
||||
ipa.assert_called_once_with(self._instance, 'guest_unpause')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_power_action')
|
||||
def test_reboot_soft(self, ipa):
|
||||
self.driver.reboot(self._context, self._instance, self._network_info,
|
||||
'SOFT')
|
||||
ipa.assert_called_once_with(self._instance, 'guest_reboot')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._instance_power_action')
|
||||
def test_reboot_hard(self, ipa):
|
||||
self.driver.reboot(self._context, self._instance, self._network_info,
|
||||
'HARD')
|
||||
ipa.assert_called_once_with(self._instance, 'guest_reset')
|
||||
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
@mock.patch('nova.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.test_assert_called_once_with('guest_get_console_output',
|
||||
'test0001')
|
||||
outputs = self._driver.get_console_output(None, self._instance)
|
||||
call.assert_called_once_with('guest_get_console_output', 'abc00001')
|
||||
self.assertEqual('console output', outputs)
|
||||
|
||||
@mock.patch.object(builtins, 'open')
|
||||
@mock.patch('nova_zvm.virt.zvm.driver.ZVMDriver._get_host')
|
||||
@mock.patch('nova.image.glance.get_remote_image_service', )
|
||||
@mock.patch('nova_zvm.virt.zvm.utils.zVMConnectorRequestHandler.call')
|
||||
def test_snapshot(self, call, get_image_service, get_host, mock_open):
|
||||
image_service = mock.Mock()
|
||||
image_id = 'e9ee1562-3ea1-4cb1-9f4c-f2033000eab1'
|
||||
get_image_service.return_value = (image_service, image_id)
|
||||
host_info = 'nova@192.168.99.1'
|
||||
get_host.return_value = host_info
|
||||
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': const.ARCHITECTURE,
|
||||
'hypervisor_type': const.HYPERVISOR_TYPE
|
||||
},
|
||||
'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)
|
||||
|
||||
call.assert_any_call('guest_capture',
|
||||
self._instance['name'], image_id)
|
||||
mock_open.assert_called_once_with(image_path, 'r')
|
||||
image_service.update.assert_called_once_with(self._context,
|
||||
image_id,
|
||||
new_image_meta,
|
||||
mock_open.return_value.__enter__.return_value,
|
||||
purge_props=False)
|
||||
call.assert_any_call('image_export', image_id, dest_path,
|
||||
remote_host=host_info)
|
||||
call.assert_any_call('image_delete', image_id)
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
# 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.compute import power_state as compute_power_state
|
||||
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.virt.zvm import driver
|
||||
from nova.virt.zvm import guest
|
||||
|
||||
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
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.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._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._guest = guest.Guest(self._hypervisor, self._instance,
|
||||
self._driver.virtapi)
|
||||
self._flavor = objects.Flavor(name='testflavor', memory_mb=512,
|
||||
vcpus=1, root_gb=3, ephemeral_gb=10,
|
||||
swap=0, extra_specs={})
|
||||
self._instance.flavor = self._flavor
|
||||
|
||||
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_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.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_get_info_err_InstanceNotFound(self, call):
|
||||
call.side_effect = exception.NovaException(results={'overallRC': 404})
|
||||
self.assertRaises(exception.InstanceNotFound, self._guest.get_info)
|
||||
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_get_info_err_general(self, call):
|
||||
call.side_effect = exception.NovaException(results={'overallRC': 500})
|
||||
self.assertRaises(exception.NovaException, self._guest.get_info)
|
||||
|
||||
@mock.patch('nova.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)
|
||||
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_private_get_image_info_err(self, call):
|
||||
call.side_effect = exception.NovaException(results={'overallRC': 500})
|
||||
self.assertRaises(exception.NovaException,
|
||||
self._guest._get_image_info,
|
||||
'context', 'image_meta_id', 'os_distro')
|
||||
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest._import_spawn_image')
|
||||
def test_private_get_image_info(self, image_import, call):
|
||||
call_response = []
|
||||
call_response.append(exception.NovaException(results=
|
||||
{'overallRC': 404}))
|
||||
call_response.append('Query_Result')
|
||||
call.side_effect = call_response
|
||||
self._guest._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'),
|
||||
mock.call('image_query', imagename='image_meta_id')
|
||||
])
|
||||
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_private_get_image_info_exist(self, call):
|
||||
call.return_value = 'image-info'
|
||||
res = self._guest._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.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'ext4', 'size': '2g'}]
|
||||
_inst = copy.copy(self._instance)
|
||||
_bdi = copy.copy(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._guest._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.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._guest._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.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._guest._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, 'test@192.168.1.1')
|
||||
|
||||
@mock.patch('nova.virt.zvm.hypervisor.Hypervisor.guest_exists')
|
||||
@mock.patch('nova.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_destroy(self, call, guest_exists):
|
||||
guest_exists.return_value = True
|
||||
self._guest.destroy(self._context, network_info=self._network_info)
|
||||
call.assert_called_once_with('guest_delete', self._instance['name'])
|
||||
|
||||
@mock.patch('nova.virt.zvm.hypervisor.Hypervisor.guest_exists')
|
||||
@mock.patch('nova.compute.manager.ComputeVirtAPI.wait_for_instance_event')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest._setup_network')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest._set_disk_list')
|
||||
@mock.patch('nova.virt.zvm.utils.generate_configdrive')
|
||||
@mock.patch('nova.virt.zvm.guest.Guest._get_image_info')
|
||||
@mock.patch('nova.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 = [{'imagename': 'image_name'}]
|
||||
gen_conf_file.return_value = 'transportfiles'
|
||||
set_disk_list.return_value = 'disk_list', 'eph_list'
|
||||
mock_exists.return_value = False
|
||||
self._guest.spawn(self._context, self._image_meta,
|
||||
injected_files=None, admin_password=None,
|
||||
allocations=None, network_info=self._network_info,
|
||||
block_device_info=_bdi, flavor=self._flavor)
|
||||
gen_conf_file.assert_called_once_with(self._context, self._instance,
|
||||
None, 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', 'test@192.168.1.1'),
|
||||
mock.call('guest_config_minidisks', self._instance.name,
|
||||
'eph_list'),
|
||||
mock.call('guest_start', self._instance.name)
|
||||
])
|
||||
|
||||
@mock.patch.object(six.moves.builtins, 'open')
|
||||
@mock.patch('nova.image.glance.get_remote_image_service')
|
||||
@mock.patch('nova.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._guest.snapshot(self._context, 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, 'test@192.168.1.1'),
|
||||
mock.call('image_delete', image_id)
|
||||
])
|
||||
|
||||
@mock.patch('nova.image.glance.get_remote_image_service')
|
||||
@mock.patch('nova.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 = exception.ZVMDriverException(error='error')
|
||||
|
||||
self.assertRaises(exception.ZVMDriverException, self._guest.snapshot,
|
||||
self._context, 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.virt.zvm.utils.ConnectorClient.call')
|
||||
@mock.patch('nova.virt.zvm.hypervisor.Hypervisor.image_delete')
|
||||
@mock.patch('nova.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 = exception.ZVMDriverException(error='error')
|
||||
|
||||
self.assertRaises(exception.ZVMDriverException, self._guest.snapshot,
|
||||
self._context, 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.virt.zvm.utils.ConnectorClient.call')
|
||||
@mock.patch('nova.virt.zvm.hypervisor.Hypervisor.image_delete')
|
||||
@mock.patch('nova.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._guest.snapshot,
|
||||
self._context, 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)
|
|
@ -0,0 +1,92 @@
|
|||
# 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 exception
|
||||
from nova import test
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.virt.zvm import driver as zvmdriver
|
||||
|
||||
|
||||
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.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.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.virt.zvm.utils.ConnectorClient.call')
|
||||
def test_get_available_resource_err_case(self, call):
|
||||
call.side_effect = exception.ZVMDriverException(error='dummy')
|
||||
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.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)
|
||||
|
||||
def test_get_host_uptime(self):
|
||||
time = self._hypervisor.get_host_uptime()
|
||||
self.assertEqual('IPL at 11/14/17 10:47:44 EST', time)
|
||||
|
||||
@mock.patch('nova.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, 'test0002']
|
||||
res = self._hypervisor.guest_exists(instance)
|
||||
self.assertTrue(res)
|
||||
|
||||
@mock.patch('nova.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)
|
|
@ -0,0 +1,141 @@
|
|||
# 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.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(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'}
|
||||
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'}
|
||||
mock_send.return_value = expected
|
||||
|
||||
rh = zvmutils.ConnectorClient(self._url)
|
||||
exc = self.assertRaises(exception.ZVMDriverException, rh.call,
|
||||
'guest_list')
|
||||
self.assertEqual('zVM Cloud Connector request failed',
|
||||
exc.format_message())
|
||||
self.assertEqual(expected, exc.kwargs['results'])
|
||||
|
||||
@mock.patch('nova.virt.zvm.utils._get_instances_path')
|
||||
def test_get_instance_path(self, fake_get):
|
||||
fake_get.return_value = '/test/tmp'
|
||||
|
||||
with mock.patch('os.path.exists') as fake_exist:
|
||||
fake_exist.return_value = True
|
||||
folder = zvmutils.get_instance_path('fake_uuid')
|
||||
self.assertEqual('/test/tmp/fake_uuid', folder)
|
||||
fake_get.assert_called_once_with()
|
||||
|
||||
@mock.patch('nova.virt.configdrive.required_by')
|
||||
@mock.patch('nova.virt.zvm.utils._create_config_drive')
|
||||
@mock.patch('nova.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',
|
||||
'admin_password')
|
||||
required.assert_called_once_with(instance)
|
||||
create.assert_called_once_with('context', '/test/tmp/fake_uuid',
|
||||
instance, 'injected_files',
|
||||
'admin_password')
|
||||
self.assertEqual('/test/cfgdrive.tgz', file)
|
||||
|
||||
@mock.patch('nova.api.metadata.base.InstanceMetadata')
|
||||
@mock.patch('nova.virt.zvm.configdrive.ZVMConfigDriveBuilder.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',
|
||||
'admin_password')
|
||||
mock_instance_metadata.assert_called_once_with('instance',
|
||||
content='injected_files',
|
||||
extra_md=extra_md,
|
||||
request_context='context')
|
||||
make_drive.assert_called_once_with('/instance_path/cfgdrive.tgz')
|
||||
|
||||
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',
|
||||
'admin_password')
|
|
@ -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,18 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""A connection to an IBM z/VM Virtualization system.
|
||||
|
||||
Generally, OpenStack z/VM virt driver will call xCat REST API to operate
|
||||
to z/VM hypervisors.xCat has a control point(a virtual machine) in z/VM
|
||||
system, which enables xCat management node to control the z/VM system.
|
||||
OpenStack z/VM driver will communicate with xCat management node through
|
||||
xCat REST API. Thus OpenStack can operate to z/VM system indirectly.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from nova_zvm.virt.zvm import driver
|
||||
|
||||
from nova.virt.zvm import driver
|
||||
|
||||
ZVMDriver = driver.ZVMDriver
|
||||
|
|
|
@ -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,7 +12,6 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
import six
|
||||
import tarfile
|
||||
|
@ -42,6 +41,11 @@ class ZVMConfigDriveBuilder(configdrive.ConfigDriveBuilder):
|
|||
|
||||
"""
|
||||
if CONF.config_drive_format in ['iso9660']:
|
||||
# cloud-init only support iso9660 and vfat, but in z/VM
|
||||
# implementation, can't link a disk to VM as iso9660 before it's
|
||||
# boot ,so create a tgz file then send to the VM deployed, and
|
||||
# during startup process, the tgz file will be extracted and
|
||||
# mounted as iso9660 format then cloud-init is able to consume it
|
||||
self._make_tgz(path)
|
||||
else:
|
||||
raise exception.ConfigDriveUnknownFormat(
|
||||
|
@ -63,6 +67,6 @@ class ZVMConfigDriveBuilder(configdrive.ConfigDriveBuilder):
|
|||
os.chdir(olddir)
|
||||
except Exception as e:
|
||||
emsg = six.text_type(e)
|
||||
LOG.debug('exception in _make_tgz %s', emsg)
|
||||
LOG.debug('exception in chdir: %s', emsg)
|
||||
|
||||
tar.close()
|
||||
|
|
|
@ -1,30 +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'
|
||||
DEFAULT_EPH_DISK_FMT = 'ext3'
|
||||
|
||||
ZVM_POWER_STAT = {
|
||||
'on': power_state.RUNNING,
|
||||
'off': power_state.SHUTDOWN,
|
||||
}
|
||||
|
||||
REBOOT_TYPE_SOFT = 'SOFT'
|
||||
REBOOT_TYPE_HARD = 'HARD'
|
|
@ -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,485 +12,151 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import eventlet
|
||||
import os
|
||||
import pwd
|
||||
import time
|
||||
|
||||
from nova.compute import power_state
|
||||
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.virt import driver
|
||||
from nova.virt import hardware
|
||||
from nova.virt import images
|
||||
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 nova_zvm.virt.zvm import conf
|
||||
from nova_zvm.virt.zvm import const
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
from nova import conf
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.objects import fields as obj_fields
|
||||
from nova.virt import driver
|
||||
from nova.virt.zvm import guest
|
||||
from nova.virt.zvm import hypervisor
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
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._reqh = zvmutils.zVMConnectorRequestHandler()
|
||||
self._vmutils = zvmutils.VMUtils()
|
||||
self._pathutils = zvmutils.PathUtils()
|
||||
self._imageop_semaphore = eventlet.semaphore.Semaphore(1)
|
||||
|
||||
# get hypervisor host name
|
||||
res = self._reqh.call('host_get_info')
|
||||
self._hypervisor_hostname = res['hypervisor_hostname']
|
||||
self._virtapi = virtapi
|
||||
self._validate_options()
|
||||
|
||||
self._hypervisor = hypervisor.Hypervisor(
|
||||
CONF.zvm.cloud_connector_url, ca_file=CONF.zvm.ca_file)
|
||||
|
||||
LOG.info("The zVM compute driver has been initialized.")
|
||||
|
||||
def _validate_options(self):
|
||||
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 exception.ZVMDriverException(error=error)
|
||||
|
||||
# Try a test to ensure length of give guest is smaller than 8
|
||||
_test_instance = CONF.instance_name_template % 0
|
||||
# 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 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.
|
||||
"""
|
||||
pass
|
||||
|
||||
def list_instances(self):
|
||||
"""Return the names of all the instances known to the virtualization
|
||||
layer, as a list.
|
||||
"""
|
||||
return self._reqh.call('guest_list')
|
||||
|
||||
def _get_host_status(self):
|
||||
LOG.debug("Getting host status for %s", CONF.host)
|
||||
|
||||
info = self._reqh.call('host_get_info')
|
||||
|
||||
host_status = {'host': CONF.host,
|
||||
'allowed_vm_type': const.ALLOWED_VM_TYPE}
|
||||
host_status['vcpus'] = info['vcpus']
|
||||
host_status['vcpus_used'] = info['vcpus_used']
|
||||
host_status['cpu_info'] = info['cpu_info']
|
||||
host_status['disk_total'] = info['disk_total']
|
||||
host_status['disk_used'] = info['disk_used']
|
||||
host_status['disk_available'] = info['disk_available']
|
||||
host_status['host_memory_total'] = info['memory_mb']
|
||||
host_status['host_memory_free'] = (info['memory_mb'] -
|
||||
info['memory_mb_used'])
|
||||
host_status['hypervisor_type'] = info['hypervisor_type']
|
||||
host_status['hypervisor_version'] = info['hypervisor_version']
|
||||
host_status['hypervisor_hostname'] = info['hypervisor_hostname']
|
||||
host_status['supported_instances'] = [(const.ARCHITECTURE,
|
||||
const.HYPERVISOR_TYPE,
|
||||
obj_fields.VMMode.HVM)]
|
||||
host_status['ipl_time'] = info['ipl_time']
|
||||
|
||||
return host_status
|
||||
return self._hypervisor.list_names()
|
||||
|
||||
def get_available_resource(self, nodename=None):
|
||||
LOG.debug("Getting available resource for %s", CONF.host)
|
||||
|
||||
try:
|
||||
host_stats = self._reqh.call('host_get_info')
|
||||
except exception.NovaException:
|
||||
host_stats = {}
|
||||
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': 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', 'zvm'),
|
||||
'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': 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': [(const.ARCHITECTURE,
|
||||
const.HYPERVISOR_TYPE,
|
||||
'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_hostname]
|
||||
|
||||
def _mapping_power_stat(self, power_stat):
|
||||
"""Translate power state to OpenStack defined constants."""
|
||||
return const.ZVM_POWER_STAT.get(power_stat, power_state.NOSTATE)
|
||||
return self._hypervisor.get_available_nodes(refresh=refresh)
|
||||
|
||||
def get_info(self, instance):
|
||||
"""Get the current status of an instance."""
|
||||
power_stat = ''
|
||||
try:
|
||||
power_stat = self._reqh.call('guest_get_power_state',
|
||||
instance['name'])
|
||||
except exception.NovaException as err:
|
||||
if err.kwargs['results']['overallRC'] == 404:
|
||||
# instance not exists
|
||||
LOG.warning("Get power state of non-exist instance: %s",
|
||||
instance['name'])
|
||||
raise exception.InstanceNotFound(instance_id=instance['name'])
|
||||
else:
|
||||
raise
|
||||
|
||||
power_stat = self._mapping_power_stat(power_stat)
|
||||
_instance_info = hardware.InstanceInfo(power_stat)
|
||||
|
||||
return _instance_info
|
||||
|
||||
def _instance_exists(self, instance_name):
|
||||
"""Overwrite this to using instance name as input parameter."""
|
||||
return instance_name.upper() in self.list_instances()
|
||||
|
||||
def instance_exists(self, instance):
|
||||
"""Overwrite this to using instance name as input parameter."""
|
||||
return self._instance_exists(instance.name)
|
||||
_guest = guest.Guest(self._hypervisor, instance)
|
||||
return _guest.get_info()
|
||||
|
||||
def spawn(self, context, instance, image_meta, injected_files,
|
||||
admin_password, allocations, network_info=None,
|
||||
block_device_info=None, flavor=None):
|
||||
LOG.info(_("Spawning new instance %s on zVM hypervisor"),
|
||||
instance['name'], instance=instance)
|
||||
# For zVM instance, limit the maximum length of instance name to \ 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 exception.InvalidInput(reason=msg)
|
||||
try:
|
||||
spawn_start = time.time()
|
||||
os_distro = image_meta.properties.os_distro
|
||||
transportfiles = self._vmutils.generate_configdrive(
|
||||
context, instance, injected_files, admin_password)
|
||||
_guest = guest.Guest(self._hypervisor, instance, virtapi=self.virtapi)
|
||||
|
||||
resp = self._get_image_info(context, image_meta.id, os_distro)
|
||||
spawn_image_name = resp[0]['imagename']
|
||||
disk_list, eph_list = self._set_disk_list(instance,
|
||||
spawn_image_name,
|
||||
block_device_info)
|
||||
|
||||
# Create the guest vm
|
||||
self._reqh.call('guest_create', instance['name'],
|
||||
instance['vcpus'], instance['memory_mb'],
|
||||
disk_list=disk_list)
|
||||
|
||||
# Deploy image to the guest vm
|
||||
remotehost = self._get_host()
|
||||
self._reqh.call('guest_deploy', instance['name'],
|
||||
spawn_image_name, transportfiles=transportfiles,
|
||||
remotehost=remotehost)
|
||||
|
||||
# Setup network for z/VM instance
|
||||
self._setup_network(instance['name'], os_distro, network_info,
|
||||
instance)
|
||||
|
||||
# Handle ephemeral disks
|
||||
if eph_list:
|
||||
self._reqh.call('guest_config_minidisks',
|
||||
instance['name'], eph_list)
|
||||
|
||||
self._wait_network_ready(instance)
|
||||
|
||||
self._reqh.call('guest_start', instance['name'])
|
||||
spawn_time = time.time() - spawn_start
|
||||
LOG.info(_("Instance spawned succeeded in %s seconds"),
|
||||
spawn_time, instance=instance)
|
||||
except Exception as err:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_("Deploy image to instance %(instance)s "
|
||||
"failed with reason: %(err)s"),
|
||||
{'instance': instance['name'], 'err': err},
|
||||
instance=instance)
|
||||
self.destroy(context, instance, network_info,
|
||||
block_device_info)
|
||||
|
||||
def _get_image_info(self, context, image_meta_id, os_distro):
|
||||
spawn_image_exist = False
|
||||
try:
|
||||
spawn_image_exist = self._reqh.call('image_query',
|
||||
imagename=image_meta_id)
|
||||
except exception.NovaException as err:
|
||||
if err.kwargs['results']['overallRC'] == 404:
|
||||
# image not exist, nothing to do
|
||||
pass
|
||||
else:
|
||||
raise err
|
||||
|
||||
if not spawn_image_exist:
|
||||
with self._imageop_semaphore:
|
||||
self._import_spawn_image(context, image_meta_id, os_distro)
|
||||
return self._reqh.call('image_query', imagename=image_meta_id)
|
||||
else:
|
||||
return spawn_image_exist
|
||||
|
||||
def _set_disk_list(self, instance, image_name, block_device_info):
|
||||
if instance['root_gb'] == 0:
|
||||
root_disk_size = self._reqh.call('image_get_root_disk_size',
|
||||
image_name)
|
||||
else:
|
||||
root_disk_size = '%ig' % instance['root_gb']
|
||||
|
||||
disk_list = []
|
||||
root_disk = {'size': root_disk_size,
|
||||
'is_boot_disk': True
|
||||
}
|
||||
disk_list.append(root_disk)
|
||||
ephemeral_disks_info = block_device_info.get('ephemerals', [])
|
||||
eph_list = []
|
||||
for eph in ephemeral_disks_info:
|
||||
eph_dict = {'size': '%ig' % eph['size'],
|
||||
'format': (eph['guest_format'] or
|
||||
CONF.default_ephemeral_format or
|
||||
const.DEFAULT_EPH_DISK_FMT)}
|
||||
eph_list.append(eph_dict)
|
||||
|
||||
if eph_list:
|
||||
disk_list.extend(eph_list)
|
||||
return disk_list, eph_list
|
||||
|
||||
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)
|
||||
|
||||
if inst_nets:
|
||||
self._reqh.call('guest_create_network_interface',
|
||||
vm_name, os_distro, inst_nets)
|
||||
|
||||
def _wait_network_ready(self, instance):
|
||||
"""Wait until neutron zvm-agent add all NICs to vm"""
|
||||
inst_name = instance['name']
|
||||
|
||||
def _wait_for_nics_add_in_vm(inst_name, expiration):
|
||||
if (CONF.zvm_reachable_timeout and
|
||||
timeutils.utcnow() > expiration):
|
||||
msg = _("NIC update check failed "
|
||||
"on instance:%s") % instance.uuid
|
||||
raise exception.NovaException(message=msg)
|
||||
|
||||
try:
|
||||
switch_dict = self._reqh.call('guest_get_nic_vswitch_info',
|
||||
inst_name)
|
||||
if switch_dict and None not in switch_dict.values():
|
||||
for key, value in switch_dict.items():
|
||||
user_direct = self._reqh.call(
|
||||
'guest_get_definition_info',
|
||||
inst_name)
|
||||
if not self._nic_coupled(user_direct, key, value):
|
||||
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 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_nics_add_in_vm, inst_name, expiration)
|
||||
timer.start(interval=10).wait()
|
||||
|
||||
def _nic_coupled(self, user_direct, vdev, vswitch):
|
||||
if vswitch is None:
|
||||
return False
|
||||
direct_info = user_direct['user_direct']
|
||||
nic_str = ("NICDEF %s TYPE QDIO LAN SYSTEM %s" %
|
||||
(vdev.upper(), vswitch.upper()))
|
||||
for info in direct_info:
|
||||
if nic_str in info:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_host(self):
|
||||
return ''.join([pwd.getpwuid(os.geteuid()).pw_name, '@', CONF.my_ip])
|
||||
|
||||
def _import_spawn_image(self, context, image_href, image_os_version):
|
||||
LOG.debug("Downloading the image %s from glance to nova compute "
|
||||
"server", image_href)
|
||||
image_path = os.path.join(os.path.normpath(CONF.zvm_image_tmp_path),
|
||||
image_href)
|
||||
if not os.path.exists(image_path):
|
||||
images.fetch(context, image_href, image_path)
|
||||
image_url = "file://" + image_path
|
||||
image_meta = {'os_version': image_os_version}
|
||||
remote_host = self._get_host()
|
||||
self._reqh.call('image_import', image_href, image_url,
|
||||
image_meta, remote_host=remote_host)
|
||||
_guest.spawn(context, image_meta, injected_files,
|
||||
admin_password, allocations,
|
||||
network_info=network_info,
|
||||
block_device_info=block_device_info, flavor=flavor)
|
||||
|
||||
def destroy(self, context, instance, network_info=None,
|
||||
block_device_info=None, destroy_disks=False):
|
||||
inst_name = instance['name']
|
||||
if self._instance_exists(inst_name):
|
||||
LOG.info(_("Destroying instance %s"), inst_name,
|
||||
instance=instance)
|
||||
self._reqh.call('guest_delete', inst_name)
|
||||
else:
|
||||
LOG.warning(_('Instance %s does not exist'), inst_name,
|
||||
instance=instance)
|
||||
_guest = guest.Guest(self._hypervisor, instance)
|
||||
_guest.destroy(context, network_info=network_info,
|
||||
block_device_info=block_device_info,
|
||||
destroy_disks=destroy_disks)
|
||||
|
||||
def get_host_uptime(self):
|
||||
res = self._reqh.call('host_get_info')
|
||||
return res['ipl_time']
|
||||
return self._hypervisor.get_host_uptime()
|
||||
|
||||
def _instance_power_action(self, instance, action, *args, **kwargs):
|
||||
inst_name = instance['name']
|
||||
if self._instance_exists(inst_name):
|
||||
LOG.info(_("Power action %(action)s to instance %(inst_name)s") %
|
||||
{'action': action, 'inst_name': inst_name},
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
_guest = guest.Guest(self._hypervisor, instance)
|
||||
_guest.snapshot(context, image_id, update_task_state)
|
||||
|
||||
def _guest_power_action(self, instance, action, *args, **kwargs):
|
||||
if self.instance_exists(instance):
|
||||
LOG.info("Power action %(action)s to instance", {'action': action},
|
||||
instance=instance)
|
||||
self._reqh.call(action, inst_name)
|
||||
_guest = guest.Guest(self._hypervisor, instance)
|
||||
_guest.guest_power_action(action, *args, **kwargs)
|
||||
else:
|
||||
LOG.warning(_('Instance %s does not exist'), inst_name,
|
||||
instance=instance)
|
||||
LOG.warning("Instance does not exist", instance=instance)
|
||||
|
||||
def power_off(self, instance, timeout=0, retry_interval=0):
|
||||
"""Power off the specified instance."""
|
||||
if timeout >= 0 and retry_interval > 0:
|
||||
self._instance_power_action(instance, 'guest_softstop',
|
||||
timeout=timeout,
|
||||
poll_interval=retry_interval)
|
||||
self._guest_power_action(instance, 'guest_softstop',
|
||||
timeout=timeout,
|
||||
poll_interval=retry_interval)
|
||||
else:
|
||||
self._instance_power_action(instance, 'guest_softstop')
|
||||
self._guest_power_action(instance, 'guest_softstop')
|
||||
|
||||
def power_on(self, context, instance, network_info,
|
||||
block_device_info=None):
|
||||
"""Power on the specified instance."""
|
||||
self._instance_power_action(instance, 'guest_start')
|
||||
self._guest_power_action(instance, 'guest_start')
|
||||
|
||||
def pause(self, instance):
|
||||
"""Pause the z/VM instance."""
|
||||
self._instance_power_action(instance, 'guest_pause')
|
||||
self._guest_power_action(instance, 'guest_pause')
|
||||
|
||||
def unpause(self, instance):
|
||||
"""Unpause the z/VM instance."""
|
||||
self._instance_power_action(instance, 'guest_unpause')
|
||||
self._guest_power_action(instance, 'guest_unpause')
|
||||
|
||||
def reboot(self, context, instance, network_info, reboot_type,
|
||||
block_device_info=None, bad_volumes_callback=None):
|
||||
if reboot_type == const.REBOOT_TYPE_SOFT:
|
||||
self._instance_power_action(instance, 'guest_reboot')
|
||||
if reboot_type == 'SOFT':
|
||||
self._guest_power_action(instance, 'guest_reboot')
|
||||
else:
|
||||
self._instance_power_action(instance, 'guest_reset')
|
||||
self._guest_power_action(instance, 'guest_reset')
|
||||
|
||||
def get_console_output(self, context, instance):
|
||||
return self._reqh.call('guest_get_console_output', instance.name)
|
||||
|
||||
def snapshot(self, context, instance, image_id, update_task_state):
|
||||
"""Create snapshot from a running VM instance.
|
||||
|
||||
:param context: security context
|
||||
:param instance: nova.objects.instance.Instance
|
||||
:param image_id: Reference to a pre-created image that will
|
||||
hold the snapshot.
|
||||
:param update_task_state: Callback function to update the task_state
|
||||
on the instance while the snapshot operation progresses. The
|
||||
function takes a task_state argument and an optional
|
||||
expected_task_state kwarg which defaults to
|
||||
nova.compute.task_states.IMAGE_SNAPSHOT. See
|
||||
nova.objects.instance.Instance.save for expected_task_state usage.
|
||||
"""
|
||||
|
||||
# Check the image status
|
||||
(image_service, image_id) = glance.get_remote_image_service(
|
||||
context, image_id)
|
||||
|
||||
# Update the instance task_state to image_pending_upload
|
||||
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
|
||||
# Call zvmsdk guest_capture to generate the image
|
||||
try:
|
||||
self._reqh.call('guest_capture', instance['name'], image_id)
|
||||
except Exception as err:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(_("Failed to capture the instance %(instance)s "
|
||||
"to generate an image with reason: %(err)s"),
|
||||
{'instance': instance['name'], '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._reqh.call('image_export', image_id,
|
||||
dest_path,
|
||||
remote_host=self._get_host())
|
||||
except Exception as err:
|
||||
LOG.error(_("Failed to export image %s from SDK server to nova "
|
||||
"compute server") % image_id)
|
||||
self._reqh.call('image_delete', image_id)
|
||||
# Save image to glance
|
||||
new_image_meta = {
|
||||
'is_public': False,
|
||||
'status': 'active',
|
||||
'properties': {
|
||||
'image_location': 'snapshot',
|
||||
'image_state': 'available',
|
||||
'owner_id': instance['project_id'],
|
||||
'os_distro': resp['os_version'],
|
||||
'architecture': const.ARCHITECTURE,
|
||||
'hypervisor_type': const.HYPERVISOR_TYPE
|
||||
},
|
||||
'disk_format': 'raw',
|
||||
'container_format': 'bare',
|
||||
}
|
||||
|
||||
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
# Save the image to glance
|
||||
try:
|
||||
with open(image_path, 'r') as image_file:
|
||||
image_service.update(context,
|
||||
image_id,
|
||||
new_image_meta,
|
||||
image_file,
|
||||
purge_props=False)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
image_service.delete(context, image_id)
|
||||
finally:
|
||||
self._pathutils.clean_up_file(image_path)
|
||||
self._reqh.call('image_delete', image_id)
|
||||
LOG.debug("Snapshot image upload complete", instance=instance)
|
||||
return self._hypervisor.guest_get_console_output(instance.name)
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
# 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 eventlet
|
||||
import os
|
||||
import six
|
||||
import time
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from nova.compute import power_state as compute_power_state
|
||||
from nova.compute import task_states
|
||||
from nova import conf
|
||||
from nova import exception
|
||||
from nova.image import glance
|
||||
from nova.objects import fields as obj_fields
|
||||
from nova import utils
|
||||
from nova.virt import hardware
|
||||
from nova.virt import images
|
||||
from nova.virt.zvm import utils as zvmutils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
DEFAULT_EPH_DISK_FMT = 'ext3'
|
||||
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)
|
||||
|
||||
def spawn(self, context, image_meta, injected_files,
|
||||
admin_password, allocations, network_info=None,
|
||||
block_device_info=None, flavor=None):
|
||||
|
||||
LOG.info("Spawning new instance %s on zVM hypervisor",
|
||||
self._instance.name, instance=self._instance)
|
||||
|
||||
if self._exists_in_hypervisor():
|
||||
raise exception.InstanceExists(name=self._instance.name)
|
||||
|
||||
try:
|
||||
spawn_start = time.time()
|
||||
os_distro = image_meta.properties.os_distro
|
||||
transportfiles = zvmutils.generate_configdrive(context,
|
||||
self._instance, injected_files, admin_password)
|
||||
|
||||
resp = self._get_image_info(context, image_meta.id, os_distro)
|
||||
spawn_image_name = resp[0]['imagename']
|
||||
disk_list, eph_list = self._set_disk_list(self._instance,
|
||||
spawn_image_name,
|
||||
block_device_info)
|
||||
|
||||
# Create the guest vm
|
||||
self._hypervisor.guest_create(self._instance.name,
|
||||
self._instance.vcpus, self._instance.memory_mb,
|
||||
disk_list)
|
||||
|
||||
# Deploy image to the guest vm
|
||||
self._hypervisor.guest_deploy(self._instance.name,
|
||||
spawn_image_name, transportfiles=transportfiles)
|
||||
|
||||
# Handle ephemeral disks
|
||||
if eph_list:
|
||||
self._hypervisor.guest_config_minidisks(self._instance.name,
|
||||
eph_list)
|
||||
# Setup network for z/VM instance
|
||||
self._wait_vif_plug_events(self._instance.name, os_distro,
|
||||
network_info, self._instance)
|
||||
|
||||
self._hypervisor.guest_start(self._instance.name)
|
||||
spawn_time = time.time() - spawn_start
|
||||
LOG.info("Instance spawned successfully in %s seconds",
|
||||
spawn_time, instance=self._instance)
|
||||
except Exception as err:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error("Deploy instance %(instance)s "
|
||||
"failed with reason: %(err)s",
|
||||
{'instance': self._instance.name, 'err': err},
|
||||
instance=self._instance)
|
||||
try:
|
||||
self.destroy(context, self._instance, network_info,
|
||||
block_device_info)
|
||||
except Exception as err:
|
||||
LOG.exception("Failed to destroy instance",
|
||||
instance=self._instance)
|
||||
|
||||
@lockutils.synchronized('IMAGE_INFO_SEMAPHORE')
|
||||
def _get_image_info(self, context, image_meta_id, os_distro):
|
||||
try:
|
||||
return self._hypervisor.image_query(imagename=image_meta_id)
|
||||
except exception.NovaException as err:
|
||||
with excutils.save_and_reraise_exception() as sare:
|
||||
if err.kwargs['results']['overallRC'] == 404:
|
||||
sare.reraise = False
|
||||
self._import_spawn_image(context, image_meta_id, os_distro)
|
||||
return self._hypervisor.image_query(
|
||||
imagename=image_meta_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:
|
||||
root_disk_size = '%ig' % instance.root_gb
|
||||
|
||||
disk_list = []
|
||||
root_disk = {'size': root_disk_size,
|
||||
'is_boot_disk': True
|
||||
}
|
||||
disk_list.append(root_disk)
|
||||
ephemeral_disks_info = block_device_info.get('ephemerals', [])
|
||||
eph_list = []
|
||||
for eph in ephemeral_disks_info:
|
||||
eph_dict = {'size': '%ig' % eph['size'],
|
||||
'format': (eph['guest_format'] or
|
||||
CONF.default_ephemeral_format or
|
||||
DEFAULT_EPH_DISK_FMT)}
|
||||
eph_list.append(eph_dict)
|
||||
|
||||
if eph_list:
|
||||
disk_list.extend(eph_list)
|
||||
return disk_list, eph_list
|
||||
|
||||
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)
|
||||
|
||||
if inst_nets:
|
||||
self._hypervisor.guest_create_network_interface(vm_name,
|
||||
os_distro, inst_nets)
|
||||
|
||||
def _get_neutron_event(self, instance, 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 []
|
||||
|
||||
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(instance, 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:
|
||||
LOG.error("Failed for vif plugging: %s", six.text_type(err),
|
||||
instance=instance)
|
||||
raise
|
||||
|
||||
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 _exists_in_hypervisor(self):
|
||||
return self._hypervisor.guest_exists(self._instance)
|
||||
|
||||
def destroy(self, context, network_info=None,
|
||||
block_device_info=None, destroy_disks=False):
|
||||
if self._exists_in_hypervisor():
|
||||
LOG.info("Destroying instance", instance=self._instance)
|
||||
try:
|
||||
self._hypervisor.guest_delete(self._instance.name)
|
||||
except exception.NovaException as err:
|
||||
if err.kwargs['results']['overallRC'] == 404:
|
||||
LOG.info("guest disappear during destroy",
|
||||
instance=self._instance)
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
LOG.warning("Instance does not exist", instance=self._instance)
|
||||
|
||||
def snapshot(self, context, image_id, update_task_state):
|
||||
(image_service, image_id) = glance.get_remote_image_service(
|
||||
context, image_id)
|
||||
|
||||
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
|
||||
instance = self._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',
|
||||
'properties': {
|
||||
'image_location': 'snapshot',
|
||||
'image_state': 'available',
|
||||
'owner_id': instance['project_id'],
|
||||
'os_distro': resp['os_version'],
|
||||
'architecture': obj_fields.Architecture.S390X,
|
||||
'hypervisor_type': obj_fields.HVType.ZVM,
|
||||
},
|
||||
'disk_format': 'raw',
|
||||
'container_format': 'bare',
|
||||
}
|
||||
update_task_state(task_state=task_states.IMAGE_UPLOADING,
|
||||
expected_state=task_states.IMAGE_PENDING_UPLOAD)
|
||||
|
||||
# Save the image to glance
|
||||
try:
|
||||
with open(image_path, 'r') as image_file:
|
||||
image_service.update(context,
|
||||
image_id,
|
||||
new_image_meta,
|
||||
image_file,
|
||||
purge_props=False)
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
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)
|
||||
|
||||
def guest_power_action(self, action, *args, **kwargs):
|
||||
self._hypervisor.guest_power_action(action, self._instance.name,
|
||||
*args, **kwargs)
|
|
@ -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 os
|
||||
import pwd
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova.compute import power_state as compute_power_state
|
||||
from nova import conf
|
||||
from nova import exception
|
||||
from nova.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()
|
||||
|
||||
self._hypervisor_hostname = host_info['hypervisor_hostname']
|
||||
self._ipl_time = host_info['ipl_time']
|
||||
|
||||
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 exception.ZVMDriverException 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):
|
||||
return self._reqh.call('guest_list')
|
||||
|
||||
def get_host_uptime(self):
|
||||
return self._ipl_time
|
||||
|
||||
def guest_exists(self, instance):
|
||||
return instance.name 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 exception.NovaException as err:
|
||||
if err.kwargs['results']['overallRC'] == 404:
|
||||
# instance does not exist
|
||||
LOG.warning("Get power state of 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, 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):
|
||||
return self._reqh.call('guest_get_definition_info', name)
|
||||
|
||||
def guest_get_nic_vswitch_info(self, name):
|
||||
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_power_action(self, action, name, *args, **kwargs):
|
||||
self._reqh.call(action, name, *args, **kwargs)
|
||||
|
||||
def guest_get_console_output(self, name):
|
||||
return self._reqh.call('guest_get_console_output', name)
|
||||
|
||||
def image_query(self, imagename):
|
||||
return self._reqh.call('image_query', imagename=imagename)
|
||||
|
||||
def image_get_root_disk_size(self, imagename):
|
||||
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, self._rhost)
|
||||
|
||||
def image_export(self, image_id, dest_path):
|
||||
resp = self._reqh.call('image_export', image_id,
|
||||
dest_path, self._rhost)
|
||||
return resp
|
||||
|
||||
def image_delete(self, image_id):
|
||||
self._reqh.call('image_delete', image_id)
|
|
@ -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,143 +12,123 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from nova.api.metadata import base as instance_metadata
|
||||
from nova.compute import power_state
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.virt import configdrive
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
import six.moves.urllib.parse as urlparse
|
||||
from zvmconnector import connector
|
||||
|
||||
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.api.metadata import base as instance_metadata
|
||||
from nova import conf
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.virt import configdrive
|
||||
from nova.virt.zvm import configdrive as zvmconfigdrive
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = conf.CONF
|
||||
|
||||
CONF.import_opt('instances_path', 'nova.compute.manager')
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def mapping_power_stat(power_stat):
|
||||
"""Translate power state to OpenStack defined constants."""
|
||||
return const.ZVM_POWER_STAT.get(power_stat, power_state.NOSTATE)
|
||||
class ConnectorClient(object):
|
||||
"""Request handler to zVM cloud connector"""
|
||||
|
||||
def __init__(self, zcc_url, ca_file=None):
|
||||
_url = urlparse.urlparse(zcc_url)
|
||||
|
||||
class zVMConnectorRequestHandler(object):
|
||||
_ssl_enabled = False
|
||||
|
||||
def __init__(self):
|
||||
_url = urlparse.urlparse(CONF.zvm_cloud_connector_url)
|
||||
_ca_file = CONF.zvm_ca_file
|
||||
_token_path = CONF.zvm_token_path
|
||||
kwargs = {}
|
||||
# http or https
|
||||
if _url.scheme == 'https':
|
||||
kwargs['ssl_enabled'] = True
|
||||
_ssl_enabled = True
|
||||
|
||||
if _ssl_enabled and ca_file:
|
||||
self._conn = connector.ZVMConnector(_url.hostname, _url.port,
|
||||
ssl_enabled=_ssl_enabled,
|
||||
verify=ca_file)
|
||||
else:
|
||||
kwargs['ssl_enabled'] = False
|
||||
|
||||
# token file exist or not
|
||||
if _token_path:
|
||||
kwargs['token_path'] = _token_path
|
||||
|
||||
# CA file exist or not
|
||||
if kwargs['ssl_enabled'] and _ca_file:
|
||||
kwargs['verify'] = _ca_file
|
||||
else:
|
||||
kwargs['verify'] = False
|
||||
|
||||
self._conn = connector.ZVMConnector(_url.hostname, _url.port, **kwargs)
|
||||
self._conn = connector.ZVMConnector(_url.hostname, _url.port,
|
||||
ssl_enabled=_ssl_enabled,
|
||||
verify=False)
|
||||
|
||||
def call(self, func_name, *args, **kwargs):
|
||||
results = self._conn.send_request(func_name, *args, **kwargs)
|
||||
if results['overallRC'] == 0:
|
||||
return results['output']
|
||||
else:
|
||||
msg = ("SDK request %(api)s failed with parameters: %(args)s "
|
||||
"%(kwargs)s . Results: %(results)s" %
|
||||
{'api': func_name, 'args': str(args), 'kwargs': str(kwargs),
|
||||
'results': str(results)})
|
||||
LOG.debug(msg)
|
||||
raise exception.NovaException(message=msg, results=results)
|
||||
|
||||
if results['overallRC'] != 0:
|
||||
LOG.debug("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 exception.ZVMDriverException("zVM Cloud Connector request "
|
||||
"failed", results=results)
|
||||
|
||||
return results['output']
|
||||
|
||||
|
||||
class PathUtils(object):
|
||||
def _get_instances_path(self):
|
||||
return os.path.normpath(CONF.instances_path)
|
||||
|
||||
def get_instance_path(self, instance_uuid):
|
||||
instance_folder = os.path.join(self._get_instances_path(),
|
||||
instance_uuid)
|
||||
if not os.path.exists(instance_folder):
|
||||
LOG.debug("Creating the instance path %s", instance_folder)
|
||||
os.makedirs(instance_folder)
|
||||
return instance_folder
|
||||
|
||||
def clean_up_folder(self, fpath):
|
||||
if os.path.isdir(fpath):
|
||||
shutil.rmtree(fpath)
|
||||
|
||||
def clean_up_file(self, filepath):
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
def _get_instances_path():
|
||||
return os.path.normpath(CONF.instances_path)
|
||||
|
||||
|
||||
class VMUtils(object):
|
||||
def __init__(self):
|
||||
self._pathutils = PathUtils()
|
||||
def get_instance_path(instance_uuid):
|
||||
instance_folder = os.path.join(_get_instances_path(),
|
||||
instance_uuid)
|
||||
if not os.path.exists(instance_folder):
|
||||
LOG.debug("Creating the instance path %s", instance_folder)
|
||||
os.makedirs(instance_folder)
|
||||
return instance_folder
|
||||
|
||||
# Prepare and create configdrive for instance
|
||||
def generate_configdrive(self, context, instance, injected_files,
|
||||
admin_password):
|
||||
# Create network configuration files
|
||||
LOG.debug('Creating network configuration files '
|
||||
'for instance: %s', instance['name'], instance=instance)
|
||||
|
||||
instance_path = self._pathutils.get_instance_path(instance['uuid'])
|
||||
def _create_config_drive(context, instance_path, instance,
|
||||
injected_files, admin_password):
|
||||
if CONF.config_drive_format not in ['iso9660']:
|
||||
msg = (_("Invalid config drive format %s") %
|
||||
CONF.config_drive_format)
|
||||
LOG.debug(msg)
|
||||
raise exception.ConfigDriveUnsupportedFormat(
|
||||
format=CONF.config_drive_format)
|
||||
|
||||
transportfiles = None
|
||||
if configdrive.required_by(instance):
|
||||
transportfiles = self._create_config_drive(context, instance_path,
|
||||
instance,
|
||||
injected_files,
|
||||
admin_password)
|
||||
return transportfiles
|
||||
LOG.debug('Using config drive', instance=instance)
|
||||
|
||||
def _create_config_drive(self, context, instance_path, instance,
|
||||
injected_files, admin_password):
|
||||
if CONF.config_drive_format not in ['tgz', 'iso9660']:
|
||||
msg = (_("Invalid config drive format %s") %
|
||||
CONF.config_drive_format)
|
||||
LOG.debug(msg)
|
||||
raise exception.ConfigDriveUnsupportedFormat(
|
||||
format=CONF.config_drive_format)
|
||||
extra_md = {}
|
||||
if admin_password:
|
||||
extra_md['admin_pass'] = admin_password
|
||||
|
||||
LOG.debug('Using config drive', instance=instance)
|
||||
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
|
||||
|
||||
extra_md = {}
|
||||
if admin_password:
|
||||
extra_md['admin_pass'] = admin_password
|
||||
configdrive_tgz = os.path.join(instance_path, 'cfgdrive.tgz')
|
||||
|
||||
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
|
||||
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)
|
||||
|
||||
configdrive_tgz = os.path.join(instance_path, 'cfgdrive.tgz')
|
||||
return configdrive_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
|
||||
# Prepare and create configdrive for instance
|
||||
def generate_configdrive(context, instance, injected_files,
|
||||
admin_password):
|
||||
# Create network configuration files
|
||||
LOG.debug('Creating network configuration files '
|
||||
'for instance: %s', instance.name, instance=instance)
|
||||
|
||||
instance_path = get_instance_path(instance.uuid)
|
||||
|
||||
transportfiles = None
|
||||
if configdrive.required_by(instance):
|
||||
transportfiles = _create_config_drive(context, instance_path,
|
||||
instance,
|
||||
injected_files,
|
||||
admin_password)
|
||||
return transportfiles
|
||||
|
||||
|
||||
def clean_up_file(filepath):
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
|
|
Loading…
Reference in New Issue