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:
Huang Rui 2017-08-17 15:50:26 +08:00
parent 9887de79eb
commit 6fea79d689
19 changed files with 754 additions and 11825 deletions

View File

@ -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

View File

View 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)

View 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

View File

@ -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)

View File

@ -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()

View File

@ -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'

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -16,3 +16,5 @@ testtools>=1.4.0
mock>=1.2
mox>=0.5.3
vine
wsgi-intercept>=1.4.1 # MIT License