switch to sdk
zvmsdk is a library that enables zvm cloud management. This patch switch from calling xcat client directly to importing zvmsdk to call zvmsdk API, to manage zvm host and virtual machines. Change-Id: I74406a618a4a8b2d80a905c56621c1acd46f1c08
This commit is contained in:
parent
9887de79eb
commit
6fea79d689
@ -14,7 +14,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Shim layer for nova_zvm.virt.zvm.driver.PowerVMDriver.
|
||||
"""Shim layer for nova_zvm.virt.zvm.driver.ZVMDriver.
|
||||
|
||||
Duplicate all public symbols. This is necessary for the constants as well as
|
||||
the classes - because instances of the classes need to be able to resolve
|
||||
@ -27,5 +27,4 @@ import nova_zvm.virt.zvm.driver as real_drv
|
||||
|
||||
LOG = real_drv.LOG
|
||||
CONF = real_drv.CONF
|
||||
ZVMInstance = real_drv.ZVMInstance
|
||||
ZVMDriver = real_drv.ZVMDriver
|
||||
|
0
nova_zvm/tests/unit/virt/__init__.py
Normal file
0
nova_zvm/tests/unit/virt/__init__.py
Normal file
57
nova_zvm/tests/unit/virt/zvm/test_configdrive.py
Normal file
57
nova_zvm/tests/unit/virt/zvm/test_configdrive.py
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright 2017 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
"""Test suite for ZVM configure drive."""
|
||||
|
||||
import os
|
||||
|
||||
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
|
||||
|
||||
|
||||
CONF = conf.CONF
|
||||
|
||||
|
||||
class FakeInstMeta(object):
|
||||
|
||||
def metadata_for_config_drive(self):
|
||||
return [('openstack', 'data1'), ('ec2', 'data2')]
|
||||
|
||||
|
||||
class ZVMConfigDriveTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(ZVMConfigDriveTestCase, self).setUp()
|
||||
self.flags(config_drive_format='iso9660',
|
||||
tempdir='/tmp/os')
|
||||
|
||||
self.inst_md = FakeInstMeta()
|
||||
|
||||
def test_create_configdrive_tgz(self):
|
||||
self._file_path = CONF.tempdir
|
||||
fileutils.ensure_tree(self._file_path)
|
||||
self._file_name = self._file_path + '/cfgdrive.tgz'
|
||||
|
||||
try:
|
||||
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)
|
405
nova_zvm/tests/unit/virt/zvm/test_driver.py
Normal file
405
nova_zvm/tests/unit/virt/zvm/test_driver.py
Normal file
@ -0,0 +1,405 @@
|
||||
# Copyright 2017 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
"""Test suite for ZVMDriver."""
|
||||
|
||||
import eventlet
|
||||
import mock
|
||||
|
||||
from nova.compute import power_state
|
||||
from nova import context
|
||||
from nova import exception as nova_exception
|
||||
from nova.image import api as image_api
|
||||
from nova.network import model as network_model
|
||||
from nova import objects
|
||||
from nova.objects import fields as obj_fields
|
||||
from nova import test
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests import uuidsentinel
|
||||
from nova.virt import fake
|
||||
from nova.virt import hardware
|
||||
from zvmsdk import api as sdkapi
|
||||
from zvmsdk import dist
|
||||
from zvmsdk import exception as sdkexception
|
||||
|
||||
from nova_zvm.virt.zvm import conf
|
||||
from nova_zvm.virt.zvm import const
|
||||
from nova_zvm.virt.zvm import driver
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
|
||||
|
||||
CONF = conf.CONF
|
||||
CONF.import_opt('host', 'nova.conf')
|
||||
CONF.import_opt('my_ip', 'nova.conf')
|
||||
|
||||
|
||||
class ZVMDriverTestCases(test.NoDBTestCase):
|
||||
"""Unit tests for z/VM driver methods."""
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, 'update_host_status')
|
||||
def setUp(self, update_host_status):
|
||||
super(ZVMDriverTestCases, self).setUp()
|
||||
|
||||
self.flags(host='fakehost',
|
||||
my_ip='10.1.1.10',
|
||||
instance_name_template = 'test%04x')
|
||||
update_host_status.return_value = [{
|
||||
'host': 'fakehost',
|
||||
'allowed_vm_type': 'zLinux',
|
||||
'vcpus': 10,
|
||||
'vcpus_used': 10,
|
||||
'cpu_info': {'Architecture': 's390x', 'CEC model': '2097'},
|
||||
'disk_total': 406105,
|
||||
'disk_used': 367263,
|
||||
'disk_available': 38842,
|
||||
'host_memory_total': 16384,
|
||||
'host_memory_free': 8096,
|
||||
'hypervisor_type': 'zvm',
|
||||
'hypervisor_version': '630',
|
||||
'hypervisor_hostname': 'fakenode',
|
||||
'supported_instances': [('s390x', 'zvm', 'hvm')],
|
||||
'ipl_time': 'IPL at 03/13/14 21:43:12 EDT',
|
||||
}]
|
||||
self.driver = driver.ZVMDriver(fake.FakeVirtAPI())
|
||||
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': 3,
|
||||
}
|
||||
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
|
||||
|
||||
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': 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_init_driver(self):
|
||||
self.assertIsInstance(self.driver._sdk_api, sdkapi.SDKAPI)
|
||||
self.assertIsInstance(self.driver._vmutils, zvmutils.VMUtils)
|
||||
self.assertIsInstance(self.driver._image_api, image_api.API)
|
||||
self.assertIsInstance(self.driver._pathutils, zvmutils.PathUtils)
|
||||
self.assertIsInstance(self.driver._imageutils, zvmutils.ImageUtils)
|
||||
self.assertIsInstance(self.driver._networkutils,
|
||||
zvmutils.NetworkUtils)
|
||||
self.assertIsInstance(self.driver._imageop_semaphore,
|
||||
eventlet.semaphore.Semaphore)
|
||||
self.assertEqual(self.driver._host_stats[0]['host'], "fakehost")
|
||||
self.assertEqual(self.driver._host_stats[0]['disk_available'], 38842)
|
||||
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'host_get_info')
|
||||
def test_update_host_status(self, host_get_info):
|
||||
host_get_info.return_value = {
|
||||
'vcpus': 10,
|
||||
'vcpus_used': 10,
|
||||
'cpu_info': {'Architecture': 's390x', 'CEC model': '2097'},
|
||||
'disk_total': 406105,
|
||||
'disk_used': 367263,
|
||||
'disk_available': 38842,
|
||||
'memory_mb': 876543,
|
||||
'memory_mb_used': 111111,
|
||||
'hypervisor_type': 'zvm',
|
||||
'hypervisor_version': '630',
|
||||
'hypervisor_hostname': 'fakenode',
|
||||
'ipl_time': 'IPL at 03/13/14 21:43:12 EDT',
|
||||
}
|
||||
info = self.driver.update_host_status()
|
||||
host_get_info.assert_called_with()
|
||||
self.assertEqual(info[0]['host'], CONF.host)
|
||||
self.assertEqual(info[0]['hypervisor_hostname'], 'fakenode')
|
||||
self.assertEqual(info[0]['host_memory_free'], 765432)
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, 'update_host_status')
|
||||
def test_get_available_resource(self, update_host_status):
|
||||
update_host_status.return_value = [{
|
||||
'host': CONF.host,
|
||||
'allowed_vm_type': const.ALLOWED_VM_TYPE,
|
||||
'vcpus': 10,
|
||||
'vcpus_used': 10,
|
||||
'cpu_info': {'Architecture': 's390x', 'CEC model': '2097'},
|
||||
'disk_total': 406105,
|
||||
'disk_used': 367263,
|
||||
'disk_available': 38842,
|
||||
'host_memory_total': 876543,
|
||||
'host_memory_free': 111111,
|
||||
'hypervisor_type': 'zvm',
|
||||
'hypervisor_version': '630',
|
||||
'hypervisor_hostname': 'fakenode',
|
||||
'supported_instances': [(const.ARCHITECTURE,
|
||||
const.HYPERVISOR_TYPE,
|
||||
obj_fields.VMMode.HVM)],
|
||||
'ipl_time': 'IPL at 03/13/14 21:43:12 EDT',
|
||||
}]
|
||||
res = self.driver.get_available_resource('fakenode')
|
||||
self.assertEqual(res['vcpus'], 10)
|
||||
self.assertEqual(res['memory_mb_used'], 765432)
|
||||
self.assertEqual(res['disk_available_least'], 38842)
|
||||
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_list')
|
||||
def test_list_instances(self, guest_list):
|
||||
self.driver.list_instances()
|
||||
guest_list.assert_called_once_with()
|
||||
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_get_info')
|
||||
@mock.patch.object(zvmutils, 'mapping_power_stat')
|
||||
def test_get_instance_info_paused(self, mapping_power_stat,
|
||||
guest_get_info):
|
||||
guest_get_info.return_value = {'power_state': 'on',
|
||||
'max_mem_kb': 2097152,
|
||||
'mem_kb': 44,
|
||||
'num_cpu': 2,
|
||||
'cpu_time_us': 796000,
|
||||
}
|
||||
mapping_power_stat.return_value = power_state.RUNNING
|
||||
fake_inst = fake_instance.fake_instance_obj(self._context,
|
||||
name='fake', power_state=power_state.PAUSED,
|
||||
memory_mb='1024',
|
||||
vcpus='4')
|
||||
inst_info = self.driver._get_instance_info(fake_inst)
|
||||
mapping_power_stat.assert_called_once_with('on')
|
||||
self.assertEqual(inst_info.state, power_state.PAUSED)
|
||||
self.assertEqual(inst_info.mem_kb, 44)
|
||||
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_get_info')
|
||||
@mock.patch.object(zvmutils, 'mapping_power_stat')
|
||||
def test_get_instance_info_off(self, mapping_power_stat, guest_get_info):
|
||||
guest_get_info.return_value = {'power_state': 'off',
|
||||
'max_mem_kb': 2097152,
|
||||
'mem_kb': 44,
|
||||
'num_cpu': 2,
|
||||
'cpu_time_us': 796000,
|
||||
}
|
||||
mapping_power_stat.return_value = power_state.SHUTDOWN
|
||||
fake_inst = fake_instance.fake_instance_obj(self._context,
|
||||
name='fake', power_state=power_state.PAUSED,
|
||||
memory_mb='1024',
|
||||
vcpus='4')
|
||||
inst_info = self.driver._get_instance_info(fake_inst)
|
||||
mapping_power_stat.assert_called_once_with('off')
|
||||
self.assertEqual(inst_info.state, power_state.SHUTDOWN)
|
||||
self.assertEqual(inst_info.mem_kb, 44)
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, '_get_instance_info')
|
||||
def test_get_info(self, _get_instance_info):
|
||||
_fake_inst_info = hardware.InstanceInfo(state=0x01, mem_kb=131072,
|
||||
num_cpu=4, cpu_time_ns=330528353,
|
||||
max_mem_kb=1048576)
|
||||
_get_instance_info.return_value = _fake_inst_info
|
||||
fake_inst = fake_instance.fake_instance_obj(self._context,
|
||||
name='fake', power_state=power_state.RUNNING,
|
||||
memory_mb='1024',
|
||||
vcpus='4')
|
||||
inst_info = self.driver.get_info(fake_inst)
|
||||
self.assertEqual(0x01, inst_info.state)
|
||||
self.assertEqual(131072, inst_info.mem_kb)
|
||||
self.assertEqual(4, inst_info.num_cpu)
|
||||
self.assertEqual(330528353, inst_info.cpu_time_ns)
|
||||
self.assertEqual(1048576, inst_info.max_mem_kb)
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, '_get_instance_info')
|
||||
def test_get_info_instance_not_exist_error(self, _get_instance_info):
|
||||
_get_instance_info.side_effect = sdkexception.ZVMVirtualMachineNotExist
|
||||
fake_inst = fake_instance.fake_instance_obj(self._context,
|
||||
name='fake', power_state=power_state.RUNNING,
|
||||
memory_mb='1024',
|
||||
vcpus='4')
|
||||
self.assertRaises(nova_exception.InstanceNotFound,
|
||||
self.driver.get_info,
|
||||
fake_inst)
|
||||
|
||||
def test_get_available_nodes(self):
|
||||
nodes = self.driver.get_available_nodes()
|
||||
self.assertEqual(nodes[0], 'fakenode')
|
||||
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_get_console_output')
|
||||
def test_get_console_output(self, gco):
|
||||
self.driver.get_console_output({}, self._instance)
|
||||
gco.assert_called_with('test0001')
|
||||
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_start')
|
||||
@mock.patch.object(driver.ZVMDriver, '_wait_network_ready')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_config_minidisks')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_deploy')
|
||||
@mock.patch.object(driver.ZVMDriver, '_setup_network')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_create')
|
||||
@mock.patch.object(zvmutils.ImageUtils, 'import_spawn_image')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'image_query')
|
||||
@mock.patch.object(zvmutils.VMUtils, 'generate_configdrive')
|
||||
@mock.patch.object(dist.LinuxDistManager, 'get_linux_dist')
|
||||
def _test_spawn(self, mock_linux_dist,
|
||||
generate_configdrive, image_query, import_spawn_image,
|
||||
guest_create, setup_network, guest_deploy,
|
||||
guest_config_minidisks, wait_network_ready,
|
||||
guest_start, image_query_result, eph_disks):
|
||||
generate_configdrive.return_value = '/tmp/fakecfg.tgz'
|
||||
image_query.side_effect = image_query_result
|
||||
|
||||
self._block_device_info['ephemerals'] = eph_disks
|
||||
root_disk = {'size': '3g', 'is_boot_disk': True}
|
||||
disk_list = [root_disk]
|
||||
eph_list = []
|
||||
for eph in eph_disks:
|
||||
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)
|
||||
|
||||
os_distro = self._image_meta.properties.os_distro
|
||||
|
||||
self.driver.spawn(self._context, self._instance, self._image_meta,
|
||||
injected_files=None,
|
||||
admin_password=None,
|
||||
network_info=self._network_info,
|
||||
block_device_info=self._block_device_info,
|
||||
flavor=self._flavor)
|
||||
generate_configdrive.assert_called_once_with(self._context,
|
||||
self._instance,
|
||||
os_distro,
|
||||
self._network_info,
|
||||
None, None)
|
||||
image_query.assert_called_with(self._image_meta.id)
|
||||
if not image_query_result[0]:
|
||||
import_spawn_image.assert_called_once_with(self._context,
|
||||
self._image_meta.id,
|
||||
os_distro)
|
||||
|
||||
guest_create.assert_called_once_with('test0001', 1, 1024, disk_list)
|
||||
setup_network.assert_called_once_with('test0001', self._network_info)
|
||||
guest_deploy.assert_called_once_with('test0001',
|
||||
'rhel7.2-s390x-netboot-0a0c576a_157f_42c8_bde5_2a254d8b77fc',
|
||||
'/tmp/fakecfg.tgz', zvmutils.get_host())
|
||||
|
||||
if eph_disks:
|
||||
guest_config_minidisks.assert_called_once_with('test0001',
|
||||
eph_list)
|
||||
|
||||
wait_network_ready.assert_called_once_with('test0001',
|
||||
self._instance)
|
||||
guest_start.assert_called_once_with('test0001')
|
||||
|
||||
def test_spawn_simple_path(self):
|
||||
self._test_spawn(image_query_result=(
|
||||
["rhel7.2-s390x-netboot-0a0c576a_157f_42c8_bde5_2a254d8b77fc"],
|
||||
["rhel7.2-s390x-netboot-0a0c576a_157f_42c8_bde5_2a254d8b77fc"]),
|
||||
eph_disks=[])
|
||||
|
||||
def test_spawn_image_not_in_backend(self):
|
||||
self._test_spawn(image_query_result=(
|
||||
[],
|
||||
["rhel7.2-s390x-netboot-0a0c576a_157f_42c8_bde5_2a254d8b77fc"]),
|
||||
eph_disks=[])
|
||||
|
||||
def test_spawn_with_ephemeral_disks(self):
|
||||
self._test_spawn(image_query_result=(
|
||||
["rhel7.2-s390x-netboot-0a0c576a_157f_42c8_bde5_2a254d8b77fc"],
|
||||
["rhel7.2-s390x-netboot-0a0c576a_157f_42c8_bde5_2a254d8b77fc"]),
|
||||
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}])
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, '_instance_exists')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_delete')
|
||||
def test_destroy(self, guest_delete, instance_exists):
|
||||
instance_exists.return_value = True
|
||||
self.driver.destroy(self._context, self._instance,
|
||||
network_info=self._network_info)
|
||||
guest_delete.assert_called_once_with(self._instance['name'])
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, '_instance_exists')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_stop')
|
||||
def test_power_off(self, guest_stop, instance_exists):
|
||||
instance_exists.return_value = True
|
||||
self.driver.power_off(self._instance)
|
||||
guest_stop.assert_called_once_with(self._instance['name'], 0, 0)
|
||||
|
||||
@mock.patch.object(driver.ZVMDriver, '_instance_exists')
|
||||
@mock.patch.object(sdkapi.SDKAPI, 'guest_start')
|
||||
def test_power_on(self, guest_start, instance_exists):
|
||||
instance_exists.return_value = True
|
||||
self.driver.power_on(self._context, self._instance,
|
||||
network_info=self._network_info)
|
||||
guest_start.assert_called_once_with(self._instance['name'])
|
File diff suppressed because it is too large
Load Diff
@ -14,226 +14,18 @@
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
zvm_opts = [
|
||||
cfg.StrOpt('zvm_xcat_server',
|
||||
default=None,
|
||||
cfg.StrOpt('zvm_image_tmp_path',
|
||||
default='/var/lib/nova/images',
|
||||
help="""
|
||||
Host name or IP address of xCAT management_node.
|
||||
The path at which images will be stored (snapshot, deploy, etc).
|
||||
|
||||
In order for the nova compute service to manage zVM, it needs to talk to an
|
||||
xcat management node. This property specifies the ip address or host name
|
||||
(which can be known from DNS of compute host) of the xcat MN server.
|
||||
The image used to deploy or image captured from instance need to be
|
||||
stored in local disk of compute node host. This configuration identifies
|
||||
the directory location.
|
||||
|
||||
Possible values:
|
||||
IP address(ipaddr) or host name(string)
|
||||
"""),
|
||||
cfg.StrOpt('zvm_xcat_username',
|
||||
default=None,
|
||||
help="""
|
||||
The xCAT user name for the REST API calls.
|
||||
|
||||
This is the user who will be authenticated by xCAT when the REST
|
||||
API call is made. Usually the user should have admin access to the resource
|
||||
being managed by xcat.
|
||||
|
||||
Related:
|
||||
zvm_xcat_password
|
||||
"""),
|
||||
cfg.StrOpt('zvm_xcat_password',
|
||||
default=None,
|
||||
secret=True,
|
||||
help="""
|
||||
The xCAT password for the REST API calls
|
||||
|
||||
This is the password which will be authenticated by xCAT when
|
||||
the REST API call is made.
|
||||
|
||||
Related:
|
||||
zvm_xcat_username
|
||||
"""),
|
||||
cfg.StrOpt('zvm_xcat_master',
|
||||
default=None,
|
||||
help="""
|
||||
The xCAT management node name from the xCAT database.
|
||||
|
||||
The nova zvm driver sometimes needs to interact with the xCAT management node
|
||||
itself, for example to query the free disk space on the node.
|
||||
In that case it needs this node name for the REST API call.
|
||||
|
||||
Possible values:
|
||||
A string specifying the name of the xCAT management node.
|
||||
This name should come from the xCAT node database.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_xcat_ca_file',
|
||||
default=None,
|
||||
help="""
|
||||
CA file for https connection to xCAT REST API.
|
||||
|
||||
When HTTPS protocol is used to communicate between z/VM driver and xCAT REST
|
||||
API, z/VM compute service needs to have a CA file which will be used to verify
|
||||
that it has the correct authorization to talk with xCAT.
|
||||
|
||||
Possible values:
|
||||
A CA file name and location in the host that is running compute service.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_diskpool',
|
||||
default=None,
|
||||
help="""
|
||||
zVM disk pool for ephemeral disks.
|
||||
|
||||
The volume group name from your directory manager on your z/VM system,
|
||||
which will be used for ephemeral disks for new instances.
|
||||
A dollar sign ($) is not allowed in the name.
|
||||
|
||||
Related:
|
||||
zvm_diskpool_type
|
||||
"""),
|
||||
cfg.StrOpt('zvm_diskpool_type',
|
||||
default='ECKD',
|
||||
help="""
|
||||
Disk type of the disk in the disk pool.
|
||||
|
||||
The disks in the disk pool must all be the same type (ECKD or FBA).
|
||||
|
||||
Related:
|
||||
zvm_diskpool
|
||||
|
||||
Possible values:
|
||||
A string, either ECKD or FBA.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_host',
|
||||
default=None,
|
||||
help="""
|
||||
z/VM host that is managed by the compute node.
|
||||
|
||||
This is the name of the hypervisor that is managed by the compute service.
|
||||
The admin should refer to the z/VM system configuration file
|
||||
SYSTEM_IDENTIFIER statement for the hypervisor name.
|
||||
|
||||
Possible values:
|
||||
A 1-8 character string, matching the z/VM system name this
|
||||
compute service is managing.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_xcat_group',
|
||||
default='all',
|
||||
help="""
|
||||
XCAT group for OpenStack.
|
||||
|
||||
There is a 'group' field in xCAT side and it can divide nodes into
|
||||
different group and let GUI to select a group of nodes to display.
|
||||
|
||||
Possible values:
|
||||
A string, only 'all' is valid value.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_scsi_pool',
|
||||
default='xcatzfcp',
|
||||
help='Default zfcp scsi disk pool'),
|
||||
cfg.BoolOpt('zvm_multiple_fcp',
|
||||
default=False,
|
||||
help='Does use multiple FCP for attaching a volume'),
|
||||
cfg.StrOpt('zvm_fcp_list',
|
||||
default=None,
|
||||
help='Configured fcp list can be used'),
|
||||
cfg.StrOpt('zvm_zhcp_fcp_list',
|
||||
default=None,
|
||||
help='Configured fcp list dedicated to hcp'),
|
||||
cfg.BoolOpt('zvm_config_drive_inject_password',
|
||||
default=False,
|
||||
help='Sets the admin password in the config drive'),
|
||||
cfg.StrOpt('zvm_vmrelocate_force',
|
||||
default=None,
|
||||
help="""
|
||||
Specify the z/VM Live Guest Relocation FORCE option.
|
||||
|
||||
Refer to the z/VM CP Commands and Utilities book for more information on
|
||||
the z/VM VMRELOCATE command and z/VM Live Guest Relocation,
|
||||
which backs the live migration feature in the zvm nova driver.
|
||||
|
||||
Possible values:
|
||||
A string which is one of (ARCHITECTURE, DOMAIN, NONE, or STORAGE)
|
||||
|
||||
ARCHITECTURE: attempt migration even though hardware architecture
|
||||
facilities or z/VM CP features are not available on the destination
|
||||
system.
|
||||
|
||||
DOMAIN: attempt migration even though the destination system is
|
||||
not in the z/VM guest's relocation domain.
|
||||
|
||||
STORAGE: attempt migration even if there appears to be insufficient
|
||||
storage resources on the destination system.
|
||||
|
||||
NONE: No VMRELOCATE FORCE will be used. Live migration will fail if
|
||||
architecture, domain or storage warnings or errors are encountered.
|
||||
|
||||
Related values:
|
||||
zvm_vmrelocate_immediate
|
||||
zvm_vmrelocate_max_total
|
||||
zvm_vmrelocate_max_quiesce
|
||||
"""),
|
||||
cfg.StrOpt('zvm_vmrelocate_immediate',
|
||||
default='no',
|
||||
help="""
|
||||
Specify the option of live migration timing.
|
||||
|
||||
Refer to zVM manual for more info on zVM live migration.
|
||||
|
||||
Possible values:
|
||||
A string which is one of (YES, NO)
|
||||
|
||||
YES: VMRELOCATE command will do one early pass through virtual machine
|
||||
storage and then go directly to the quiesce stage.
|
||||
NO: specifies immediate processing.
|
||||
|
||||
Related values:
|
||||
zvm_vmrelocate_force
|
||||
zvm_vmrelocate_max_total
|
||||
zvm_vmrelocate_max_quiesce
|
||||
"""),
|
||||
cfg.StrOpt('zvm_vmrelocate_max_total',
|
||||
default='nolimit',
|
||||
help="""
|
||||
Maximum time to wait, in seconds, for live migration to complete.
|
||||
If this time is exceeded, z/VM will cancel the migration and leave
|
||||
the z/VM guest running on the source system.
|
||||
|
||||
Refer to the z/VM CP Commands and Utilities book for more information
|
||||
on the z/VM VMRELOCATE command and z/VM Live Guest Relocation,
|
||||
which backs the live migration feature in the zvm nova driver.
|
||||
|
||||
Possible values:
|
||||
A positive integer indicating the time in seconds to wait for
|
||||
the migration to complete, or "nolimit" to indicate that there is no
|
||||
time limit on the total amount of time the system should allow for
|
||||
this live migration
|
||||
|
||||
Related values:
|
||||
zvm_vmrelocate_force
|
||||
zvm_vmrelocate_immediate
|
||||
zvm_vmrelocate_max_quiesce
|
||||
"""),
|
||||
cfg.StrOpt('zvm_vmrelocate_max_quiesce',
|
||||
default='nolimit',
|
||||
help="""
|
||||
Maximum time to wait, in seconds, for the quiesce time of a live migration
|
||||
to complete. If this time is exceeded, z/VM will cancel the migration and
|
||||
leave the z/VM guest running on the source system.
|
||||
The quiesce time is the time during which the Linux guest will be unresponsive
|
||||
while its final state is transferred.
|
||||
|
||||
Refer to the z/VM CP Commands and Utilities book for more information on the
|
||||
z/VM VMRELOCATE command and z/VM Live Guest Relocation,
|
||||
which backs the live migration feature in the zvm nova driver.
|
||||
|
||||
Possible values:
|
||||
A positive integer indicating the time in seconds to wait for the
|
||||
quiesce time to complete, or "nolimit" to indicate that there is no time
|
||||
limit on the quiesce time the system should allow for this live migration.
|
||||
|
||||
Related values:
|
||||
zvm_vmrelocate_force
|
||||
zvm_vmrelocate_immediate
|
||||
zvm_vmrelocate_max_total
|
||||
A path in host that running compute service.
|
||||
"""),
|
||||
cfg.IntOpt('zvm_reachable_timeout',
|
||||
default=300,
|
||||
@ -252,239 +44,8 @@ Possible Values:
|
||||
but it will vary depending on instance and system load.
|
||||
A value of 0 is used for debug. In this case the underlying z/VM guest
|
||||
will not be deleted when the instance is marked in ERROR state.
|
||||
"""),
|
||||
cfg.IntOpt('zvm_xcat_connection_timeout',
|
||||
default=3600,
|
||||
help="""
|
||||
Timeout (seconds) for an xCAT HTTP request.
|
||||
|
||||
The z/VM driver communicates with xCAT via REST APIs. This value states the
|
||||
maximum amount of time the z/VM driver will wait before timing out a REST API
|
||||
call. Some actions, like copying an image to the OpenStack compute node,
|
||||
may take a long time, so set this to the maximum time these long
|
||||
running tasks may take.
|
||||
|
||||
Possible Values:
|
||||
Any positive integer.
|
||||
Recommended to be larger than 3600 (1 hour), depending on the size of
|
||||
your images.
|
||||
"""),
|
||||
cfg.IntOpt('zvm_console_log_size',
|
||||
default=100,
|
||||
help="""
|
||||
The maximum allowed console log size, in kilobytes.
|
||||
|
||||
Console logs must be transferred to OpenStack from xCAT, this controls how
|
||||
large each file can be. A smaller size may mean more calls will be needed
|
||||
to transfer large consoles, which may not be desirable for performance reasons.
|
||||
"""),
|
||||
]
|
||||
|
||||
zvm_user_opts = [
|
||||
cfg.StrOpt('zvm_user_profile',
|
||||
default='osdflt',
|
||||
help="""
|
||||
PROFILE name to use when creating a z/VM guest.
|
||||
|
||||
When nova deploys an instance on z/VM, it can include some common statements
|
||||
from a PROFILE definition.
|
||||
This PROFILE must already be included in your z/VM user directory.
|
||||
|
||||
Possible values:
|
||||
An 8 character name of a PROFILE that is already defined in the z/VM
|
||||
user directory.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_user_default_password',
|
||||
default='dfltpass',
|
||||
deprecated_for_removal=True,
|
||||
deprecated_reason="""
|
||||
zvm_user_default_password is not secure and has been replaced by
|
||||
zvm_default_admin_userid from Pike release and will be removed in
|
||||
the near future.
|
||||
Already running instances are not affected.
|
||||
""",
|
||||
secret=True,
|
||||
help="""
|
||||
Default password for a new created z/VM user.
|
||||
|
||||
This is the password for any z/VM user IDs created when OpenStack deploys new
|
||||
virtual servers (also called nova instances), defined in the USER directory
|
||||
statement.
|
||||
|
||||
Possible values:
|
||||
A 1-8 character string.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_default_admin_userid',
|
||||
default='',
|
||||
help="""
|
||||
Default LOGONBY userid(s) for the cloud.
|
||||
|
||||
This is a set of z/VM userid(s) which are allowed to logon using the LOGONBY
|
||||
keyword to the guests created by the z/VM openstack solution, compatible with
|
||||
the LBYONLY keyword of the user directory statement. This value is only used
|
||||
when a guest is created. If you change this value, existing guests' directory
|
||||
entries are not automatically updated with the new value.
|
||||
When an ESM is installed, this parameter only governs when the ESM
|
||||
defers to CP's processing.
|
||||
|
||||
Usage note:
|
||||
The default is an empty string (''). When the string is empty, you can't
|
||||
log on to your instances using the 3270 protocol; When a
|
||||
non-empty string is provided, blank chars will be used as delimiter,
|
||||
you can use LOGONBY xxx command to log on the
|
||||
OpenStack created guest using the corresponding admin userid's password.
|
||||
|
||||
For example, when you set this value to 'oper1 oper2 oper3 jones', it means
|
||||
you can use any one of 'oper1', 'oper2', 'oper3', 'jones' as an admin user.
|
||||
|
||||
see the z/VM CP Planning and Administration for additional information.
|
||||
|
||||
Possible values:
|
||||
A maximum of 8 blank-delimited strings. Each non-blank string must be a
|
||||
valid z/VM userid.
|
||||
e.g '' is a valid value.
|
||||
'oper1 oper2' is a valid value.
|
||||
'o1 o2 o3 o4 o5 o6 o7 o8 o9' is NOT a valid value.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_user_default_privilege',
|
||||
default='G',
|
||||
help="""
|
||||
Privilege class for the z/VM guest.
|
||||
|
||||
The privilege class controls which z/VM commands the guest can issue.
|
||||
Classes A-G are defined by default, or the administrator can define
|
||||
their own using characters A-Z, 1-6.
|
||||
|
||||
Possible value:
|
||||
Currently, 'G' is the recommended value.
|
||||
Consult with your z/VM administrator for more information.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_user_root_vdev',
|
||||
default='0100',
|
||||
help="""
|
||||
Virtual device number for root disk.
|
||||
|
||||
When nova deploys an instance, it creates a root disk and several ephemeral
|
||||
or persistent disks. This value is the virtual device number of the root
|
||||
disk. If the root disk is a cinder volume instead, this value does not apply.
|
||||
|
||||
Possible values:
|
||||
An integer value in hex format, between 0 and 65536 (x'FFFF').
|
||||
It should not conflict with other device numbers in the z/VM guest's
|
||||
configuration, for example device numbers of the NICs or ephemeral or
|
||||
persistent disks.
|
||||
|
||||
Sample root disk in user directory:
|
||||
MDISK 0100 <disktype> <start> <end> <volumelabel> <readwrite>
|
||||
|
||||
Related values:
|
||||
zvm_default_nic_vdev
|
||||
zvm_user_adde_vdev
|
||||
"""),
|
||||
cfg.StrOpt('zvm_default_nic_vdev',
|
||||
default='1000',
|
||||
help="""
|
||||
Virtual device number for default NIC address.
|
||||
|
||||
This value is the first NIC virtual device number,
|
||||
each NIC needs 3 numbers for control/read/write, so by default
|
||||
the first NIC's address is 1000, the second one is 1003 etc.
|
||||
|
||||
Possible values:
|
||||
An integer value in hex format, between 0 and 65536 (x'FFFF').
|
||||
It should not conflict with other device numbers in the z/VM guest's
|
||||
configuration, for example device numbers of the root or ephemeral or
|
||||
persistent disks.
|
||||
|
||||
Sample NIC definitions in the z/VM user directory:
|
||||
NICDEF 1000 TYPE QDIO LAN SYSTEM <vswitch1> MACID <macid1>
|
||||
NICDEF 1003 TYPE QDIO LAN SYSTEM <vswitch2> MACID <macid2>
|
||||
|
||||
Related values:
|
||||
zvm_user_root_vdev
|
||||
zvm_user_adde_vdev
|
||||
"""),
|
||||
cfg.StrOpt('zvm_user_adde_vdev',
|
||||
default='0101',
|
||||
help="""
|
||||
Virtual device number for additional ephemeral disk.
|
||||
|
||||
Nova allows a user to deploy an instance with one or more ephemeral disks.
|
||||
This value is the virtual device number of the first ephemeral disk.
|
||||
Other ephemeral disks will take subsequent numbers.
|
||||
|
||||
Possible values:
|
||||
An integer value in hex format, between 0 and 65536 (x'FFFF').
|
||||
It should not conflict with other device numbers in the z/VM guest's
|
||||
configuration, for example device numbers of the root disk or NICs.
|
||||
|
||||
Sample ephemeral disk definitions in the z/VM user directory:
|
||||
MDISK 0101 <disktype1> <start1> <end1> <volumelabel1> <readwrite1>
|
||||
MDISK 0102 <disktype2> <start2> <end2> <volumelabel2> <readwrite2>
|
||||
|
||||
Related values:
|
||||
zvm_user_root_vdev
|
||||
vm_default_nic_vdev
|
||||
"""),
|
||||
]
|
||||
|
||||
zvm_image_opts = [
|
||||
cfg.StrOpt('zvm_image_tmp_path',
|
||||
default='/var/lib/nova/images',
|
||||
help="""
|
||||
The path at which images will be stored (snapshot, deploy, etc).
|
||||
|
||||
The image used to deploy or image captured from instance need to be
|
||||
stored in local disk of compute node host. This configuration identifies
|
||||
the directory location.
|
||||
|
||||
Possible values:
|
||||
A path in host that running compute service.
|
||||
"""),
|
||||
cfg.StrOpt('zvm_default_ephemeral_mntdir',
|
||||
default='/mnt/ephemeral',
|
||||
help='The path to which the ephemeral disk be mounted'),
|
||||
cfg.StrOpt('zvm_image_default_password',
|
||||
default='rootpass',
|
||||
secret=True,
|
||||
help='Default os root password for a new created vm'),
|
||||
cfg.IntOpt('xcat_image_clean_period',
|
||||
default=30,
|
||||
help="""
|
||||
Number of days an unused xCAT image will be retained before it is purged.
|
||||
|
||||
Copies of Glance images are kept in the xCAT MN to make deploys faster.
|
||||
Unused images are purged to reclaim disk space. If an image has been purged
|
||||
from xCAT, the next deploy will take slightly longer as it must be copied
|
||||
from OpenStack into xCAT.
|
||||
|
||||
Possible values:
|
||||
Any positive integer, recommended to be at least 30 (1 month).
|
||||
"""),
|
||||
cfg.IntOpt('xcat_free_space_threshold',
|
||||
default=50,
|
||||
help='The threshold for xCAT free space, if snapshot or spawn '
|
||||
'check xCAT free space not enough for its image '
|
||||
'operations, it will prune image to meet the threshold'),
|
||||
cfg.StrOpt('zvm_image_compression_level',
|
||||
default=None,
|
||||
help="""
|
||||
The level of gzip compression used when capturing disk.
|
||||
|
||||
A snapshotted image will consume disk space on xCAT MN host and the OpenStack
|
||||
compute host. To save disk space the image should be compressed.
|
||||
The zvm driver uses gzip to compress the image. gzip has a set of different
|
||||
levels depending on the speed and quality of compression.
|
||||
For more information, please refer to the -N option of the gzip command.
|
||||
|
||||
Possible values:
|
||||
An integer between 0 and 9, where 0 is no compression and 9 is the best,
|
||||
but slowest compression. A value of "None" will result in the default
|
||||
compression level, which is currently '6' for gzip.
|
||||
"""),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(zvm_opts)
|
||||
CONF.register_opts(zvm_user_opts)
|
||||
CONF.register_opts(zvm_image_opts)
|
||||
|
@ -12,7 +12,9 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
import six
|
||||
import tarfile
|
||||
|
||||
from nova import exception
|
||||
@ -21,8 +23,6 @@ from nova.virt import configdrive
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
@ -43,7 +43,7 @@ class ZVMConfigDriveBuilder(configdrive.ConfigDriveBuilder):
|
||||
:raises ProcessExecuteError if a helper process has failed.
|
||||
|
||||
"""
|
||||
if CONF.config_drive_format in ['tgz', 'iso9660']:
|
||||
if CONF.config_drive_format in ['iso9660']:
|
||||
self._make_tgz(path)
|
||||
else:
|
||||
raise exception.ConfigDriveUnknownFormat(
|
||||
@ -64,7 +64,7 @@ class ZVMConfigDriveBuilder(configdrive.ConfigDriveBuilder):
|
||||
try:
|
||||
os.chdir(olddir)
|
||||
except Exception as e:
|
||||
emsg = zvmutils.format_exception_msg(e)
|
||||
emsg = six.text_type(e)
|
||||
LOG.debug('exception in _make_tgz %s', emsg)
|
||||
|
||||
tar.close()
|
||||
|
@ -19,90 +19,9 @@ from nova.compute import power_state
|
||||
HYPERVISOR_TYPE = 'zvm'
|
||||
ARCHITECTURE = 's390x'
|
||||
ALLOWED_VM_TYPE = 'zLinux'
|
||||
XCAT_MGT = 'zvm'
|
||||
|
||||
XCAT_RINV_HOST_KEYWORDS = {
|
||||
"zvm_host": "z/VM Host:",
|
||||
"zhcp": "zHCP:",
|
||||
"cec_vendor": "CEC Vendor:",
|
||||
"cec_model": "CEC Model:",
|
||||
"hypervisor_os": "Hypervisor OS:",
|
||||
"hypervisor_name": "Hypervisor Name:",
|
||||
"architecture": "Architecture:",
|
||||
"lpar_cpu_total": "LPAR CPU Total:",
|
||||
"lpar_cpu_used": "LPAR CPU Used:",
|
||||
"lpar_memory_total": "LPAR Memory Total:",
|
||||
"lpar_memory_used": "LPAR Memory Used:",
|
||||
"lpar_memory_offline": "LPAR Memory Offline:",
|
||||
"ipl_time": "IPL Time:",
|
||||
}
|
||||
|
||||
XCAT_DISKPOOL_KEYWORDS = {
|
||||
"disk_total": "Total:",
|
||||
"disk_used": "Used:",
|
||||
"disk_available": "Free:",
|
||||
}
|
||||
|
||||
XCAT_RESPONSE_KEYS = ('info', 'data', 'node', 'errorcode', 'error')
|
||||
DEFAULT_EPH_DISK_FMT = 'ext3'
|
||||
|
||||
ZVM_POWER_STAT = {
|
||||
'on': power_state.RUNNING,
|
||||
'off': power_state.SHUTDOWN,
|
||||
}
|
||||
|
||||
ZVM_DEFAULT_ROOT_DISK = "dasda"
|
||||
ZVM_DEFAULT_SECOND_DISK = "dasdb"
|
||||
ZVM_DEFAULT_ROOT_VOLUME = "sda"
|
||||
ZVM_DEFAULT_SECOND_VOLUME = "sdb"
|
||||
ZVM_DEFAULT_THIRD_VOLUME = "sdc"
|
||||
ZVM_DEFAULT_LAST_VOLUME = "sdz"
|
||||
|
||||
DEFAULT_EPH_DISK_FMT = "ext3"
|
||||
DISK_FUNC_NAME = "setupDisk"
|
||||
|
||||
ZVM_DEFAULT_FCP_ID = 'auto'
|
||||
|
||||
ZVM_IMAGE_SIZE_MAX = 10
|
||||
|
||||
# This is the version that our plugin can work ,if xcat version
|
||||
# is lower than this version, we will warn user and prevent
|
||||
# compute service to start up.
|
||||
# Will bump this version time to time for each release.
|
||||
XCAT_MINIMUM_VERSION = '2.8.3.16'
|
||||
|
||||
|
||||
# It means we introduced 'version' concept at 2.8.3.7
|
||||
# later on, any new features especially backward incompatible
|
||||
# change need a new version such as
|
||||
# Support_xxxx = 'x.x.x.x', then we will compare whether
|
||||
# The XCAT we are using has higher or lower version than x.x.x.x
|
||||
# and do different things according to the version
|
||||
# we might INFO in log if version is lower than XCAT_INIT_VERSION
|
||||
XCAT_INIT_VERSION = '2.8.3.7'
|
||||
|
||||
# From 2.8.3.7 version, openstack will only send 'nozip' format
|
||||
# to xcat, so before that, a tgz format will be send and processed
|
||||
# while >= this version, a nozip flag along with tar format is sent.
|
||||
# xcat was bumped to this version at 2015.08.06, so version lower than
|
||||
# it should use zip format instead.
|
||||
XCAT_BUNDLE_USE_NOZIP = '2.8.3.7'
|
||||
|
||||
XCAT_SUPPORT_CHVM_SMCLI_VERSION = '2.8.3.8'
|
||||
|
||||
# e0a7bc3502e8635bf7a2954220a6406f563f3a1f added this
|
||||
# support add --ipl as param to mkvm call
|
||||
XCAT_MKVM_SUPPORT_IPL = '2.8.3.11'
|
||||
|
||||
# I1a6ff024609e50f3a20a4bac2d86f94f7152e3cf xcat change added this
|
||||
# rinv now can use 'cpumempowerstat' option
|
||||
XCAT_RINV_SUPPORT_CPUMEMPOWERSTAT = '2.8.3.13'
|
||||
|
||||
# xCAT version that supports collection of diagnostics for failed deployments
|
||||
XCAT_SUPPORT_COLLECT_DIAGNOSTICS_DEPLOYFAILED = '2.8.3.16'
|
||||
|
||||
# Reasons for collecting diagnostics.
|
||||
# xCAT may adjust diagnostic contents based on the reason passed in.
|
||||
DIAGNOSTICS_RSN_DEPLOYMENT_TIMEOUT = 'OpenStackDeploymentTimeout'
|
||||
|
||||
# add IUCV support to replace ssh
|
||||
XCAT_SUPPORT_IUCV = '2.8.3.15'
|
||||
|
@ -1,678 +0,0 @@
|
||||
# Copyright 2015 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
|
||||
from nova.i18n import _
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from nova_zvm.virt.zvm import exception
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class LinuxDist(object):
|
||||
"""Linux distribution base class
|
||||
|
||||
Due to we need to interact with linux dist and inject different files
|
||||
according to the dist version. Currently only RHEL and SLES are supported
|
||||
"""
|
||||
|
||||
def create_network_configuration_files(self, file_path, network_info,
|
||||
base_vdev):
|
||||
"""Generate network configuration files to instance."""
|
||||
device_num = 0
|
||||
cfg_files = []
|
||||
cmd_strings = ''
|
||||
udev_cfg_str = ''
|
||||
dns_cfg_str = ''
|
||||
route_cfg_str = ''
|
||||
cmd_str = None
|
||||
file_path = self._get_network_file_path()
|
||||
file_name_route = file_path + 'routes'
|
||||
|
||||
file_name_dns = self._get_dns_filename()
|
||||
for vif in network_info:
|
||||
file_name = self._get_device_filename(device_num)
|
||||
network = vif['network']
|
||||
(cfg_str, cmd_str, dns_str,
|
||||
route_str) = self._generate_network_configuration(network,
|
||||
base_vdev, device_num)
|
||||
target_net_conf_file_name = file_path + file_name
|
||||
cfg_str_for_log = cfg_str.replace('\n', ' ')
|
||||
LOG.debug('Network configure file[%(file)s] content is: %(cfg)s',
|
||||
{'file': target_net_conf_file_name,
|
||||
'cfg': cfg_str_for_log})
|
||||
cfg_files.append((target_net_conf_file_name, cfg_str))
|
||||
udev_cfg_str += self._get_udev_configuration(device_num,
|
||||
'0.0.' + str(base_vdev).zfill(4))
|
||||
self._append_udev_rules_file(cfg_files, base_vdev)
|
||||
if cmd_str is not None:
|
||||
cmd_strings += cmd_str
|
||||
if len(dns_str) > 0:
|
||||
dns_cfg_str += dns_str
|
||||
if len(route_str) > 0:
|
||||
route_cfg_str += route_str
|
||||
base_vdev = str(hex(int(base_vdev, 16) + 3))[2:]
|
||||
device_num += 1
|
||||
|
||||
if len(dns_cfg_str) > 0:
|
||||
cfg_files.append((file_name_dns, dns_cfg_str))
|
||||
self._append_udev_info(cfg_files, file_name_route, route_cfg_str,
|
||||
udev_cfg_str)
|
||||
return cfg_files, cmd_strings
|
||||
|
||||
def _generate_network_configuration(self, network, vdev, device_num):
|
||||
ip_v4 = dns_str = ''
|
||||
|
||||
subnets_v4 = [s for s in network['subnets'] if s['version'] == 4]
|
||||
|
||||
if len(subnets_v4[0]['ips']) > 0:
|
||||
ip_v4 = subnets_v4[0]['ips'][0]['address']
|
||||
if len(subnets_v4[0]['dns']) > 0:
|
||||
for dns in subnets_v4[0]['dns']:
|
||||
dns_str += 'nameserver ' + dns['address'] + '\n'
|
||||
netmask_v4 = str(subnets_v4[0].as_netaddr().netmask)
|
||||
gateway_v4 = subnets_v4[0]['gateway']['address'] or ''
|
||||
broadcast_v4 = str(subnets_v4[0].as_netaddr().broadcast)
|
||||
device = self._get_device_name(device_num)
|
||||
address_read = str(vdev).zfill(4)
|
||||
address_write = str(hex(int(vdev, 16) + 1))[2:].zfill(4)
|
||||
address_data = str(hex(int(vdev, 16) + 2))[2:].zfill(4)
|
||||
subchannels = '0.0.%s' % address_read.lower()
|
||||
subchannels += ',0.0.%s' % address_write.lower()
|
||||
subchannels += ',0.0.%s' % address_data.lower()
|
||||
|
||||
cfg_str = self._get_cfg_str(device, broadcast_v4, gateway_v4,
|
||||
ip_v4, netmask_v4, address_read,
|
||||
subchannels)
|
||||
cmd_str = self._get_cmd_str(address_read, address_write,
|
||||
address_data)
|
||||
route_str = self._get_route_str(gateway_v4)
|
||||
|
||||
return cfg_str, cmd_str, dns_str, route_str
|
||||
|
||||
def assemble_zfcp_srcdev(self, fcp, wwpn, lun):
|
||||
path = '/dev/disk/by-path/ccw-0.0.%(fcp)s-zfcp-0x%(wwpn)s:0x%(lun)s'
|
||||
srcdev = path % {'fcp': fcp, 'wwpn': wwpn, 'lun': lun}
|
||||
return srcdev
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_network_file_path(self):
|
||||
"""Get network file configuration path."""
|
||||
pass
|
||||
|
||||
def get_change_passwd_command(self, admin_password):
|
||||
"""construct change password command
|
||||
|
||||
:admin_password: the password to be changed to
|
||||
"""
|
||||
return "echo 'root:%s' | chpasswd" % admin_password
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4,
|
||||
netmask_v4, address_read, subchannels):
|
||||
"""construct configuration file of network device."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_device_filename(self, device_num):
|
||||
"""construct the name of a network device file."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_route_str(self, gateway_v4):
|
||||
"""construct a router string."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_cmd_str(self, address_read, address_write, address_data):
|
||||
"""construct network startup command string."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_dns_filename(self):
|
||||
"""construct the name of dns file."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_znetconfig_contents(self):
|
||||
"""construct znetconfig file will be called during first boot."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_device_name(self, device_num):
|
||||
"""construct the name of a network device."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_udev_configuration(self, device, dev_channel):
|
||||
"""construct udev configuration info."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _get_udev_rules(self, channel_read, channel_write, channel_data):
|
||||
"""construct udev rules info."""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str,
|
||||
udev_cfg_str):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def _append_udev_rules_file(self, cfg_files, base_vdev):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_scp_string(self, root, fcp, wwpn, lun):
|
||||
"""construct scp_data string for ipl parameter"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun):
|
||||
"""construct the lines composing the script to generate
|
||||
the /etc/zipl.conf file
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class rhel(LinuxDist):
|
||||
def _get_network_file_path(self):
|
||||
return '/etc/sysconfig/network-scripts/'
|
||||
|
||||
def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4,
|
||||
netmask_v4, address_read, subchannels):
|
||||
cfg_str = 'DEVICE=\"' + device + '\"\n'
|
||||
cfg_str += 'BOOTPROTO=\"static\"\n'
|
||||
cfg_str += 'BROADCAST=\"' + broadcast_v4 + '\"\n'
|
||||
cfg_str += 'GATEWAY=\"' + gateway_v4 + '\"\n'
|
||||
cfg_str += 'IPADDR=\"' + ip_v4 + '\"\n'
|
||||
cfg_str += 'NETMASK=\"' + netmask_v4 + '\"\n'
|
||||
cfg_str += 'NETTYPE=\"qeth\"\n'
|
||||
cfg_str += 'ONBOOT=\"yes\"\n'
|
||||
cfg_str += 'PORTNAME=\"PORT' + address_read + '\"\n'
|
||||
cfg_str += 'OPTIONS=\"layer2=1\"\n'
|
||||
cfg_str += 'SUBCHANNELS=\"' + subchannels + '\"\n'
|
||||
return cfg_str
|
||||
|
||||
def _get_route_str(self, gateway_v4):
|
||||
return ''
|
||||
|
||||
def _get_cmd_str(self, address_read, address_write, address_data):
|
||||
return ''
|
||||
|
||||
def _get_dns_filename(self):
|
||||
return '/etc/resolv.conf'
|
||||
|
||||
def _get_device_name(self, device_num):
|
||||
return 'eth' + str(device_num)
|
||||
|
||||
def _get_udev_configuration(self, device, dev_channel):
|
||||
return ''
|
||||
|
||||
def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str,
|
||||
udev_cfg_str):
|
||||
pass
|
||||
|
||||
def _get_udev_rules(self, channel_read, channel_write, channel_data):
|
||||
"""construct udev rules info."""
|
||||
return ''
|
||||
|
||||
def _append_udev_rules_file(self, cfg_files, base_vdev):
|
||||
pass
|
||||
|
||||
|
||||
class rhel6(rhel):
|
||||
def get_znetconfig_contents(self):
|
||||
return '\n'.join(('cio_ignore -R',
|
||||
'znetconf -R -n',
|
||||
'udevadm trigger',
|
||||
'udevadm settle',
|
||||
'sleep 2',
|
||||
'znetconf -A',
|
||||
'service network restart',
|
||||
'cio_ignore -u'))
|
||||
|
||||
def _get_device_filename(self, device_num):
|
||||
return 'ifcfg-eth' + str(device_num)
|
||||
|
||||
def _get_device_name(self, device_num):
|
||||
return 'eth' + str(device_num)
|
||||
|
||||
def get_scp_string(self, root, fcp, wwpn, lun):
|
||||
return ("=root=%(root)s selinux=0 zfcp.allow_lun_scan=0 "
|
||||
"rd_ZFCP=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % {
|
||||
'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}
|
||||
|
||||
def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun):
|
||||
return ['#!/bin/bash\n',
|
||||
('echo -e "[defaultboot]\\n'
|
||||
'timeout=5\\n'
|
||||
'default=boot-from-volume\\n'
|
||||
'target=/boot/\\n'
|
||||
'[boot-from-volume]\\n'
|
||||
'image=%(image)s\\n'
|
||||
'ramdisk=%(ramdisk)s\\n'
|
||||
'parameters=\\"root=%(root)s '
|
||||
'rd_ZFCP=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s '
|
||||
'zfcp.allow_lun_scan=0 selinux=0\\""'
|
||||
'>/etc/zipl_volume.conf\n'
|
||||
'zipl -c /etc/zipl_volume.conf')
|
||||
% {'image': image, 'ramdisk': ramdisk, 'root': root,
|
||||
'fcp': fcp, 'wwpn': wwpn, 'lun': lun}]
|
||||
|
||||
|
||||
class rhel7(rhel):
|
||||
def get_znetconfig_contents(self):
|
||||
return '\n'.join(('cio_ignore -R',
|
||||
'znetconf -R -n',
|
||||
'udevadm trigger',
|
||||
'udevadm settle',
|
||||
'sleep 2',
|
||||
'znetconf -A',
|
||||
'cio_ignore -u'))
|
||||
|
||||
def _get_device_filename(self, device_num):
|
||||
# Construct a device like ifcfg-enccw0.0.1000, ifcfg-enccw0.0.1003
|
||||
base = int(CONF.zvm_default_nic_vdev, 16)
|
||||
device = str(hex(base + device_num * 3))[2:]
|
||||
return 'ifcfg-enccw0.0.' + str(device).zfill(4)
|
||||
|
||||
def _get_device_name(self, device_num):
|
||||
# Construct a device like enccw0.0.1000, enccw0.0.1003
|
||||
base = int(CONF.zvm_default_nic_vdev, 16)
|
||||
device = str(hex(base + device_num * 3))[2:]
|
||||
return 'enccw0.0.' + str(device).zfill(4)
|
||||
|
||||
def get_scp_string(self, root, fcp, wwpn, lun):
|
||||
return ("=root=%(root)s selinux=0 zfcp.allow_lun_scan=0 "
|
||||
"rd.zfcp=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % {
|
||||
'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}
|
||||
|
||||
def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun):
|
||||
return ['#!/bin/bash\n',
|
||||
('echo -e "[defaultboot]\\n'
|
||||
'timeout=5\\n'
|
||||
'default=boot-from-volume\\n'
|
||||
'target=/boot/\\n'
|
||||
'[boot-from-volume]\\n'
|
||||
'image=%(image)s\\n'
|
||||
'ramdisk=%(ramdisk)s\\n'
|
||||
'parameters=\\"root=%(root)s '
|
||||
'rd.zfcp=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s '
|
||||
'zfcp.allow_lun_scan=0 selinux=0\\""'
|
||||
'>/etc/zipl_volume.conf\n'
|
||||
'zipl -c /etc/zipl_volume.conf')
|
||||
% {'image': image, 'ramdisk': ramdisk, 'root': root,
|
||||
'fcp': fcp, 'wwpn': wwpn, 'lun': lun}]
|
||||
|
||||
|
||||
class sles(LinuxDist):
|
||||
def _get_network_file_path(self):
|
||||
return '/etc/sysconfig/network/'
|
||||
|
||||
def _get_cidr_from_ip_netmask(self, ip, netmask):
|
||||
netmask_fields = netmask.split('.')
|
||||
bin_str = ''
|
||||
for octet in netmask_fields:
|
||||
bin_str += bin(int(octet))[2:].zfill(8)
|
||||
mask = str(len(bin_str.rstrip('0')))
|
||||
cidr_v4 = ip + '/' + mask
|
||||
return cidr_v4
|
||||
|
||||
def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4,
|
||||
netmask_v4, address_read, subchannels):
|
||||
cidr_v4 = self._get_cidr_from_ip_netmask(ip_v4, netmask_v4)
|
||||
cfg_str = "BOOTPROTO=\'static\'\n"
|
||||
cfg_str += "IPADDR=\'%s\'\n" % cidr_v4
|
||||
cfg_str += "BROADCAST=\'%s\'\n" % broadcast_v4
|
||||
cfg_str += "STARTMODE=\'onboot\'\n"
|
||||
cfg_str += ("NAME=\'OSA Express Network card (%s)\'\n" %
|
||||
address_read)
|
||||
return cfg_str
|
||||
|
||||
def _get_route_str(self, gateway_v4):
|
||||
route_str = 'default %s - -\n' % gateway_v4
|
||||
return route_str
|
||||
|
||||
def _get_cmd_str(self, address_read, address_write, address_data):
|
||||
cmd_str = 'qeth_configure -l 0.0.%s ' % address_read.lower()
|
||||
cmd_str += '0.0.%(write)s 0.0.%(data)s 1\n' % {'write':
|
||||
address_write.lower(), 'data': address_data.lower()}
|
||||
cmd_str += ('echo "0.0.%(read)s,0.0.%(write)s,0.0.%(data)s #`date`"'
|
||||
' >>/boot/zipl/active_devices.txt\n' % {'read':
|
||||
address_read.lower(), 'write': address_write.lower(),
|
||||
'data': address_data.lower()})
|
||||
return cmd_str
|
||||
|
||||
def _get_dns_filename(self):
|
||||
return '/etc/resolv.conf'
|
||||
|
||||
def _get_device_filename(self, device_num):
|
||||
return 'ifcfg-eth' + str(device_num)
|
||||
|
||||
def _get_device_name(self, device_num):
|
||||
return 'eth' + str(device_num)
|
||||
|
||||
def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str,
|
||||
udev_cfg_str):
|
||||
udev_file_name = '/etc/udev/rules.d/70-persistent-net.rules'
|
||||
cfg_files.append((udev_file_name, udev_cfg_str))
|
||||
if len(route_cfg_str) > 0:
|
||||
cfg_files.append((file_name_route, route_cfg_str))
|
||||
|
||||
def _get_udev_configuration(self, device, dev_channel):
|
||||
cfg_str = 'SUBSYSTEM==\"net\", ACTION==\"add\", DRIVERS==\"qeth\",'
|
||||
cfg_str += ' KERNELS==\"%s\", ATTR{type}==\"1\",' % dev_channel
|
||||
cfg_str += ' KERNEL==\"eth*\", NAME=\"eth%s\"\n' % device
|
||||
|
||||
return cfg_str
|
||||
|
||||
def _append_udev_rules_file(self, cfg_files, base_vdev):
|
||||
rules_file_name = '/etc/udev/rules.d/51-qeth-0.0.%s.rules' % base_vdev
|
||||
read_ch = '0.0.' + base_vdev
|
||||
write_ch = '0.0.' + str(hex(int(base_vdev, 16) + 1))[2:]
|
||||
data_ch = '0.0.' + str(hex(int(base_vdev, 16) + 2))[2:]
|
||||
udev_rules_str = self._get_udev_rules(read_ch, write_ch, data_ch)
|
||||
cfg_files.append((rules_file_name, udev_rules_str))
|
||||
|
||||
def _get_udev_rules(self, channel_read, channel_write, channel_data):
|
||||
"""construct udev rules info."""
|
||||
sub_str = '%(read)s %%k %(read)s %(write)s %(data)s qeth' % {
|
||||
'read': channel_read,
|
||||
'read': channel_read,
|
||||
'write': channel_write,
|
||||
'data': channel_data}
|
||||
rules_str = '# Configure qeth device at'
|
||||
rules_str += ' %(read)s/%(write)s/%(data)s\n' % {
|
||||
'read': channel_read,
|
||||
'write': channel_write,
|
||||
'data': channel_data}
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL=='
|
||||
'\"qeth\", IMPORT{program}=\"collect %s\"\n') % sub_str
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"'
|
||||
'%(read)s\", IMPORT{program}="collect %(channel)s\"\n') % {
|
||||
'read': channel_read, 'channel': sub_str}
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"'
|
||||
'%(write)s\", IMPORT{program}=\"collect %(channel)s\"\n') % {
|
||||
'write': channel_write, 'channel': sub_str}
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"'
|
||||
'%(data)s\", IMPORT{program}=\"collect %(channel)s\"\n') % {
|
||||
'data': channel_data, 'channel': sub_str}
|
||||
rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"drivers\", KERNEL==\"'
|
||||
'qeth\", IMPORT{program}=\"collect --remove %s\"\n') % sub_str
|
||||
rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"ccw\", KERNEL==\"'
|
||||
'%(read)s\", IMPORT{program}=\"collect --remove %(channel)s\"\n'
|
||||
) % {'read': channel_read, 'channel': sub_str}
|
||||
rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"ccw\", KERNEL==\"'
|
||||
'%(write)s\", IMPORT{program}=\"collect --remove %(channel)s\"\n'
|
||||
) % {'write': channel_write, 'channel': sub_str}
|
||||
rules_str += ('ACTION==\"remove\", SUBSYSTEM==\"ccw\", KERNEL==\"'
|
||||
'%(data)s\", IMPORT{program}=\"collect --remove %(channel)s\"\n'
|
||||
) % {'data': channel_data, 'channel': sub_str}
|
||||
rules_str += ('TEST==\"[ccwgroup/%(read)s]\", GOTO=\"qeth-%(read)s'
|
||||
'-end\"\n') % {'read': channel_read, 'read': channel_read}
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccw\", ENV{COLLECT_'
|
||||
'%(read)s}==\"0\", ATTR{[drivers/ccwgroup:qeth]group}=\"'
|
||||
'%(read)s,%(write)s,%(data)s\"\n') % {
|
||||
'read': channel_read, 'read': channel_read,
|
||||
'write': channel_write, 'data': channel_data}
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"qeth'
|
||||
'\", ENV{COLLECT_%(read)s}==\"0\", ATTR{[drivers/'
|
||||
'ccwgroup:qeth]group}=\"%(read)s,%(write)s,%(data)s\"\n'
|
||||
'LABEL=\"qeth-%(read)s-end\"\n') % {
|
||||
'read': channel_read, 'read': channel_read, 'write': channel_write,
|
||||
'data': channel_data, 'read': channel_read}
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccwgroup\", KERNEL=='
|
||||
'\"%s\", ATTR{layer2}=\"1\"\n') % channel_read
|
||||
rules_str += ('ACTION==\"add\", SUBSYSTEM==\"ccwgroup\", KERNEL=='
|
||||
'\"%s\", ATTR{online}=\"1\"\n') % channel_read
|
||||
return rules_str
|
||||
|
||||
def get_scp_string(self, root, fcp, wwpn, lun):
|
||||
return ("=root=%(root)s zfcp.allow_lun_scan=0 "
|
||||
"zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % {
|
||||
'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}
|
||||
|
||||
def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun):
|
||||
return ['#!/bin/bash\n',
|
||||
('echo -e "[defaultboot]\\n'
|
||||
'default=boot-from-volume\\n'
|
||||
'[boot-from-volume]\\n'
|
||||
'image=%(image)s\\n'
|
||||
'target = /boot/zipl\\n'
|
||||
'ramdisk=%(ramdisk)s\\n'
|
||||
'parameters=\\"root=%(root)s '
|
||||
'zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s '
|
||||
'zfcp.allow_lun_scan=0\\""'
|
||||
'>/etc/zipl_volume.conf\n'
|
||||
'mkinitrd\n'
|
||||
'zipl -c /etc/zipl_volume.conf')
|
||||
% {'image': image, 'ramdisk': ramdisk, 'root': root,
|
||||
'fcp': fcp, 'wwpn': wwpn, 'lun': lun}]
|
||||
|
||||
|
||||
class sles11(sles):
|
||||
def get_znetconfig_contents(self):
|
||||
return '\n'.join(('cio_ignore -R',
|
||||
'znetconf -R -n',
|
||||
'sleep 2',
|
||||
'udevadm trigger',
|
||||
'udevadm settle',
|
||||
'sleep 2',
|
||||
'znetconf -A',
|
||||
'service network restart',
|
||||
'cio_ignore -u'))
|
||||
|
||||
|
||||
class sles12(sles):
|
||||
def get_znetconfig_contents(self):
|
||||
remove_route = 'rm -f %s/ifroute-eth*' % self._get_network_file_path()
|
||||
return '\n'.join(('cio_ignore -R',
|
||||
'znetconf -R -n',
|
||||
'sleep 2',
|
||||
remove_route,
|
||||
'udevadm trigger',
|
||||
'udevadm settle',
|
||||
'sleep 2',
|
||||
'znetconf -A',
|
||||
'cio_ignore -u'))
|
||||
|
||||
|
||||
class ubuntu(LinuxDist):
|
||||
def create_network_configuration_files(self, file_path, network_info,
|
||||
base_vdev):
|
||||
"""Generate network configuration files to instance."""
|
||||
cfg_files = []
|
||||
cmd_strings = ''
|
||||
network_config_file_name = self._get_network_file()
|
||||
network_cfg_str = 'auto lo\n'
|
||||
network_cfg_str += 'iface lo inet loopback\n'
|
||||
|
||||
for vif in network_info:
|
||||
network_hw_config_fname = self._get_device_filename(base_vdev)
|
||||
network_hw_config_str = self._get_network_hw_config_str(base_vdev)
|
||||
cfg_files.append((network_hw_config_fname, network_hw_config_str))
|
||||
network = vif['network']
|
||||
(cfg_str, dns_str) = self._generate_network_configuration(network,
|
||||
base_vdev)
|
||||
LOG.debug('Network configure file content is: %s', cfg_str)
|
||||
network_cfg_str += cfg_str
|
||||
if len(dns_str) > 0:
|
||||
network_cfg_str += dns_str
|
||||
base_vdev = str(hex(int(base_vdev, 16) + 3))[2:]
|
||||
cfg_files.append((network_config_file_name, network_cfg_str))
|
||||
return cfg_files, cmd_strings
|
||||
|
||||
def _get_network_file(self):
|
||||
return '/etc/network/interfaces'
|
||||
|
||||
def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4,
|
||||
netmask_v4):
|
||||
cfg_str = 'auto ' + device + '\n'
|
||||
cfg_str += 'iface ' + device + ' inet static\n'
|
||||
cfg_str += 'address ' + ip_v4 + '\n'
|
||||
cfg_str += 'netmask ' + netmask_v4 + '\n'
|
||||
cfg_str += 'broadcast ' + broadcast_v4 + '\n'
|
||||
cfg_str += 'gateway ' + gateway_v4 + '\n'
|
||||
return cfg_str
|
||||
|
||||
def _generate_network_configuration(self, network, vdev):
|
||||
ip_v4 = dns_str = ''
|
||||
subnets_v4 = [s for s in network['subnets'] if s['version'] == 4]
|
||||
|
||||
if len(subnets_v4[0]['ips']) > 0:
|
||||
ip_v4 = subnets_v4[0]['ips'][0]['address']
|
||||
if len(subnets_v4[0]['dns']) > 0:
|
||||
for dns in subnets_v4[0]['dns']:
|
||||
dns_str += 'dns-nameservers ' + dns['address'] + '\n'
|
||||
netmask_v4 = str(subnets_v4[0].as_netaddr().netmask)
|
||||
gateway_v4 = subnets_v4[0]['gateway']['address'] or ''
|
||||
broadcast_v4 = str(subnets_v4[0].as_netaddr().broadcast)
|
||||
device = self._get_device_name(vdev)
|
||||
cfg_str = self._get_cfg_str(device, broadcast_v4, gateway_v4,
|
||||
ip_v4, netmask_v4)
|
||||
return cfg_str, dns_str
|
||||
|
||||
def _get_route_str(self, gateway_v4):
|
||||
return ''
|
||||
|
||||
def _get_cmd_str(self, address_read, address_write, address_data):
|
||||
return ''
|
||||
|
||||
def _get_device_name(self, device_num):
|
||||
return 'enc' + str(device_num)
|
||||
|
||||
def _get_dns_filename(self):
|
||||
return ''
|
||||
|
||||
def _get_device_filename(self, device_num):
|
||||
return '/etc/sysconfig/hardware/config-ccw-0.0.' + str(device_num)
|
||||
|
||||
def _get_network_hw_config_str(self, base_vdev):
|
||||
ccwgroup_chans_str = ' '.join((
|
||||
'0.0.' + str(hex(int(base_vdev, 16)))[2:],
|
||||
'0.0.' + str(hex(int(base_vdev, 16) + 1))[2:],
|
||||
'0.0.' + str(hex(int(base_vdev, 16) + 2))[2:]))
|
||||
return '\n'.join(('CCWGROUP_CHANS=(' + ccwgroup_chans_str + ')',
|
||||
'QETH_OPTIONS=layer2'))
|
||||
|
||||
def _get_network_file_path(self):
|
||||
pass
|
||||
|
||||
def get_znetconfig_contents(self):
|
||||
return '\n'.join(('cio_ignore -R',
|
||||
'znetconf -R -n',
|
||||
'sleep 2',
|
||||
'udevadm trigger',
|
||||
'udevadm settle',
|
||||
'sleep 2',
|
||||
'znetconf -A',
|
||||
'/etc/init.d/networking restart',
|
||||
'cio_ignore -u'))
|
||||
|
||||
def _get_udev_configuration(self, device, dev_channel):
|
||||
return ''
|
||||
|
||||
def _append_udev_info(self, cfg_files, file_name_route, route_cfg_str,
|
||||
udev_cfg_str):
|
||||
pass
|
||||
|
||||
def get_scp_string(self, root, fcp, wwpn, lun):
|
||||
# Although the content is the same as SLES, I don't want to merge them.
|
||||
# Because they are NOT logically related, and it's very likely that
|
||||
# they evolve separately in the future.
|
||||
return ("=root=%(root)s zfcp.allow_lun_scan=0 "
|
||||
"zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s") % {
|
||||
'root': root, 'fcp': fcp, 'wwpn': wwpn, 'lun': lun}
|
||||
|
||||
def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun):
|
||||
return ['#!/bin/bash\n',
|
||||
('echo -e "[defaultboot]\\n'
|
||||
'default=boot-from-volume\\n'
|
||||
'[boot-from-volume]\\n'
|
||||
'image=%(image)s\\n'
|
||||
'target = /boot/zipl\\n'
|
||||
'ramdisk=%(ramdisk)s\\n'
|
||||
'parameters=\\"root=%(root)s '
|
||||
'zfcp.device=0.0.%(fcp)s,0x%(wwpn)s,0x%(lun)s '
|
||||
'zfcp.allow_lun_scan=0\\""'
|
||||
'>/etc/zipl_volume.conf\n'
|
||||
'mkinitrd\n'
|
||||
'zipl -c /etc/zipl_volume.conf')
|
||||
% {'image': image, 'ramdisk': ramdisk, 'root': root,
|
||||
'fcp': fcp, 'wwpn': wwpn, 'lun': lun}]
|
||||
|
||||
def assemble_zfcp_srcdev(self, fcp, wwpn, lun):
|
||||
path = '/dev/disk/by-path/ccw-0.0.%(fcp)s-fc-0x%(wwpn)s-lun-%(lun)s'
|
||||
lun = int(lun[0:4], 16)
|
||||
srcdev = path % {'fcp': fcp, 'wwpn': wwpn, 'lun': lun}
|
||||
return srcdev
|
||||
|
||||
def _get_udev_rules(self, channel_read, channel_write, channel_data):
|
||||
"""construct udev rules info."""
|
||||
return ''
|
||||
|
||||
def _append_udev_rules_file(self, cfg_files, base_vdev):
|
||||
pass
|
||||
|
||||
|
||||
class ubuntu16(ubuntu):
|
||||
pass
|
||||
|
||||
|
||||
class ListDistManager(object):
|
||||
def get_linux_dist(self, os_version):
|
||||
distro, release = self.parse_dist(os_version)
|
||||
return globals()[distro + release]
|
||||
|
||||
def _parse_release(self, os_version, distro, remain):
|
||||
supported = {'rhel': ['6', '7'],
|
||||
'sles': ['11', '12'],
|
||||
'ubuntu': ['16']}
|
||||
releases = supported[distro]
|
||||
|
||||
for r in releases:
|
||||
if remain.startswith(r):
|
||||
return r
|
||||
else:
|
||||
msg = _('Can not handle os: %s') % os_version
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
def parse_dist(self, os_version):
|
||||
"""Separate os and version from os_version.
|
||||
|
||||
Possible return value are only:
|
||||
('rhel', x.y) and ('sles', x.y) where x.y may not be digits
|
||||
"""
|
||||
supported = {'rhel': ['rhel', 'redhat', 'red hat'],
|
||||
'sles': ['suse', 'sles'],
|
||||
'ubuntu': ['ubuntu']}
|
||||
os_version = os_version.lower()
|
||||
for distro, patterns in supported.items():
|
||||
for i in patterns:
|
||||
if os_version.startswith(i):
|
||||
# Not guarrentee the version is digital
|
||||
remain = os_version.split(i, 2)[1]
|
||||
release = self._parse_release(os_version, distro, remain)
|
||||
return distro, release
|
||||
|
||||
msg = _('Can not handle os: %s') % os_version
|
||||
raise exception.ZVMImageError(msg=msg)
|
File diff suppressed because it is too large
Load Diff
@ -26,18 +26,6 @@ class ZVMDriverError(ZVMBaseException):
|
||||
msg_fmt = _('z/VM driver error: %(msg)s')
|
||||
|
||||
|
||||
class ZVMXCATRequestFailed(ZVMBaseException):
|
||||
msg_fmt = _('Request to xCAT server %(xcatserver)s failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMInvalidXCATResponseDataError(ZVMBaseException):
|
||||
msg_fmt = _('Invalid data returned from xCAT: %(msg)s')
|
||||
|
||||
|
||||
class ZVMXCATInternalError(ZVMBaseException):
|
||||
msg_fmt = _('Error returned from xCAT: %(msg)s')
|
||||
|
||||
|
||||
class ZVMVolumeError(ZVMBaseException):
|
||||
msg_fmt = _('Volume error: %(msg)s')
|
||||
|
||||
@ -46,34 +34,10 @@ class ZVMImageError(ZVMBaseException):
|
||||
msg_fmt = _("Image error: %(msg)s")
|
||||
|
||||
|
||||
class ZVMGetImageFromXCATFailed(ZVMBaseException):
|
||||
msg_fmt = _('Get image from xCAT failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMNetworkError(ZVMBaseException):
|
||||
msg_fmt = _("z/VM network error: %(msg)s")
|
||||
|
||||
|
||||
class ZVMXCATXdshFailed(ZVMBaseException):
|
||||
msg_fmt = _('Execute xCAT xdsh command failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMXCATCreateNodeFailed(ZVMBaseException):
|
||||
msg_fmt = _('Create xCAT node %(node)s failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMXCATCreateUserIdFailed(ZVMBaseException):
|
||||
msg_fmt = _('Create xCAT user id %(instance)s failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMXCATUpdateNodeFailed(ZVMBaseException):
|
||||
msg_fmt = _('Update node %(node)s info failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMXCATDeployNodeFailed(ZVMBaseException):
|
||||
msg_fmt = _('Deploy image on node %(node)s failed: %(msg)s')
|
||||
|
||||
|
||||
class ZVMConfigDriveError(ZVMBaseException):
|
||||
msg_fmt = _('Create configure drive failed: %(msg)s')
|
||||
|
||||
|
@ -1,824 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import tarfile
|
||||
import xml.dom.minidom as Dom
|
||||
|
||||
from nova import exception as nova_exception
|
||||
from nova.i18n import _
|
||||
from nova.image import glance
|
||||
from nova import utils
|
||||
from nova.virt import images
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from nova_zvm.virt.zvm import exception
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
QUEUE_BUFFER_SIZE = 10
|
||||
|
||||
|
||||
class ZVMImages(object):
|
||||
|
||||
def __init__(self):
|
||||
self._xcat_url = zvmutils.get_xcat_url()
|
||||
self._pathutils = zvmutils.PathUtils()
|
||||
|
||||
def create_zvm_image(self, instance, image_name, image_href):
|
||||
"""Create z/VM image from z/VM instance by invoking xCAT REST API
|
||||
imgcapture.
|
||||
"""
|
||||
nodename = instance['name']
|
||||
profile = image_name + "_" + image_href.replace('-', '_')
|
||||
body = ['nodename=' + nodename,
|
||||
'profile=' + profile]
|
||||
if CONF.zvm_image_compression_level:
|
||||
if CONF.zvm_image_compression_level.isdigit() and (
|
||||
int(CONF.zvm_image_compression_level) in range(0, 10)):
|
||||
body.append('compress=%s' % CONF.zvm_image_compression_level)
|
||||
else:
|
||||
msg = _("Invalid zvm_image_compression_level detected, please"
|
||||
"specify it with a integer between 0 and 9 in your nova.conf")
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
url = self._xcat_url.imgcapture()
|
||||
LOG.debug('Capturing %s start', instance['name'])
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMImageError):
|
||||
res = zvmutils.xcat_request("POST", url, body)
|
||||
|
||||
os_image = self._get_os_image(res)
|
||||
|
||||
return os_image
|
||||
|
||||
def _get_os_image(self, response):
|
||||
"""Return the image_name by parsing the imgcapture rest api
|
||||
response.
|
||||
"""
|
||||
image_name_xcat = ""
|
||||
if len(response['info']) > 0:
|
||||
for info in response['info']:
|
||||
for info_element in info:
|
||||
if "Completed capturing the image" in info_element:
|
||||
start_index = info_element.find('(')
|
||||
end_index = info_element.find(')')
|
||||
image_name_xcat = info_element[start_index + 1:
|
||||
end_index]
|
||||
return image_name_xcat
|
||||
if len(image_name_xcat) == 0:
|
||||
msg = _("Capture image failed.")
|
||||
LOG.error(msg)
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
else:
|
||||
msg = _("Capture image returns bad response. res is %s") % response
|
||||
LOG.error(msg)
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
def get_snapshot_time_path(self):
|
||||
return self._pathutils.get_snapshot_time_path()
|
||||
|
||||
def get_image_from_xcat(self, image_name_xcat, image_name,
|
||||
snapshot_time_path):
|
||||
"""Import image from xCAT to nova, by invoking the imgexport
|
||||
REST API.
|
||||
"""
|
||||
LOG.debug("Getting image from xCAT")
|
||||
destination = os.path.join(snapshot_time_path, image_name + '.tgz')
|
||||
host = zvmutils.get_host()
|
||||
body = ['osimage=' + image_name_xcat,
|
||||
'destination=' + destination,
|
||||
'remotehost=' + host]
|
||||
url = self._xcat_url.imgexport()
|
||||
|
||||
try:
|
||||
zvmutils.xcat_request("POST", url, body)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError) as err:
|
||||
msg = (_("Transfer image to compute node failed: %s") %
|
||||
err.format_message())
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
return destination
|
||||
|
||||
def delete_image_glance(self, image_service, context, image_href):
|
||||
"""Delete the image from glance database and image repository.
|
||||
|
||||
Can be used for a rollback step if operations to image fail.
|
||||
"""
|
||||
image_service.delete(context, image_href)
|
||||
|
||||
def clean_up_snapshot_time_path(self, snapshot_time_path):
|
||||
"""Clean up the time_path and its contents under "snapshot_tmp" after
|
||||
image uploaded to glance.
|
||||
|
||||
Also be used for a rollback step if operations to the image file fail.
|
||||
"""
|
||||
if os.path.exists(snapshot_time_path):
|
||||
LOG.debug("Cleaning up nova local image file")
|
||||
shutil.rmtree(snapshot_time_path)
|
||||
|
||||
def _delete_image_file_from_xcat(self, image_name_xcat):
|
||||
"""Delete image file from xCAT MN.
|
||||
|
||||
When capturing, image in the xCAT MN's repository will be removed after
|
||||
it is imported to nova compute node.
|
||||
"""
|
||||
LOG.debug("Removing image files from xCAT MN image repository")
|
||||
url = self._xcat_url.rmimage('/' + image_name_xcat)
|
||||
try:
|
||||
zvmutils.xcat_request("DELETE", url)
|
||||
except (exception.ZVMXCATInternalError,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATRequestFailed):
|
||||
LOG.warning(_("Failed to delete image file %s from xCAT"),
|
||||
image_name_xcat)
|
||||
|
||||
def _delete_image_object_from_xcat(self, image_name_xcat):
|
||||
"""Delete image object from xCAT MN.
|
||||
|
||||
After capturing, image definition in the xCAT MN's table osimage and
|
||||
linuximage will be removed after it is imported to nova compute node.
|
||||
|
||||
"""
|
||||
LOG.debug("Deleting the image object")
|
||||
url = self._xcat_url.rmobject('/' + image_name_xcat)
|
||||
try:
|
||||
zvmutils.xcat_request("DELETE", url)
|
||||
except (exception.ZVMXCATInternalError,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATRequestFailed):
|
||||
LOG.warning(_("Failed to delete image definition %s from xCAT"),
|
||||
image_name_xcat)
|
||||
|
||||
def get_image_file_path_from_image_name(self, image_name_xcat):
|
||||
return zvmutils.xcat_cmd_gettab("linuximage", "imagename",
|
||||
image_name_xcat, "rootimgdir")
|
||||
|
||||
def delete_image_from_xcat(self, image_name_xcat):
|
||||
self._delete_image_file_from_xcat(image_name_xcat)
|
||||
self._delete_image_object_from_xcat(image_name_xcat)
|
||||
|
||||
def _getxmlnode(self, node, name):
|
||||
return node.getElementsByTagName(name)[0] if node else []
|
||||
|
||||
def _getnode(self, node_root, tagname):
|
||||
"""For parse manifest."""
|
||||
nodename = node_root.getElementsByTagName(tagname)[0]
|
||||
nodevalue = nodename.childNodes[0].data
|
||||
return nodevalue
|
||||
|
||||
def parse_manifest_xml(self, image_package_path):
|
||||
"""Return the image properties from manifest.xml."""
|
||||
LOG.debug("Parsing the manifest.xml")
|
||||
manifest_xml = os.path.join(image_package_path, "manifest.xml")
|
||||
|
||||
manifest = {}
|
||||
|
||||
if os.path.exists(manifest_xml):
|
||||
xml_file = Dom.parse(manifest_xml) # nosec
|
||||
else:
|
||||
LOG.warning(_('manifest.xml does not exist'))
|
||||
manifest['imagename'] = ''
|
||||
manifest['imagetype'] = ''
|
||||
manifest['osarch'] = ''
|
||||
manifest['osname'] = ''
|
||||
manifest['osvers'] = ''
|
||||
manifest['profile'] = ''
|
||||
manifest['provmethod'] = ''
|
||||
return manifest
|
||||
|
||||
node_root = xml_file.documentElement
|
||||
node_root = self._getxmlnode(node_root, 'osimage')
|
||||
manifest['imagename'] = self._getnode(node_root, "imagename")
|
||||
manifest['imagetype'] = self._getnode(node_root, "imagetype")
|
||||
manifest['osarch'] = self._getnode(node_root, "osarch")
|
||||
manifest['osname'] = self._getnode(node_root, "osname")
|
||||
manifest['osvers'] = self._getnode(node_root, "osvers")
|
||||
manifest['profile'] = self._getnode(node_root, "profile")
|
||||
manifest['provmethod'] = self._getnode(node_root, "provmethod")
|
||||
|
||||
return manifest
|
||||
|
||||
def untar_image_bundle(self, snapshot_time_path, image_bundle):
|
||||
"""Untar the image bundle *.tgz from xCAT and remove the *.tgz."""
|
||||
if os.path.exists(image_bundle):
|
||||
LOG.debug("Untarring the image bundle ... ")
|
||||
tarobj = tarfile.open(image_bundle, "r:gz")
|
||||
for tarinfo in tarobj:
|
||||
tarobj.extract(tarinfo.name, path=snapshot_time_path)
|
||||
tarobj.close()
|
||||
os.remove(image_bundle)
|
||||
else:
|
||||
self.clean_up_snapshot_time_path(snapshot_time_path)
|
||||
msg = _("Image bundle does not exist %s") % image_bundle
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
def get_image_file_name(self, image_package_path):
|
||||
if os.path.exists(image_package_path):
|
||||
file_contents = os.listdir(image_package_path)
|
||||
for f in file_contents:
|
||||
if f.endswith('.img'):
|
||||
return f
|
||||
msg = _("Can not find image file under %s") % image_package_path
|
||||
else:
|
||||
msg = _("Image path %s not exist") % image_package_path
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
def image_exist_xcat(self, image_id):
|
||||
"""To see if the specific image exist in xCAT MN's image
|
||||
repository.
|
||||
"""
|
||||
LOG.debug("Checking if the image %s exists or not in xCAT "
|
||||
"MN's image repository ", image_id)
|
||||
image_uuid = image_id.replace('-', '_')
|
||||
parm = '&criteria=profile=~' + image_uuid
|
||||
url = self._xcat_url.lsdef_image(addp=parm)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMImageError):
|
||||
res = zvmutils.xcat_request("GET", url)
|
||||
|
||||
res_image = res['info']
|
||||
|
||||
if '_' in str(res_image):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def fetch_image(self, context, image_id, target, user, project):
|
||||
LOG.debug("Downloading image %s from glance image server",
|
||||
image_id)
|
||||
try:
|
||||
images.fetch(context, image_id, target)
|
||||
except Exception as err:
|
||||
emsg = zvmutils.format_exception_msg(err)
|
||||
msg = _("Download image file of image %(id)s failed with reason:"
|
||||
" %(err)s") % {'id': image_id, 'err': emsg}
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
def generate_manifest_file(self, image_meta, image_name, disk_file,
|
||||
manifest_path):
|
||||
"""Generate the manifest.xml file from glance's image metadata
|
||||
as a part of the image bundle.
|
||||
"""
|
||||
image_id = image_meta['id']
|
||||
image_type = image_meta['properties']['image_type_xcat']
|
||||
os_version = image_meta['properties']['os_version']
|
||||
os_name = image_meta['properties']['os_name']
|
||||
os_arch = image_meta['properties']['architecture']
|
||||
prov_method = image_meta['properties']['provisioning_method']
|
||||
|
||||
image_profile = '_'.join((image_name, image_id.replace('-', '_')))
|
||||
image_name_xcat = '-'.join((os_version, os_arch,
|
||||
prov_method, image_profile))
|
||||
rootimgdir_str = ('/install', prov_method, os_version,
|
||||
os_arch, image_profile)
|
||||
rootimgdir = '/'.join(rootimgdir_str)
|
||||
today_date = datetime.date.today()
|
||||
last_use_date_string = today_date.strftime("%Y-%m-%d")
|
||||
is_deletable = "auto:last_use_date:" + last_use_date_string
|
||||
|
||||
doc = Dom.Document()
|
||||
xcatimage = doc.createElement('xcatimage')
|
||||
doc.appendChild(xcatimage)
|
||||
|
||||
# Add linuximage section
|
||||
imagename = doc.createElement('imagename')
|
||||
imagename_value = doc.createTextNode(image_name_xcat)
|
||||
imagename.appendChild(imagename_value)
|
||||
rootimagedir = doc.createElement('rootimgdir')
|
||||
rootimagedir_value = doc.createTextNode(rootimgdir)
|
||||
rootimagedir.appendChild(rootimagedir_value)
|
||||
linuximage = doc.createElement('linuximage')
|
||||
linuximage.appendChild(imagename)
|
||||
linuximage.appendChild(rootimagedir)
|
||||
xcatimage.appendChild(linuximage)
|
||||
|
||||
# Add osimage section
|
||||
osimage = doc.createElement('osimage')
|
||||
manifest = {'imagename': image_name_xcat,
|
||||
'imagetype': image_type,
|
||||
'isdeletable': is_deletable,
|
||||
'osarch': os_arch,
|
||||
'osname': os_name,
|
||||
'osvers': os_version,
|
||||
'profile': image_profile,
|
||||
'provmethod': prov_method}
|
||||
|
||||
if 'image_comments' in image_meta['properties']:
|
||||
manifest['comments'] = image_meta['properties']['image_comments']
|
||||
|
||||
for item in list(manifest.keys()):
|
||||
itemkey = doc.createElement(item)
|
||||
itemvalue = doc.createTextNode(manifest[item])
|
||||
itemkey.appendChild(itemvalue)
|
||||
osimage.appendChild(itemkey)
|
||||
xcatimage.appendChild(osimage)
|
||||
f = open(manifest_path + '/manifest.xml', 'w')
|
||||
f.write(doc.toprettyxml(indent=''))
|
||||
f.close()
|
||||
|
||||
# Add the rawimagefiles section
|
||||
rawimagefiles = doc.createElement('rawimagefiles')
|
||||
xcatimage.appendChild(rawimagefiles)
|
||||
|
||||
files = doc.createElement('files')
|
||||
files_value = doc.createTextNode(rootimgdir + '/' + disk_file)
|
||||
files.appendChild(files_value)
|
||||
|
||||
rawimagefiles.appendChild(files)
|
||||
|
||||
f = open(manifest_path + '/manifest.xml', 'w')
|
||||
f.write(doc.toprettyxml(indent=' '))
|
||||
f.close()
|
||||
|
||||
self._rewr(manifest_path)
|
||||
|
||||
return manifest_path + '/manifest.xml'
|
||||
|
||||
def _rewr(self, manifest_path):
|
||||
f = open(manifest_path + '/manifest.xml', 'r')
|
||||
lines = f.read()
|
||||
f.close()
|
||||
|
||||
lines = lines.replace('\n', '')
|
||||
lines = re.sub(r'>(\s*)<', r'>\n\1<', lines)
|
||||
lines = re.sub(r'>[ \t]*(\S+)[ \t]*<', r'>\1<', lines)
|
||||
|
||||
f = open(manifest_path + '/manifest.xml', 'w')
|
||||
f.write(lines)
|
||||
f.close()
|
||||
|
||||
def generate_image_bundle(self, spawn_path, tmp_file_fn, image_name):
|
||||
"""Generate the image bundle which is used to import to xCAT MN's
|
||||
image repository.
|
||||
"""
|
||||
image_bundle_name = image_name + '.tar'
|
||||
tar_file = spawn_path + '/' + tmp_file_fn + '_' + image_bundle_name
|
||||
LOG.debug("The generate the image bundle file is %s", tar_file)
|
||||
|
||||
os.chdir(spawn_path)
|
||||
tarFile = tarfile.open(tar_file, mode='w')
|
||||
|
||||
try:
|
||||
tarFile.add(tmp_file_fn)
|
||||
tarFile.close()
|
||||
except Exception as err:
|
||||
msg = (_("Generate image bundle failed: %s") % err)
|
||||
LOG.error(msg)
|
||||
if os.path.isfile(tar_file):
|
||||
os.remove(tar_file)
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
finally:
|
||||
self._pathutils.clean_temp_folder(tmp_file_fn)
|
||||
|
||||
return tar_file
|
||||
|
||||
def check_space_imgimport_xcat(self, context, instance, tar_file,
|
||||
xcat_free_space_threshold, zvm_xcat_master):
|
||||
image_href = instance['image_ref']
|
||||
try:
|
||||
free_space_xcat = self.get_free_space_xcat(
|
||||
xcat_free_space_threshold, zvm_xcat_master)
|
||||
img_transfer_needed = self._get_transfer_needed_space_xcat(context,
|
||||
image_href, tar_file)
|
||||
larger = max(xcat_free_space_threshold, img_transfer_needed)
|
||||
if img_transfer_needed > free_space_xcat:
|
||||
larger = max(xcat_free_space_threshold, img_transfer_needed)
|
||||
size_needed = float(larger - free_space_xcat)
|
||||
self.prune_image_xcat(context, size_needed,
|
||||
img_transfer_needed)
|
||||
else:
|
||||
LOG.debug("Image transfer needed space satisfied in xCAT")
|
||||
except exception.ZVMImageError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
os.remove(tar_file)
|
||||
|
||||
def put_image_to_xcat(self, image_bundle_package, image_profile):
|
||||
"""Import the image bundle from compute node to xCAT MN's image
|
||||
repository.
|
||||
"""
|
||||
remote_host_info = zvmutils.get_host()
|
||||
body = ['osimage=%s' % image_bundle_package,
|
||||
'profile=%s' % image_profile,
|
||||
'remotehost=%s' % remote_host_info,
|
||||
'nozip']
|
||||
url = self._xcat_url.imgimport()
|
||||
|
||||
try:
|
||||
zvmutils.xcat_request("POST", url, body)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError) as err:
|
||||
msg = (_("Import the image bundle to xCAT MN failed: %s") %
|
||||
err.format_message())
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
finally:
|
||||
os.remove(image_bundle_package)
|
||||
|
||||
def get_imgname_xcat(self, image_id):
|
||||
"""Get the xCAT deployable image name by image id."""
|
||||
image_uuid = image_id.replace('-', '_')
|
||||
parm = '&criteria=profile=~' + image_uuid
|
||||
url = self._xcat_url.lsdef_image(addp=parm)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMImageError):
|
||||
res = zvmutils.xcat_request("GET", url)
|
||||
with zvmutils.expect_invalid_xcat_resp_data(res):
|
||||
res_image = res['info'][0][0]
|
||||
res_img_name = res_image.strip().split(" ")[0]
|
||||
|
||||
if res_img_name:
|
||||
return res_img_name
|
||||
else:
|
||||
LOG.error(_("Fail to find the right image to deploy"))
|
||||
|
||||
def _get_image_list_xcat(self):
|
||||
"""Get an image list from xcat osimage table.
|
||||
|
||||
criteria: osarch=s390x and provmethod=netboot|raw|sysclone and
|
||||
isdeletable field
|
||||
|
||||
"""
|
||||
display_field = '&field=isdeletable'
|
||||
isdeletable_criteria = '&criteria=isdeletable=~^auto:last_use_date:'
|
||||
osarch_criteria = '&criteria=osarch=s390x'
|
||||
provmethod_criteria = '&criteria=provmethod=~netboot|raw|sysclone'
|
||||
|
||||
addp = ''.join([isdeletable_criteria, osarch_criteria,
|
||||
provmethod_criteria, display_field])
|
||||
url = self._xcat_url.lsdef_image(addp=addp)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMImageError):
|
||||
output = zvmutils.xcat_request("GET", url)
|
||||
|
||||
image_list = []
|
||||
if len(output['info']) <= 0:
|
||||
return image_list
|
||||
|
||||
if len(output['info'][0]) > 0:
|
||||
i = 0
|
||||
while i < len(output['info'][0]):
|
||||
if "Object name:" in output['info'][0][i]:
|
||||
sub_list = []
|
||||
len_objectname = len("Object name: ")
|
||||
is_deletable = output['info'][0][i + 1]
|
||||
last_use_date = self._validate_last_use_date(
|
||||
output['info'][0][i][len_objectname:],
|
||||
is_deletable)
|
||||
if last_use_date is None:
|
||||
i += 2
|
||||
continue
|
||||
sub_list.append(output['info'][0][i][len_objectname:])
|
||||
sub_list.append(last_use_date)
|
||||
image_list.append(sub_list)
|
||||
i += 2
|
||||
|
||||
return image_list
|
||||
|
||||
def update_last_use_date(self, image_name_xcat):
|
||||
"""Update the last_use_date in xCAT osimage table after a
|
||||
successful deploy.
|
||||
"""
|
||||
LOG.debug("Update the last_use_date in xCAT osimage table "
|
||||
"after a successful deploy")
|
||||
|
||||
today_date = datetime.date.today()
|
||||
last_use_date_string = today_date.strftime("%Y-%m-%d")
|
||||
url = self._xcat_url.tabch('/osimage')
|
||||
is_deletable = "auto:last_use_date:" + last_use_date_string
|
||||
body = ["imagename=" + image_name_xcat,
|
||||
"osimage.isdeletable=" + is_deletable]
|
||||
|
||||
try:
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError) as err:
|
||||
LOG.warning(_("Illegal date for last_use_date %s"),
|
||||
err.format_message())
|
||||
|
||||
return last_use_date_string
|
||||
|
||||
def _validate_last_use_date(self, image_name, is_deletable):
|
||||
"""Validate the isdeletable date format."""
|
||||
last_use_date_string = is_deletable.split(":")[2]
|
||||
timere = ("^\d{4}[-]((0([1-9]{1}))|"
|
||||
"(1[0|1|2]))[-](([0-2]([0-9]{1}))|(3[0|1]))$")
|
||||
|
||||
if (len(last_use_date_string) == 10) and (
|
||||
re.match(timere, last_use_date_string)):
|
||||
LOG.debug("The format for last_use_date is valid ")
|
||||
else:
|
||||
LOG.warning(_("The format for image %s record in xcat table "
|
||||
"osimage's last_used_date is not valid. The correct "
|
||||
"format is auto:last_use_date:yyyy-mm-dd"), image_name)
|
||||
return
|
||||
|
||||
try:
|
||||
last_use_date_datetime = datetime.datetime.strptime(
|
||||
last_use_date_string, '%Y-%m-%d')
|
||||
except Exception as err:
|
||||
LOG.warning(_("Illegal date for last_use_date %(msg)s"), err)
|
||||
return
|
||||
|
||||
return last_use_date_datetime.date()
|
||||
|
||||
def _verify_is_deletable_periodic(self, last_use_date, clean_period):
|
||||
"""Check the last_use_date of an image to determine if the image
|
||||
need to be cleaned.
|
||||
"""
|
||||
now = datetime.date.today()
|
||||
delta = (now - last_use_date).days
|
||||
if (delta - clean_period) >= 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def clean_image_cache_xcat(self, clean_period):
|
||||
"""Clean the old image."""
|
||||
image_list = self._get_image_list_xcat()
|
||||
if len(image_list) <= 0:
|
||||
return
|
||||
else:
|
||||
i = 0
|
||||
while i < len(image_list):
|
||||
image_name_xcat = image_list[i][0]
|
||||
last_use_date = image_list[i][1]
|
||||
if self._verify_is_deletable_periodic(last_use_date,
|
||||
clean_period):
|
||||
LOG.debug('Delete the image %s', image_name_xcat)
|
||||
self.delete_image_from_xcat(image_name_xcat)
|
||||
else:
|
||||
LOG.debug("Keep the image")
|
||||
i += 1
|
||||
|
||||
def _get_image_bundle_size(self, tar_file):
|
||||
size_byte = os.path.getsize(tar_file)
|
||||
return float(size_byte) / 1024 / 1024 / 1024
|
||||
|
||||
def _get_image_size_glance(self, context, image_href):
|
||||
(image_service, image_id) = glance.get_remote_image_service(
|
||||
context, image_href)
|
||||
|
||||
try:
|
||||
image_meta = image_service.show(context, image_href)
|
||||
except nova_exception.ImageNotFound:
|
||||
image_meta = {}
|
||||
return 0
|
||||
|
||||
size_byte = image_meta['size']
|
||||
return float(size_byte) / 1024 / 1024 / 1024
|
||||
|
||||
def get_free_space_xcat(self, xcat_free_space_threshold, zvm_xcat_master):
|
||||
"""Get the free space in xCAT MN /install."""
|
||||
LOG.debug("Get the xCAT MN /install free space")
|
||||
addp = "&field=--freerepospace"
|
||||
if isinstance(zvm_xcat_master, str):
|
||||
url = self._xcat_url.rinv("/" + zvm_xcat_master, addp=addp)
|
||||
else:
|
||||
msg = _("zvm_xcat_master should be specified as a string")
|
||||
LOG.error(msg)
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMImageError):
|
||||
result = zvmutils.xcat_request("GET", url)
|
||||
with zvmutils.expect_invalid_xcat_resp_data(result):
|
||||
if len(result['info']) == 0:
|
||||
msg = _("'rinv <zvm_xcat_master> --freerepospace' returns "
|
||||
"null, please check 'df -h /install', there may "
|
||||
"be something wrong with the mount of /install")
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
free_space_line = result['info'][0][0]
|
||||
free_space = free_space_line.split()[4]
|
||||
if free_space.endswith("G"):
|
||||
free_disk_value = free_space.rstrip("G")
|
||||
return float(free_disk_value)
|
||||
elif free_space.endswith("M"):
|
||||
free_disk_value = free_space.rstrip("M")
|
||||
return float(free_disk_value) / 1024
|
||||
elif free_space == "0":
|
||||
return 0
|
||||
else:
|
||||
return xcat_free_space_threshold
|
||||
|
||||
def get_imgcapture_needed(self, instance, user_dict):
|
||||
"""Get the space needed on xCAT MN for an image capture."""
|
||||
LOG.debug("Getting %s root disk size as image capture max needed.",
|
||||
instance['name'])
|
||||
try:
|
||||
rdisk = ''.join(['MDISK ', CONF.zvm_user_root_vdev])
|
||||
rdisk_dict = [mdisk for mdisk in user_dict
|
||||
if (mdisk.__contains__(rdisk))]
|
||||
if not rdisk_dict or not rdisk_dict[0]:
|
||||
raise exception.ZVMImageError(msg="User direct info error")
|
||||
|
||||
rdisk_list = rdisk_dict[0].split(' ')
|
||||
# will get similar result USERID: MDISK 0100 3390 1 3338 0E3B MR
|
||||
if rdisk_list[3] == '3390':
|
||||
return int(rdisk_list[5]) * 737280 / 1024.0 / 1024 / 1024 * 2
|
||||
elif rdisk_list[3] == '9336':
|
||||
return int(rdisk_list[5]) * 512 / 1024.0 / 1024 / 1024 * 2
|
||||
else:
|
||||
raise exception.ZVMImageError(msg="Unknown disk type")
|
||||
except (IndexError, ValueError, TypeError) as err:
|
||||
raise exception.ZVMImageError(msg=err)
|
||||
|
||||
def _get_transfer_needed_space_xcat(self, context, image_href, tar_file):
|
||||
"""To transfer an image bundle from glance to xCAT, the needed size is
|
||||
image_bundle_size + image_size.
|
||||
"""
|
||||
image_bundle_size = self._get_image_bundle_size(tar_file)
|
||||
image_size = self._get_image_size_glance(context, image_href)
|
||||
return image_bundle_size + image_size
|
||||
|
||||
def _get_image_href_by_osimage(self, image_name_xcat):
|
||||
"""If we have the xCAT.osimage.imagename, we want to get the
|
||||
image_href.
|
||||
"""
|
||||
try:
|
||||
image_profile = image_name_xcat.split("-")
|
||||
if len(image_profile) >= 4:
|
||||
return image_profile[3]
|
||||
else:
|
||||
return image_name_xcat
|
||||
except (TypeError, IndexError):
|
||||
LOG.error(_("xCAT imagename format for %s is not as expected"),
|
||||
image_name_xcat)
|
||||
|
||||
def _sort_image_by_use_date(self, image_list, left, right):
|
||||
"""Sort the image_list by last_use_date from oldest image to latest."""
|
||||
if (left < right):
|
||||
i = left
|
||||
j = right
|
||||
x = image_list[left]
|
||||
while (i < j):
|
||||
while (i < j and image_list[j][1] >= x[1]):
|
||||
j -= 1
|
||||
if(i < j):
|
||||
image_list[i] = image_list[j]
|
||||
i += 1
|
||||
while(i < j and image_list[i][1] < x[1]):
|
||||
i += 1
|
||||
if(i < j):
|
||||
image_list[j] = image_list[i]
|
||||
j -= 1
|
||||
image_list[i] = x
|
||||
self._sort_image_by_use_date(image_list, left, i - 1)
|
||||
self._sort_image_by_use_date(image_list, i + 1, right)
|
||||
|
||||
def _get_to_be_deleted_images_xcat(self, context, size_needed,
|
||||
current_needed):
|
||||
"""To get a list of images which is to be removed from xCAT image
|
||||
repository because it cannot provide enough space for image operations
|
||||
from OpenStack.
|
||||
"""
|
||||
image_list = self._get_image_list_xcat()
|
||||
size_sum = 0
|
||||
to_be_deleted_image_profile = []
|
||||
|
||||
if len(image_list) <= 0:
|
||||
msg = _("No image to be deleted, please create space manually "
|
||||
"on xcat(%s).") % CONF.zvm_xcat_server
|
||||
LOG.warning(msg)
|
||||
else:
|
||||
self._sort_image_by_use_date(image_list, 0, len(image_list) - 1)
|
||||
for img in image_list:
|
||||
image_name_xcat = img[0]
|
||||
image_profile = self._get_image_href_by_osimage(
|
||||
image_name_xcat)
|
||||
image_uuid = image_profile.partition('_')[2].replace("_", "-")
|
||||
image_size = self._get_image_size_glance(context,
|
||||
image_uuid)
|
||||
if image_size > 0:
|
||||
to_be_deleted_image_profile.append(image_profile)
|
||||
size_sum += image_size
|
||||
if size_sum >= size_needed:
|
||||
return to_be_deleted_image_profile
|
||||
|
||||
if size_sum < current_needed:
|
||||
msg = _("xCAT MN space not enough for current image operation: "
|
||||
"%(n)d G needed,%(a)d G available") % {'n': current_needed,
|
||||
'a': size_sum}
|
||||
LOG.warning(msg)
|
||||
|
||||
return to_be_deleted_image_profile
|
||||
|
||||
def prune_image_xcat(self, context, size_needed, current_needed):
|
||||
"""Remove the images which meet remove criteria from xCAT."""
|
||||
LOG.debug("Clear up space by clean images in xCAT")
|
||||
to_be_deleted_image_profile = self._get_to_be_deleted_images_xcat(
|
||||
context, size_needed, current_needed)
|
||||
if len(to_be_deleted_image_profile) > 0:
|
||||
for image_profile in to_be_deleted_image_profile:
|
||||
image_name_xcat = self.get_imgname_xcat(image_profile)
|
||||
self.delete_image_from_xcat(image_name_xcat)
|
||||
|
||||
def zimage_check(self, image_meta):
|
||||
"""Do a brief check to see if the image is a valid zVM image."""
|
||||
property_ = ['image_file_name', 'image_type_xcat', 'architecture',
|
||||
'os_name', 'provisioning_method', 'os_version']
|
||||
missing_prop = []
|
||||
for prop in property_:
|
||||
if prop not in list(image_meta['properties'].keys()):
|
||||
missing_prop.append(prop)
|
||||
|
||||
if len(missing_prop) > 0:
|
||||
msg = (_("The image %(id)s is not a valid zVM image, "
|
||||
"property %(prop)s are missing") % {'id': image_meta['id'],
|
||||
'prop': missing_prop})
|
||||
LOG.error(msg)
|
||||
raise nova_exception.ImageUnacceptable(image_id=image_meta['id'],
|
||||
reason=msg)
|
||||
|
||||
def cleanup_image_after_migration(self, inst_name):
|
||||
"""Cleanup osimages in xCAT image repository while confirm migration
|
||||
or revert migration at source compute.
|
||||
"""
|
||||
image_list = self._get_image_list_xcat()
|
||||
matchee = ''.join(['rsz', inst_name])
|
||||
for img in image_list:
|
||||
img_name = img[0]
|
||||
if matchee in img_name:
|
||||
self.delete_image_from_xcat(img_name)
|
||||
|
||||
def get_root_disk_units(self, image_file_path):
|
||||
"""use 'hexdump' to get the root_disk_units."""
|
||||
try:
|
||||
(output, _toss) = utils.execute('hexdump', '-n', '48',
|
||||
'-C', image_file_path)
|
||||
except processutils.ProcessExecutionError:
|
||||
msg = (_("Get image property failed,"
|
||||
" please check whether the image file exists!"))
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
LOG.debug("hexdump result is %s", output)
|
||||
try:
|
||||
root_disk_size = int(output[144:156])
|
||||
disk_units = output[220:223]
|
||||
root_disk_units = ':'.join([str(root_disk_size), disk_units])
|
||||
except ValueError:
|
||||
msg = (_("Image file at %s is missing built-in disk size "
|
||||
"metadata, it was probably not captured with xCAT")
|
||||
% image_file_path)
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
if 'FBA' not in output and 'CKD' not in output:
|
||||
msg = (_("The image's disk type is not valid. Currently we only"
|
||||
" support FBA and CKD disk"))
|
||||
raise exception.ZVMImageError(msg=msg)
|
||||
|
||||
LOG.debug("The image's root_disk_units is %s", root_disk_units)
|
||||
return root_disk_units
|
||||
|
||||
def set_image_root_disk_units(self, context, image_meta, image_file_path):
|
||||
"""Set the property 'root_disk_units'to image."""
|
||||
new_image_meta = image_meta
|
||||
root_disk_units = self.get_root_disk_units(image_file_path)
|
||||
LOG.debug("The image's root_disk_units is %s", root_disk_units)
|
||||
|
||||
(glance_image_service, image_id) = glance.get_remote_image_service(
|
||||
context, image_meta['id'])
|
||||
new_image_meta = glance_image_service.show(context, image_id)
|
||||
new_image_meta['properties']['root_disk_units'] = root_disk_units
|
||||
|
||||
try:
|
||||
glance_image_service.update(context, image_id,
|
||||
new_image_meta, None)
|
||||
except nova_exception.ImageNotAuthorized:
|
||||
msg = _('Not allowed to modify attributes for image %s') % image_id
|
||||
LOG.info(msg)
|
||||
|
||||
return new_image_meta
|
||||
|
||||
def get_image_menifest(self, image_name_xcat):
|
||||
"""Get image manifest info from xcat osimage table."""
|
||||
attr_list = ['imagetype', 'osarch', 'imagename', 'osname',
|
||||
'osvers', 'profile', 'provmethod']
|
||||
menifest = zvmutils.xcat_cmd_gettab_multi_attr('osimage', 'imagename',
|
||||
image_name_xcat, attr_list)
|
||||
return menifest
|
@ -1,909 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import binascii
|
||||
import six
|
||||
import time
|
||||
|
||||
from nova.compute import power_state
|
||||
from nova import exception as nova_exception
|
||||
from nova.i18n import _
|
||||
from nova.virt import hardware
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova_zvm.virt.zvm import const
|
||||
from nova_zvm.virt.zvm import dist
|
||||
from nova_zvm.virt.zvm import exception
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
from nova_zvm.virt.zvm import volumeop
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
CONF.import_opt('default_ephemeral_format', 'nova.conf')
|
||||
|
||||
|
||||
class CopiedInstance(object):
|
||||
_KWS = ('name', 'image_ref', 'uuid', 'user_id', 'project_id',
|
||||
'power_state', 'system_metadata', 'memory_mb', 'vcpus',
|
||||
'root_gb', 'ephemeral_gb')
|
||||
|
||||
def __init__(self, inst_obj):
|
||||
"""Dictionary compatible instance object for zVM driver internal use.
|
||||
|
||||
:inst_obj: instance object of nova.objects.instance.Instance.
|
||||
"""
|
||||
for k in self._KWS:
|
||||
if inst_obj.get(k, None):
|
||||
setattr(self, k, inst_obj[k])
|
||||
|
||||
def get(self, name, default=None):
|
||||
return getattr(self, name, default)
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
setattr(self, name, value)
|
||||
|
||||
def __getitem__(self, name):
|
||||
return getattr(self, name, None)
|
||||
|
||||
|
||||
class ZVMInstance(object):
|
||||
'''OpenStack instance that running on of z/VM hypervisor.'''
|
||||
|
||||
def __init__(self, driver, instance=None):
|
||||
"""Initialize instance attributes for database."""
|
||||
instance = instance or {}
|
||||
self._xcat_url = zvmutils.get_xcat_url()
|
||||
self._instance = instance
|
||||
self._name = instance['name']
|
||||
self._volumeop = volumeop.VolumeOperator()
|
||||
self._dist_manager = dist.ListDistManager()
|
||||
self._driver = driver
|
||||
|
||||
def power_off(self, timeout=0, retry_interval=10):
|
||||
"""Power off z/VM instance."""
|
||||
try:
|
||||
self._power_state("PUT", "softoff")
|
||||
except exception.ZVMXCATInternalError as err:
|
||||
err_str = err.format_message()
|
||||
if ("Return Code: 200" in err_str and
|
||||
"Reason Code: 12" in err_str):
|
||||
# Instance already not active
|
||||
LOG.warning(_("z/VM instance %s not active"), self._name)
|
||||
return
|
||||
else:
|
||||
msg = _("Failed to power off instance: %s") % err_str
|
||||
LOG.error(msg)
|
||||
raise nova_exception.InstancePowerOffFailure(reason=msg)
|
||||
|
||||
timeout = timeout or CONF.shutdown_timeout
|
||||
retry_interval = retry_interval or 10
|
||||
retry_count = timeout // retry_interval
|
||||
while (retry_count > 0):
|
||||
if self._check_power_stat() == power_state.SHUTDOWN:
|
||||
# In shutdown state already
|
||||
return
|
||||
else:
|
||||
time.sleep(retry_interval)
|
||||
retry_count -= 1
|
||||
|
||||
LOG.warning(_("Failed to shutdown instance %(inst)s in %(time)d "
|
||||
"seconds"), {'inst': self._name, 'time': timeout})
|
||||
|
||||
def power_on(self):
|
||||
""""Power on z/VM instance."""
|
||||
try:
|
||||
self._power_state("PUT", "on")
|
||||
except exception.ZVMXCATInternalError as err:
|
||||
err_str = err.format_message()
|
||||
if ("Return Code: 200" in err_str and
|
||||
"Reason Code: 8" in err_str):
|
||||
# Instance already active
|
||||
LOG.warning(_("z/VM instance %s already active"), self._name)
|
||||
return
|
||||
raise nova_exception.InstancePowerOnFailure(reason=err_str)
|
||||
|
||||
self._wait_for_reachable()
|
||||
if not self._reachable:
|
||||
LOG.error(_("Failed to power on instance %s: timeout"), self._name)
|
||||
raise nova_exception.InstancePowerOnFailure(reason="timeout")
|
||||
|
||||
def is_powered_off(self):
|
||||
"""Return True if the instance is powered off."""
|
||||
return self._check_power_stat() == power_state.SHUTDOWN
|
||||
|
||||
def reset(self):
|
||||
"""Hard reboot z/VM instance."""
|
||||
try:
|
||||
self._power_state("PUT", "reset")
|
||||
except exception.ZVMXCATInternalError as err:
|
||||
err_str = err.format_message()
|
||||
if ("Return Code: 200" in err_str and
|
||||
"Reason Code: 12" in err_str):
|
||||
# Be able to reset in power state of SHUTDOWN
|
||||
LOG.warning(_("Reset z/VM instance %s from SHUTDOWN state"),
|
||||
self._name)
|
||||
return
|
||||
else:
|
||||
raise err
|
||||
self._wait_for_reachable()
|
||||
|
||||
def reboot(self):
|
||||
"""Soft reboot z/VM instance."""
|
||||
self._power_state("PUT", "reboot")
|
||||
self._wait_for_reachable()
|
||||
|
||||
def pause(self):
|
||||
"""Pause the z/VM instance."""
|
||||
self._power_state("PUT", "pause")
|
||||
|
||||
def unpause(self):
|
||||
"""Unpause the z/VM instance."""
|
||||
self._power_state("PUT", "unpause")
|
||||
self._wait_for_reachable()
|
||||
|
||||
def attach_volume(self, volumeop, context, connection_info, instance,
|
||||
mountpoint, is_active, rollback=True):
|
||||
volumeop.attach_volume_to_instance(context, connection_info,
|
||||
instance, mountpoint,
|
||||
is_active, rollback)
|
||||
|
||||
def detach_volume(self, volumeop, connection_info, instance, mountpoint,
|
||||
is_active, rollback=True):
|
||||
volumeop.detach_volume_from_instance(connection_info,
|
||||
instance, mountpoint,
|
||||
is_active, rollback)
|
||||
|
||||
def get_info(self):
|
||||
cpumempowerstat_version = const.XCAT_RINV_SUPPORT_CPUMEMPOWERSTAT
|
||||
# new version has cpumempowerstat support in order gain performance
|
||||
if self._driver.has_min_version(cpumempowerstat_version):
|
||||
return self._get_info_cpumempowerstat()
|
||||
else:
|
||||
return self._get_info_cpumem()
|
||||
|
||||
def _get_info_cpumempowerstat(self):
|
||||
"""Get current status of an z/VM instance through cpumempowerstat."""
|
||||
_instance_info = hardware.InstanceInfo()
|
||||
max_mem_kb = int(self._instance['memory_mb']) * 1024
|
||||
|
||||
try:
|
||||
rec_list = self._get_rinv_info('cpumempowerstat')
|
||||
except exception.ZVMXCATInternalError:
|
||||
raise nova_exception.InstanceNotFound(instance_id=self._name)
|
||||
|
||||
mem = self._get_current_memory(rec_list)
|
||||
num_cpu = self._get_guest_cpus(rec_list)
|
||||
cpu_time = self._get_cpu_used_time(rec_list)
|
||||
power_stat = self._get_power_stat(rec_list)
|
||||
|
||||
if ((power_stat == power_state.RUNNING) and
|
||||
(self._instance['power_state'] == power_state.PAUSED)):
|
||||
# return paused state only previous power state is paused
|
||||
_instance_info.state = power_state.PAUSED
|
||||
else:
|
||||
_instance_info.state = power_stat
|
||||
|
||||
# TODO(jichenjc): set max mem through SMAPI result
|
||||
_instance_info.max_mem_kb = max_mem_kb
|
||||
_instance_info.mem_kb = mem
|
||||
_instance_info.num_cpu = num_cpu
|
||||
_instance_info.cpu_time_ns = cpu_time
|
||||
return _instance_info
|
||||
|
||||
def _get_info_cpumem(self):
|
||||
"""Get current status of an z/VM instance through cpumem."""
|
||||
_instance_info = hardware.InstanceInfo()
|
||||
|
||||
power_stat = self._check_power_stat()
|
||||
is_reachable = self.is_reachable()
|
||||
|
||||
max_mem_kb = int(self._instance['memory_mb']) * 1024
|
||||
if is_reachable:
|
||||
try:
|
||||
rec_list = self._get_rinv_info('cpumem')
|
||||
except exception.ZVMXCATInternalError:
|
||||
raise nova_exception.InstanceNotFound(instance_id=self._name)
|
||||
|
||||
try:
|
||||
mem = self._get_current_memory(rec_list)
|
||||
num_cpu = self._get_cpu_count(rec_list)
|
||||
cpu_time = self._get_cpu_used_time(rec_list)
|
||||
_instance_info.state = power_stat
|
||||
_instance_info.max_mem_kb = max_mem_kb
|
||||
_instance_info.mem_kb = mem
|
||||
_instance_info.num_cpu = num_cpu
|
||||
_instance_info.cpu_time_ns = cpu_time
|
||||
|
||||
except exception.ZVMInvalidXCATResponseDataError:
|
||||
LOG.warning(_("Failed to get inventory info for %s"),
|
||||
self._name)
|
||||
_instance_info.state = power_stat
|
||||
_instance_info.max_mem_kb = max_mem_kb
|
||||
_instance_info.mem_kb = max_mem_kb
|
||||
_instance_info.num_cpu = self._instance['vcpus']
|
||||
_instance_info.cpu_time_ns = 0
|
||||
|
||||
else:
|
||||
# Since xCAT rinv can't get info from a server that in power state
|
||||
# of SHUTDOWN or PAUSED
|
||||
if ((power_stat == power_state.RUNNING) and
|
||||
(self._instance['power_state'] == power_state.PAUSED)):
|
||||
# return paused state only previous power state is paused
|
||||
_instance_info.state = power_state.PAUSED
|
||||
_instance_info.max_mem_kb = max_mem_kb
|
||||
_instance_info.mem_kb = max_mem_kb
|
||||
_instance_info.num_cpu = self._instance['vcpus']
|
||||
_instance_info.cpu_time_ns = 0
|
||||
else:
|
||||
# otherwise return xcat returned state
|
||||
_instance_info.state = power_stat
|
||||
_instance_info.max_mem_kb = max_mem_kb
|
||||
_instance_info.mem_kb = 0
|
||||
_instance_info.num_cpu = self._instance['vcpus']
|
||||
_instance_info.cpu_time_ns = 0
|
||||
return _instance_info
|
||||
|
||||
def create_xcat_node(self, zhcp, userid=None):
|
||||
"""Create xCAT node for z/VM instance."""
|
||||
LOG.debug("Creating xCAT node for %s", self._name)
|
||||
|
||||
user_id = userid or self._name
|
||||
body = ['userid=%s' % user_id,
|
||||
'hcp=%s' % zhcp,
|
||||
'mgt=zvm',
|
||||
'groups=%s' % CONF.zvm_xcat_group]
|
||||
url = self._xcat_url.mkdef('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATCreateNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("POST", url, body)
|
||||
|
||||
def _create_user_id_body(self, boot_from_volume):
|
||||
kwprofile = 'profile=%s' % CONF.zvm_user_profile
|
||||
body = [kwprofile,
|
||||
'cpu=%i' % self._instance['vcpus'],
|
||||
'memory=%im' % self._instance['memory_mb'],
|
||||
'privilege=%s' % CONF.zvm_user_default_privilege]
|
||||
|
||||
# if we don't set admin userid, then we will used old password
|
||||
# field, otherwise, will use logon by method.
|
||||
if not CONF.zvm_default_admin_userid:
|
||||
body.append('password=%s' % CONF.zvm_user_default_password)
|
||||
else:
|
||||
body.append('password=LBYONLY')
|
||||
body.append('logonby=%s' % CONF.zvm_default_admin_userid)
|
||||
|
||||
# if mkvm in lower version xcat won't support it
|
||||
# they will ignore this param.
|
||||
if not boot_from_volume:
|
||||
body.append('ipl=%s' % CONF.zvm_user_root_vdev)
|
||||
|
||||
return body
|
||||
|
||||
def _check_set_ipl(self):
|
||||
xcat_version = self._driver._xcat_version
|
||||
|
||||
if not zvmutils.xcat_support_mkvm_ipl_param(xcat_version):
|
||||
self._set_ipl(CONF.zvm_user_root_vdev)
|
||||
|
||||
def create_userid(self, block_device_info, image_meta, context,
|
||||
os_image=None):
|
||||
"""Create z/VM userid into user directory for a z/VM instance."""
|
||||
# We do not support boot from volume currently
|
||||
LOG.debug("Creating the z/VM user entry for instance %s", self._name)
|
||||
|
||||
boot_from_volume = zvmutils.is_boot_from_volume(block_device_info)[1]
|
||||
|
||||
eph_disks = block_device_info.get('ephemerals', [])
|
||||
|
||||
body = self._create_user_id_body(boot_from_volume)
|
||||
|
||||
if not boot_from_volume:
|
||||
# image_meta passed from spawn is a dict, in resize is a object
|
||||
if isinstance(image_meta, dict):
|
||||
if 'name' in image_meta.keys():
|
||||
kwimage = 'imagename=%s' % image_meta['name']
|
||||
body.append(kwimage)
|
||||
else:
|
||||
image_name = getattr(image_meta, 'name')
|
||||
if image_name:
|
||||
kwimage = 'imagename=%s' % image_name
|
||||
body.append(kwimage)
|
||||
|
||||
if os_image:
|
||||
kwimage = 'osimage=%s' % os_image
|
||||
body.append(kwimage)
|
||||
|
||||
# Versions of xCAT that do not understand the instance ID and
|
||||
# request ID will silently ignore them.
|
||||
url = self._xcat_url.mkvm('/' + self._name, self._instance.uuid,
|
||||
context)
|
||||
|
||||
# Note: driver.py:spawn() has already checked that the root disk units
|
||||
# and the type of disks in the pool are compatible.
|
||||
try:
|
||||
zvmutils.xcat_request("POST", url, body)
|
||||
|
||||
if not boot_from_volume:
|
||||
size = '%ig' % self._instance['root_gb']
|
||||
# If the flavor specifies 0 for the root disk size, use the
|
||||
# size in the image's metadata
|
||||
if size == '0g':
|
||||
root_disk_units = image_meta['properties'][
|
||||
'root_disk_units']
|
||||
size = root_disk_units.split(":")[0]
|
||||
|
||||
# Add root disk and set ipl
|
||||
self.add_mdisk(CONF.zvm_diskpool,
|
||||
CONF.zvm_user_root_vdev,
|
||||
size)
|
||||
self._check_set_ipl()
|
||||
|
||||
# Add additional ephemeral disk
|
||||
if self._instance['ephemeral_gb'] != 0:
|
||||
if eph_disks == []:
|
||||
# Create ephemeral disk according to flavor
|
||||
fmt = (CONF.default_ephemeral_format or
|
||||
const.DEFAULT_EPH_DISK_FMT)
|
||||
self.add_mdisk(CONF.zvm_diskpool,
|
||||
CONF.zvm_user_adde_vdev,
|
||||
'%ig' % self._instance['ephemeral_gb'],
|
||||
fmt)
|
||||
else:
|
||||
# Create ephemeral disks according --ephemeral option
|
||||
for idx, eph in enumerate(eph_disks):
|
||||
vdev = (eph.get('vdev') or
|
||||
zvmutils.generate_eph_vdev(idx))
|
||||
size = eph['size']
|
||||
size_in_units = eph.get('size_in_units', False)
|
||||
if not size_in_units:
|
||||
size = '%ig' % size
|
||||
fmt = (eph.get('guest_format') or
|
||||
CONF.default_ephemeral_format or
|
||||
const.DEFAULT_EPH_DISK_FMT)
|
||||
self.add_mdisk(CONF.zvm_diskpool, vdev, size, fmt)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError,
|
||||
exception.ZVMDriverError) as err:
|
||||
msg = _("Failed to create z/VM userid: %s") % err.format_message()
|
||||
LOG.error(msg)
|
||||
raise exception.ZVMXCATCreateUserIdFailed(instance=self._name,
|
||||
msg=msg)
|
||||
|
||||
def prepare_volume_boot(self, context, instance, block_device_mapping,
|
||||
root_device, volume_meta):
|
||||
try:
|
||||
connection_info = self._volumeop.get_root_volume_connection_info(
|
||||
block_device_mapping, root_device)
|
||||
(lun, wwpn, size,
|
||||
fcp) = self._volumeop.extract_connection_info(context,
|
||||
connection_info)
|
||||
if len(wwpn) > 16:
|
||||
# a wwpn list similar to:
|
||||
# 5005076800aa0001;5005076800aa0002;5005076800aa0003
|
||||
wwpn = wwpn.split(';')[0]
|
||||
|
||||
(kernel_parm_string, scpdata) = self._forge_hex_scpdata(fcp,
|
||||
wwpn, lun, volume_meta)
|
||||
|
||||
loaddev_str = "%(wwpn)s %(lun)s 1 %(scpdata)s" % {'wwpn': wwpn,
|
||||
'lun': lun, 'scpdata': scpdata}
|
||||
|
||||
self._volumeop.volume_boot_init(instance, fcp)
|
||||
self._set_ipl(fcp)
|
||||
self._set_loaddev(loaddev_str)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError,
|
||||
exception.ZVMVolumeError,
|
||||
exception.ZVMDriverError) as err:
|
||||
msg = _("Failed to prepare volume to boot") % err.format_message()
|
||||
LOG.error(msg)
|
||||
raise exception.ZVMVolumeError(msg=msg)
|
||||
|
||||
return (lun, wwpn, size, fcp)
|
||||
|
||||
def clean_volume_boot(self, context, instance, block_device_mapping,
|
||||
root_device):
|
||||
try:
|
||||
connection_info = self._volumeop.get_root_volume_connection_info(
|
||||
block_device_mapping, root_device)
|
||||
(lun, wwpn, size,
|
||||
fcp) = self._volumeop.extract_connection_info(context,
|
||||
connection_info)
|
||||
self._volumeop.volume_boot_cleanup(instance, fcp)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError,
|
||||
exception.ZVMVolumeError,
|
||||
exception.ZVMDriverError) as err:
|
||||
emsg = err.format_message()
|
||||
msg = _("Failed to clean boot from volume "
|
||||
"preparations: %s") % emsg
|
||||
LOG.warning(msg)
|
||||
raise exception.ZVMVolumeError(msg=msg)
|
||||
|
||||
def _forge_hex_scpdata(self, fcp, wwpn, lun, volume_meta):
|
||||
"""Forge scpdata in string form and HEX form."""
|
||||
root = volume_meta['root']
|
||||
os_version = volume_meta['os_version']
|
||||
linux_dist = self._dist_manager.get_linux_dist(os_version)()
|
||||
scp_string = linux_dist.get_scp_string(root, fcp, wwpn, lun)
|
||||
|
||||
# Convert to HEX string
|
||||
# Without Encode / Decode it will still work for python 2.6 but not for
|
||||
# Python 3
|
||||
try:
|
||||
scp_string_ascii = scp_string.encode('ascii')
|
||||
scp_string_hex = binascii.hexlify(scp_string_ascii)
|
||||
scp_data = scp_string_hex.decode('ascii')
|
||||
except Exception as err:
|
||||
errmsg = _("Failed to forge hex scpdata: %s") % err
|
||||
LOG.error(errmsg)
|
||||
raise exception.ZVMDriverError(msg=errmsg)
|
||||
|
||||
return (scp_string, scp_data)
|
||||
|
||||
def _set_ipl(self, ipl_state):
|
||||
body = ["--setipl %s" % ipl_state]
|
||||
url = self._xcat_url.chvm('/' + self._name)
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def _set_loaddev(self, loaddev):
|
||||
body = ["--setloaddev %s" % loaddev]
|
||||
url = self._xcat_url.chvm('/' + self._name)
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def get_userid(self):
|
||||
return zvmutils.get_userid(self._name)
|
||||
|
||||
def unlock_userid(self, zhcp_node):
|
||||
_uid = self.get_userid()
|
||||
cmd = "/opt/zhcp/bin/smcli Image_Unlock_DM -T %s" % _uid
|
||||
zvmutils.xdsh(zhcp_node, cmd)
|
||||
|
||||
def unlock_devices(self, zhcp_node):
|
||||
_uid = self.get_userid()
|
||||
cmd = "/opt/zhcp/bin/smcli Image_Lock_Query_DM -T %s" % _uid
|
||||
resp = zvmutils.xdsh(zhcp_node, cmd)
|
||||
with zvmutils.expect_invalid_xcat_resp_data(resp):
|
||||
resp_str = resp['data'][0][0]
|
||||
|
||||
if resp_str.__contains__("is Unlocked..."):
|
||||
# unlocked automatically, do nothing
|
||||
return
|
||||
|
||||
def _unlock_device(vdev):
|
||||
cmd = ("/opt/zhcp/bin/smcli Image_Unlock_DM -T %(uid)s -v %(vdev)s"
|
||||
% {'uid': _uid, 'vdev': vdev})
|
||||
zvmutils.xdsh(zhcp_node, cmd)
|
||||
|
||||
resp_list = resp_str.split('\n')
|
||||
for s in resp_list:
|
||||
if s.__contains__('Device address:'):
|
||||
vdev = s.rpartition(':')[2].strip()
|
||||
_unlock_device(vdev)
|
||||
|
||||
def _delete_userid(self, url):
|
||||
try:
|
||||
zvmutils.xcat_request("DELETE", url)
|
||||
except exception.ZVMXCATInternalError as err:
|
||||
emsg = err.format_message()
|
||||
LOG.debug("error emsg in delete_userid: %s", emsg)
|
||||
if (emsg.__contains__("Return Code: 400") and
|
||||
emsg.__contains__("Reason Code: 4")):
|
||||
# zVM user definition not found, delete xCAT node directly
|
||||
self.delete_xcat_node()
|
||||
else:
|
||||
raise
|
||||
|
||||
def delete_userid(self, zhcp_node, context):
|
||||
"""Delete z/VM userid for the instance.This will remove xCAT node
|
||||
at same time.
|
||||
"""
|
||||
# Versions of xCAT that do not understand the instance ID and
|
||||
# request ID will silently ignore them.
|
||||
url = self._xcat_url.rmvm('/' + self._name, self._instance.uuid,
|
||||
context)
|
||||
|
||||
try:
|
||||
self._delete_userid(url)
|
||||
except exception.ZVMXCATInternalError as err:
|
||||
emsg = err.format_message()
|
||||
if (emsg.__contains__("Return Code: 400") and
|
||||
emsg.__contains__("Reason Code: 12")):
|
||||
# The vm was locked. Unlock before deleting
|
||||
self.unlock_userid(zhcp_node)
|
||||
elif (emsg.__contains__("Return Code: 408") and
|
||||
emsg.__contains__("Reason Code: 12")):
|
||||
# The vm device was locked. Unlock the device before deleting
|
||||
self.unlock_devices(zhcp_node)
|
||||
else:
|
||||
LOG.debug("exception not able to handle in delete_userid "
|
||||
"%s", self._name)
|
||||
raise err
|
||||
# delete the vm after unlock
|
||||
self._delete_userid(url)
|
||||
except exception.ZVMXCATRequestFailed as err:
|
||||
emsg = err.format_message()
|
||||
if (emsg.__contains__("Invalid nodes and/or groups") and
|
||||
emsg.__contains__("Forbidden")):
|
||||
# Assume neither zVM userid nor xCAT node exist in this case
|
||||
return
|
||||
else:
|
||||
raise err
|
||||
|
||||
def delete_xcat_node(self):
|
||||
"""Remove xCAT node for z/VM instance."""
|
||||
url = self._xcat_url.rmdef('/' + self._name)
|
||||
try:
|
||||
zvmutils.xcat_request("DELETE", url)
|
||||
except exception.ZVMXCATInternalError as err:
|
||||
if err.format_message().__contains__("Could not find an object"):
|
||||
# The xCAT node not exist
|
||||
return
|
||||
else:
|
||||
raise err
|
||||
|
||||
def add_mdisk(self, diskpool, vdev, size, fmt=None):
|
||||
"""Add a 3390 mdisk for a z/VM user.
|
||||
|
||||
NOTE: No read, write and multi password specified, and
|
||||
access mode default as 'MR'.
|
||||
|
||||
"""
|
||||
disk_type = CONF.zvm_diskpool_type
|
||||
if (disk_type == 'ECKD'):
|
||||
action = '--add3390'
|
||||
elif (disk_type == 'FBA'):
|
||||
action = '--add9336'
|
||||
else:
|
||||
errmsg = _("Disk type %s is not supported.") % disk_type
|
||||
LOG.error(errmsg)
|
||||
raise exception.ZVMDriverError(msg=errmsg)
|
||||
|
||||
if fmt:
|
||||
body = [" ".join([action, diskpool, vdev, size, "MR", "''", "''",
|
||||
"''", fmt])]
|
||||
else:
|
||||
body = [" ".join([action, diskpool, vdev, size])]
|
||||
url = self._xcat_url.chvm('/' + self._name)
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def _power_state(self, method, state):
|
||||
"""Invoke xCAT REST API to set/get power state for a instance."""
|
||||
body = [state]
|
||||
url = self._xcat_url.rpower('/' + self._name)
|
||||
return zvmutils.xcat_request(method, url, body)
|
||||
|
||||
def _check_power_stat(self):
|
||||
"""Get power status of a z/VM instance."""
|
||||
LOG.debug('Query power stat of %s', self._name)
|
||||
res_dict = self._power_state("GET", "stat")
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _get_power_string(d):
|
||||
tempstr = d['info'][0][0]
|
||||
return tempstr[(tempstr.find(':') + 2):].strip()
|
||||
|
||||
power_stat = _get_power_string(res_dict)
|
||||
return zvmutils.mapping_power_stat(power_stat)
|
||||
|
||||
def _get_rinv_info(self, command):
|
||||
"""get rinv result and return in a list."""
|
||||
field = '&field=%s' % command
|
||||
url = self._xcat_url.rinv('/' + self._name, field)
|
||||
LOG.debug('Remote inventory of %s', self._name)
|
||||
res_info = zvmutils.xcat_request("GET", url)['info']
|
||||
|
||||
with zvmutils.expect_invalid_xcat_resp_data(res_info):
|
||||
rinv_info = res_info[0][0].split('\n')
|
||||
|
||||
return rinv_info
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _modify_storage_format(self, mem):
|
||||
"""modify storage from 'G' ' M' to 'K'."""
|
||||
# Only special case for 0
|
||||
if mem == '0':
|
||||
return 0
|
||||
|
||||
new_mem = 0
|
||||
if mem.endswith('G'):
|
||||
new_mem = int(mem[:-1]) * 1024 * 1024
|
||||
elif mem.endswith('M'):
|
||||
new_mem = int(mem[:-1]) * 1024
|
||||
elif mem.endswith('K'):
|
||||
new_mem = int(mem[:-1])
|
||||
else:
|
||||
exp = "ending with a 'G', 'M' or 'K'"
|
||||
errmsg = _("Invalid memory format: %(invalid)s; Expected: "
|
||||
"%(exp)s") % {'invalid': mem, 'exp': exp}
|
||||
LOG.error(errmsg)
|
||||
raise exception.ZVMInvalidXCATResponseDataError(msg=errmsg)
|
||||
return new_mem
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _get_current_memory(self, rec_list):
|
||||
"""Return the max memory can be used."""
|
||||
_mem = None
|
||||
|
||||
for rec in rec_list:
|
||||
if rec.__contains__("Total Memory: "):
|
||||
tmp_list = rec.split()
|
||||
_mem = tmp_list[3]
|
||||
|
||||
_mem = self._modify_storage_format(_mem)
|
||||
return _mem
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _get_cpu_count(self, rec_list):
|
||||
"""Return the virtual cpu count."""
|
||||
_cpu_flag = False
|
||||
num_cpu = 0
|
||||
|
||||
for rec in rec_list:
|
||||
if (_cpu_flag is True):
|
||||
tmp_list = rec.split()
|
||||
if (len(tmp_list) > 1):
|
||||
if (tmp_list[1] == "CPU"):
|
||||
num_cpu += 1
|
||||
else:
|
||||
_cpu_flag = False
|
||||
if rec.__contains__("Processors: "):
|
||||
_cpu_flag = True
|
||||
|
||||
return num_cpu
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _get_cpu_used_time(self, rec_list):
|
||||
"""Return the cpu used time in."""
|
||||
cpu_time = 0
|
||||
|
||||
for rec in rec_list:
|
||||
if rec.__contains__("CPU Used Time: "):
|
||||
tmp_list = rec.split()
|
||||
cpu_time = tmp_list[4]
|
||||
|
||||
return float(cpu_time)
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _get_guest_cpus(self, rec_list):
|
||||
"""Return the processer count, used by cpumempowerstat"""
|
||||
guest_cpus = 0
|
||||
|
||||
for rec in rec_list:
|
||||
if rec.__contains__("Guest CPUs: "):
|
||||
tmp_list = rec.split()
|
||||
guest_cpus = tmp_list[3]
|
||||
|
||||
return int(guest_cpus)
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def _get_power_stat(self, rec_list):
|
||||
"""Return the power stat, used by cpumempowerstat"""
|
||||
power_stat = None
|
||||
|
||||
for rec in rec_list:
|
||||
if rec.__contains__("Power state: "):
|
||||
tmp_list = rec.split()
|
||||
power_stat = tmp_list[3]
|
||||
|
||||
return zvmutils.mapping_power_stat(power_stat)
|
||||
|
||||
@zvmutils.wrap_invalid_xcat_resp_data_error
|
||||
def is_reachable(self):
|
||||
"""Check whether IUCV connection works well."""
|
||||
if zvmutils.xcat_support_iucv(self._driver._xcat_version):
|
||||
LOG.debug("Check whether VM %s is reachable.", self._name)
|
||||
result = self._power_state("PUT", "isreachable")
|
||||
if ': reachable' in result['info'][0][0]:
|
||||
|
||||
return True
|
||||
else:
|
||||
url = self._xcat_url.nodestat('/' + self._name)
|
||||
LOG.debug('Get instance status of %s', self._name)
|
||||
res_dict = zvmutils.xcat_request("GET", url)
|
||||
|
||||
with zvmutils.expect_invalid_xcat_resp_data():
|
||||
status = res_dict['node'][0][0]['data'][0]
|
||||
|
||||
if status is not None:
|
||||
if status.__contains__('sshd'):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _wait_for_reachable(self):
|
||||
"""Called at an interval until the instance is reachable."""
|
||||
self._reachable = False
|
||||
|
||||
def _check_reachable():
|
||||
if not self.is_reachable():
|
||||
raise exception.ZVMRetryException()
|
||||
else:
|
||||
self._reachable = True
|
||||
|
||||
zvmutils.looping_call(_check_reachable, 5, 5, 30,
|
||||
CONF.zvm_reachable_timeout,
|
||||
exception.ZVMRetryException)
|
||||
|
||||
def update_node_info(self, image_meta):
|
||||
LOG.debug("Update the node info for instance %s", self._name)
|
||||
|
||||
image_name = ''.join(i for i in image_meta['name'] if i.isalnum())
|
||||
image_id = image_meta['id']
|
||||
os_type = image_meta['properties']['os_version']
|
||||
os_arch = image_meta['properties']['architecture']
|
||||
prov_method = image_meta['properties']['provisioning_method']
|
||||
profile_name = '_'.join((image_name, image_id.replace('-', '_')))
|
||||
|
||||
body = ['noderes.netboot=%s' % const.HYPERVISOR_TYPE,
|
||||
'nodetype.os=%s' % os_type,
|
||||
'nodetype.arch=%s' % os_arch,
|
||||
'nodetype.provmethod=%s' % prov_method,
|
||||
'nodetype.profile=%s' % profile_name]
|
||||
url = self._xcat_url.chtab('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATUpdateNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def update_node_info_resize(self, image_name_xcat):
|
||||
LOG.debug("Update the nodetype for instance %s", self._name)
|
||||
|
||||
name_section = image_name_xcat.split("-")
|
||||
os_type = name_section[0]
|
||||
os_arch = name_section[1]
|
||||
profile_name = name_section[3]
|
||||
|
||||
body = ['noderes.netboot=%s' % const.HYPERVISOR_TYPE,
|
||||
'nodetype.os=%s' % os_type,
|
||||
'nodetype.arch=%s' % os_arch,
|
||||
'nodetype.provmethod=%s' % 'sysclone',
|
||||
'nodetype.profile=%s' % profile_name]
|
||||
|
||||
url = self._xcat_url.chtab('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATUpdateNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def get_provmethod(self):
|
||||
addp = "&col=node=%s&attribute=provmethod" % self._name
|
||||
url = self._xcat_url.gettab('/nodetype', addp)
|
||||
res_info = zvmutils.xcat_request("GET", url)
|
||||
return res_info['data'][0][0]
|
||||
|
||||
def update_node_provmethod(self, provmethod):
|
||||
LOG.debug("Update the nodetype for instance %s", self._name)
|
||||
|
||||
body = ['nodetype.provmethod=%s' % provmethod]
|
||||
|
||||
url = self._xcat_url.chtab('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATUpdateNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def update_node_def(self, hcp, userid):
|
||||
"""Update xCAT node definition."""
|
||||
|
||||
body = ['zvm.hcp=%s' % hcp,
|
||||
'zvm.userid=%s' % userid]
|
||||
url = self._xcat_url.chtab('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATUpdateNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def deploy_node(self, image_name, transportfiles=None, vdev=None):
|
||||
LOG.debug("Begin to deploy image on instance %s", self._name)
|
||||
vdev = vdev or CONF.zvm_user_root_vdev
|
||||
remote_host_info = zvmutils.get_host()
|
||||
body = ['netboot',
|
||||
'device=%s' % vdev,
|
||||
'osimage=%s' % image_name]
|
||||
|
||||
if transportfiles:
|
||||
body.append('transport=%s' % transportfiles)
|
||||
body.append('remotehost=%s' % remote_host_info)
|
||||
|
||||
url = self._xcat_url.nodeset('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATDeployNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("PUT", url, body)
|
||||
|
||||
def copy_xcat_node(self, source_node_name):
|
||||
"""Create xCAT node from an existing z/VM instance."""
|
||||
LOG.debug("Creating xCAT node %s from existing node", self._name)
|
||||
|
||||
url = self._xcat_url.lsdef_node('/' + source_node_name)
|
||||
res_info = zvmutils.xcat_request("GET", url)['info'][0]
|
||||
|
||||
body = []
|
||||
for info in res_info:
|
||||
if ("=" in info and ("postbootscripts" not in info) and
|
||||
("postscripts" not in info) and ("hostnames" not in info)):
|
||||
body.append(info.lstrip())
|
||||
|
||||
url = self._xcat_url.mkdef('/' + self._name)
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMXCATCreateNodeFailed, node=self._name):
|
||||
zvmutils.xcat_request("POST", url, body)
|
||||
|
||||
def get_console_log(self, logsize):
|
||||
"""get console log."""
|
||||
url = self._xcat_url.rinv('/' + self._name, '&field=--consoleoutput'
|
||||
'&field=%s') % logsize
|
||||
|
||||
# Because we might have logs in the console, we need ignore the warning
|
||||
res_info = zvmutils.xcat_request("GET", url, ignore_warning=True)
|
||||
|
||||
with zvmutils.expect_invalid_xcat_resp_data(res_info):
|
||||
log_data = res_info['info'][0][0]
|
||||
|
||||
return log_data
|
||||
|
||||
def collect_diagnostics(self, context, reason):
|
||||
xcat_version = self._driver._xcat_version
|
||||
|
||||
if zvmutils.xcat_support_deployment_failure_diagnostics(xcat_version):
|
||||
# Diagnostics request is only supported >= xCAT 2.3.8.16
|
||||
# On older versions of xCAT, do nothing. If the request is issued
|
||||
# on an older version, xCAT will treat it as an error.
|
||||
url = self._xcat_url.mkdiag('/', self._name, self._instance.uuid,
|
||||
context)
|
||||
# Some body properties will appear to duplicate information
|
||||
# carried elsewhere, for example the request ID which is a URL
|
||||
# query parameter as well. This is intentional. The request body
|
||||
# is considered opaque to xCAT, data there is simply passed through
|
||||
# into the diagnostics blob as "upstream context". Other parts
|
||||
# of the request, such as the URL query parameters, are NOT opaque
|
||||
# and xCAT uses them to filter the data that is captured.
|
||||
body = ['reason=%s' % reason,
|
||||
'openstack_nova_instance_uuid=%s' % self._instance.uuid]
|
||||
if context is not None:
|
||||
try:
|
||||
body.append('openstack_request_id=%s' % context.request_id)
|
||||
except Exception as err:
|
||||
# Cannot use format_message() in this context, because the
|
||||
# Exception class does not implement that method.
|
||||
msg = _("Failed to add request ID to message body: %(err)s"
|
||||
) % {'err': six.text_type(err)}
|
||||
LOG.error(msg)
|
||||
# Continue and return the original URL once the error is
|
||||
# logged. Failing the request over this is NOT desired.
|
||||
try:
|
||||
zvmutils.xcat_request("POST", url, body)
|
||||
except (exception.ZVMXCATRequestFailed,
|
||||
exception.ZVMInvalidXCATResponseDataError,
|
||||
exception.ZVMXCATInternalError,
|
||||
exception.ZVMDriverError) as err:
|
||||
msg = _("Failed to collect deployment timeout diagnostics: %s"
|
||||
) % err.format_message()
|
||||
LOG.error(msg)
|
||||
else:
|
||||
msg = _("Skipping diagnostics collection; xCAT version %(actual)s "
|
||||
"does not support that function. The first xCAT version "
|
||||
"supporting that function is %(required)s."
|
||||
) % {'actual': xcat_version, 'required':
|
||||
const.XCAT_SUPPORT_COLLECT_DIAGNOSTICS_DEPLOYFAILED}
|
||||
LOG.debug(msg)
|
@ -1,164 +0,0 @@
|
||||
# Copyright 2013 IBM Corp.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from nova_zvm.virt.zvm import exception
|
||||
from nova_zvm.virt.zvm import utils as zvmutils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
NetworkUtils = zvmutils.NetworkUtils()
|
||||
|
||||
|
||||
class NetworkOperator(object):
|
||||
"""Configuration check and manage MAC address."""
|
||||
|
||||
def __init__(self):
|
||||
self._xcat_url = zvmutils.get_xcat_url()
|
||||
|
||||
def add_xcat_host(self, node, ip, host_name):
|
||||
"""Add/Update hostname/ip bundle in xCAT MN nodes table."""
|
||||
commands = "node=%s" % node + " hosts.ip=%s" % ip
|
||||
commands += " hosts.hostnames=%s" % host_name
|
||||
body = [commands]
|
||||
url = self._xcat_url.tabch("/hosts")
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
result_data = zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
return result_data
|
||||
|
||||
def _delete_xcat_host(self, node_name):
|
||||
"""Remove xcat hosts table rows where node name is node_name."""
|
||||
commands = "-d node=%s hosts" % node_name
|
||||
body = [commands]
|
||||
url = self._xcat_url.tabch("/hosts")
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def add_xcat_mac(self, node, interface, mac, zhcp=None):
|
||||
"""Add node name, interface, mac address into xcat mac table."""
|
||||
commands = "mac.node=%s" % node + " mac.mac=%s" % mac
|
||||
commands += " mac.interface=%s" % interface
|
||||
if zhcp is not None:
|
||||
commands += " mac.comments=%s" % zhcp
|
||||
url = self._xcat_url.tabch("/mac")
|
||||
body = [commands]
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def add_xcat_switch(self, node, nic_name, interface, zhcp=None):
|
||||
"""Add node name and nic name address into xcat switch table."""
|
||||
commands = "switch.node=%s" % node
|
||||
commands += " switch.port=%s" % nic_name
|
||||
commands += " switch.interface=%s" % interface
|
||||
if zhcp is not None:
|
||||
commands += " switch.comments=%s" % zhcp
|
||||
url = self._xcat_url.tabch("/switch")
|
||||
body = [commands]
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def _delete_xcat_mac(self, node_name):
|
||||
"""Remove node mac record from xcat mac table."""
|
||||
commands = "-d node=%s mac" % node_name
|
||||
url = self._xcat_url.tabch("/mac")
|
||||
body = [commands]
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def _delete_xcat_switch(self, node_name):
|
||||
"""Remove node switch record from xcat switch table."""
|
||||
commands = "-d node=%s switch" % node_name
|
||||
url = self._xcat_url.tabch("/switch")
|
||||
body = [commands]
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def update_xcat_mac(self, node, interface, mac, zhcp=None):
|
||||
"""Add node name, interface, mac address into xcat mac table."""
|
||||
commands = "node=%s" % node + " interface=%s" % interface
|
||||
commands += " mac.mac=%s" % mac
|
||||
if zhcp is not None:
|
||||
commands += " mac.comments=%s" % zhcp
|
||||
url = self._xcat_url.tabch("/mac")
|
||||
body = [commands]
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def update_xcat_switch(self, node, nic_name, interface, zhcp=None):
|
||||
"""Add node name and nic name address into xcat switch table."""
|
||||
commands = "node=%s" % node
|
||||
commands += " interface=%s" % interface
|
||||
commands += " switch.port=%s" % nic_name
|
||||
if zhcp is not None:
|
||||
commands += " switch.comments=%s" % zhcp
|
||||
url = self._xcat_url.tabch("/switch")
|
||||
body = [commands]
|
||||
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url, body)['data']
|
||||
|
||||
def clean_mac_switch_host(self, node_name):
|
||||
"""Clean node records in xCAT mac, host and switch table."""
|
||||
self.clean_mac_switch(node_name)
|
||||
self._delete_xcat_host(node_name)
|
||||
|
||||
def clean_mac_switch(self, node_name):
|
||||
"""Clean node records in xCAT mac and switch table."""
|
||||
self._delete_xcat_mac(node_name)
|
||||
self._delete_xcat_switch(node_name)
|
||||
|
||||
def makehosts(self):
|
||||
"""Update xCAT MN /etc/hosts file."""
|
||||
url = self._xcat_url.network("/makehosts")
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url)['data']
|
||||
|
||||
def makeDNS(self):
|
||||
"""Update xCAT MN DNS."""
|
||||
url = self._xcat_url.network("/makedns")
|
||||
with zvmutils.except_xcat_call_failed_and_reraise(
|
||||
exception.ZVMNetworkError):
|
||||
return zvmutils.xcat_request("PUT", url)['data']
|
||||
|
||||
def config_xcat_mac(self, instance_name):
|
||||
"""Hook xCat to prevent assign MAC for instance."""
|
||||
fake_mac_addr = "00:00:00:00:00:00"
|
||||
nic_name = "fake"
|
||||
self.add_xcat_mac(instance_name, nic_name, fake_mac_addr)
|
||||
|
||||
def create_xcat_table_about_nic(self, zhcpnode, inst_name,
|
||||
nic_name, mac_address, vdev):
|
||||
self._delete_xcat_mac(inst_name)
|
||||
self.add_xcat_mac(inst_name, vdev, mac_address, zhcpnode)
|
||||
self.add_xcat_switch(inst_name, nic_name, vdev, zhcpnode)
|
@ -12,7 +12,6 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import itertools
|
||||
|
||||
from nova_zvm.virt.zvm import conf as zvm_conf
|
||||
@ -24,8 +23,6 @@ def list_opts():
|
||||
# we keep this into DEFAULT.
|
||||
('DEFAULT',
|
||||
itertools.chain(
|
||||
zvm_conf.zvm_image_opts,
|
||||
zvm_conf.zvm_opts,
|
||||
zvm_conf.zvm_user_opts,
|
||||
)),
|
||||
]
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -8,3 +8,5 @@ oslo.serialization>=1.10.0 # Apache-2.0
|
||||
oslo.service>=1.10.0 # Apache-2.0
|
||||
oslo.utils>=3.20.0 # Apache-2.0
|
||||
six>=1.9.0
|
||||
|
||||
CloudLib4zvm>=0.2.2
|
||||
|
@ -16,3 +16,5 @@ testtools>=1.4.0
|
||||
mock>=1.2
|
||||
mox>=0.5.3
|
||||
vine
|
||||
wsgi-intercept>=1.4.1 # MIT License
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user