Huang Rui 2e3d781d86 Move nova zvm virt driver to separate namespace
Currently, nova zvm virt driver is in nova namespace. This result
in some troubles when generating config file, generating docs and
packaging.

All changes in this commit are trying to move nova zvm virt driver
code into new namespace - nova_zvm .

Change-Id: I5251069dfd24ff4e337e9308439415482eb2234c
2017-03-23 12:51:13 +08:00

903 lines
36 KiB
Python

# 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 _, _LW
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(_LW("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(_LW("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(_LW("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(_LW("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(_LW("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,
'password=%s' % CONF.zvm_user_default_password,
'cpu=%i' % self._instance['vcpus'],
'memory=%im' % self._instance['memory_mb'],
'privilege=%s' % CONF.zvm_user_default_privilege]
# 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 = _LW("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)