zVMCloudConnector-1.4.1/0000775000175000017510000000000013442723341014521 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/0000775000175000017510000000000013442723341016037 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/smutclient.py0000664000175000017510000045151213442676324020620 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import hashlib # On SLES12, we found that if you import urllib.parse later # than requests, you will find a error like 'not able to load # urllib.parse, this is because urllib will be in sys.modules # when first import requests # as workaround here, we first import urllib then import requests # later, we need consider to use urllib.request to replace # requests if that's possible to avoid this kind of issue import shutil import six.moves.urllib.parse as urlparse import requests import threading import os import re import six import string import tempfile from smutLayer import smut from zvmsdk import config from zvmsdk import constants as const from zvmsdk import database from zvmsdk import exception from zvmsdk import log from zvmsdk import returncode from zvmsdk import utils as zvmutils CONF = config.CONF LOG = log.LOG _LOCK = threading.Lock() CHUNKSIZE = 4096 _SMUT_CLIENT = None def get_smutclient(): global _SMUT_CLIENT if _SMUT_CLIENT is None: try: _SMUT_CLIENT = zvmutils.import_object( 'zvmsdk.smutclient.SMUTClient') except ImportError: LOG.error("Unable to get smutclient") raise ImportError return _SMUT_CLIENT class SMUTClient(object): def __init__(self): self._smut = smut.SMUT() self._pathutils = zvmutils.PathUtils() self._NetDbOperator = database.NetworkDbOperator() self._GuestDbOperator = database.GuestDbOperator() self._ImageDbOperator = database.ImageDbOperator() def _request(self, requestData): try: results = self._smut.request(requestData) except Exception as err: LOG.error('SMUT internal parse encounter error') raise exception.SDKInternalError(msg=err, modID='smut') def _is_smut_internal_error(results): internal_error_list = returncode.SMUT_INTERNAL_ERROR for error in internal_error_list: if results['overallRC'] != error[0]: # overallRC does not match, continue next continue if error[1] is not None and results['rc'] != error[1]: # rc match failed continue if error[2] is not None and results['rs'] not in error[2]: # rs match failed continue # All match finish successfully, return true return True return False if results['overallRC'] != 0: results.pop('logEntries') # Check whether this smut error belongs to internal error, if so, # raise internal error, otherwise raise clientrequestfailed error if _is_smut_internal_error(results): msg = "SMUT internal error. Results: %s" % str(results) LOG.error(msg) raise exception.SDKInternalError(msg=msg, modID='smut', results=results) else: msg = ("SMUT request failed. RequestData: '%s', Results: '%s'" % (requestData, str(results))) raise exception.SDKSMUTRequestFailed(results, msg) return results def get_guest_temp_path(self, userid): return self._pathutils.get_guest_temp_path(userid) def _generate_vdev(self, base, offset): """Generate virtual device number based on base vdev :param base: base virtual device number, string of 4 bit hex. :param offset: offset to base, integer. """ vdev = hex(int(base, 16) + offset)[2:] return vdev.rjust(4, '0') def generate_disk_vdev(self, start_vdev=None, offset=0): """Generate virtual device number for disks :param offset: offset of user_root_vdev. :return: virtual device number, string of 4 bit hex. """ if not start_vdev: start_vdev = CONF.zvm.user_root_vdev vdev = self._generate_vdev(start_vdev, offset) if offset >= 0 and offset < 254: return vdev else: msg = ("Failed to generate disk vdev, invalid virtual device" "number for disk:%s" % vdev) LOG.error(msg) raise exception.SDKGuestOperationError(rs=2, msg=msg) def add_mdisks(self, userid, disk_list, start_vdev=None): """Add disks for the userid :disks: A list dictionary to describe disk info, for example: disk: [{'size': '1g', 'format': 'ext3', 'disk_pool': 'ECKD:eckdpool1'}] """ for idx, disk in enumerate(disk_list): vdev = self.generate_disk_vdev(start_vdev=start_vdev, offset=idx) self._add_mdisk(userid, disk, vdev) disk['vdev'] = vdev if disk.get('disk_pool') is None: disk['disk_pool'] = CONF.zvm.disk_pool sizeUpper = disk.get('size').strip().upper() sizeUnit = sizeUpper[-1] if sizeUnit != 'G' and sizeUnit != 'M': sizeValue = sizeUpper disk_pool = disk.get('disk_pool') [diskpool_type, diskpool_name] = disk_pool.split(':') if (diskpool_type.upper() == 'ECKD'): # Convert the cylinders to bytes convert = 737280 else: # Convert the blocks to bytes convert = 512 byteSize = float(float(int(sizeValue) * convert / 1024) / 1024) unit = "M" if (byteSize > 1024): byteSize = float(byteSize / 1024) unit = "G" byteSize = "%.1f" % byteSize disk['size'] = byteSize + unit return disk_list def remove_mdisks(self, userid, vdev_list): for vdev in vdev_list: self._remove_mdisk(userid, vdev) def dedicate_device(self, userid, vaddr, raddr, mode): """dedicate device :userid: The name of the image obtaining a dedicated device :vaddr: The virtual device number of the device :raddr: A real device number to be dedicated or attached to the specified image :mode: Specify a 1 if the virtual device is to be in read-only mode. Otherwise, specify a 0. """ # dedicate device to directory entry self._dedicate_device(userid, vaddr, raddr, mode) def _dedicate_device(self, userid, vaddr, raddr, mode): """dedicate device.""" action = 'dedicate' rd = ('changevm %(uid)s %(act)s %(va)s %(ra)s %(mod)i' % {'uid': userid, 'act': action, 'va': vaddr, 'ra': raddr, 'mod': mode}) action = "dedicate device to userid '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) def get_fcp_info_by_status(self, userid, status): """get fcp information by the status. :userid: The name of the image to query fcp info :status: The status of target fcps. eg:'active', 'free' or 'offline'. """ results = self._get_fcp_info_by_status(userid, status) return results def _get_fcp_info_by_status(self, userid, status): action = 'fcpinfo' rd = ' '.join(['getvm', userid, action, status]) action = "query fcp info of '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): results = self._request(rd) return results['response'] def undedicate_device(self, userid, vaddr): """undedicate device :userid: The name of the image obtaining a dedicated device :vaddr: The virtual device number of the device """ # undedicate device to directory entry self._undedicate_device(userid, vaddr) def _undedicate_device(self, userid, vaddr): """undedicate device.""" action = 'undedicate' rd = ('changevm %(uid)s %(act)s %(va)s' % {'uid': userid, 'act': action, 'va': vaddr}) action = "undedicate device from userid '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) def get_image_performance_info(self, userid): """Get CPU and memory usage information. :userid: the zvm userid to be queried """ pi_dict = self.image_performance_query([userid]) return pi_dict.get(userid, None) def _parse_vswitch_inspect_data(self, rd_list): """ Parse the Virtual_Network_Vswitch_Query_Byte_Stats data to get inspect data. """ def _parse_value(data_list, idx, keyword, offset): return idx + offset, data_list[idx].rpartition(keyword)[2].strip() vsw_dict = {} with zvmutils.expect_invalid_resp_data(): # vswitch count idx = 0 idx, vsw_count = _parse_value(rd_list, idx, 'vswitch count:', 2) vsw_dict['vswitch_count'] = int(vsw_count) # deal with each vswitch data vsw_dict['vswitches'] = [] for i in range(vsw_dict['vswitch_count']): vsw_data = {} # skip vswitch number idx += 1 # vswitch name idx, vsw_name = _parse_value(rd_list, idx, 'vswitch name:', 1) vsw_data['vswitch_name'] = vsw_name # uplink count idx, up_count = _parse_value(rd_list, idx, 'uplink count:', 1) # skip uplink data idx += int(up_count) * 9 # skip bridge data idx += 8 # nic count vsw_data['nics'] = [] idx, nic_count = _parse_value(rd_list, idx, 'nic count:', 1) nic_count = int(nic_count) for j in range(nic_count): nic_data = {} idx, nic_id = _parse_value(rd_list, idx, 'nic_id:', 1) userid, toss, vdev = nic_id.partition(' ') nic_data['userid'] = userid nic_data['vdev'] = vdev idx, nic_data['nic_fr_rx'] = _parse_value(rd_list, idx, 'nic_fr_rx:', 1 ) idx, nic_data['nic_fr_rx_dsc'] = _parse_value(rd_list, idx, 'nic_fr_rx_dsc:', 1 ) idx, nic_data['nic_fr_rx_err'] = _parse_value(rd_list, idx, 'nic_fr_rx_err:', 1 ) idx, nic_data['nic_fr_tx'] = _parse_value(rd_list, idx, 'nic_fr_tx:', 1 ) idx, nic_data['nic_fr_tx_dsc'] = _parse_value(rd_list, idx, 'nic_fr_tx_dsc:', 1 ) idx, nic_data['nic_fr_tx_err'] = _parse_value(rd_list, idx, 'nic_fr_tx_err:', 1 ) idx, nic_data['nic_rx'] = _parse_value(rd_list, idx, 'nic_rx:', 1 ) idx, nic_data['nic_tx'] = _parse_value(rd_list, idx, 'nic_tx:', 1 ) vsw_data['nics'].append(nic_data) # vlan count idx, vlan_count = _parse_value(rd_list, idx, 'vlan count:', 1) # skip vlan data idx += int(vlan_count) * 3 # skip the blank line idx += 1 vsw_dict['vswitches'].append(vsw_data) return vsw_dict def _is_vdev_valid(self, vdev, vdev_info): for used_vdev in vdev_info: if (((int(vdev, 16) >= int(used_vdev, 16)) and (int(vdev, 16) <= int(used_vdev, 16) + 2)) or ((int(vdev, 16) < int(used_vdev, 16)) and (int(vdev, 16) >= int(used_vdev, 16) - 2))): return False return True def get_power_state(self, userid): """Get power status of a z/VM instance.""" LOG.debug('Querying power stat of %s' % userid) requestData = "PowerVM " + userid + " status" action = "query power state of '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): results = self._request(requestData) with zvmutils.expect_invalid_resp_data(results): status = results['response'][0].partition(': ')[2] return status def _check_power_state(self, userid, action): # Get the vm status power_state = self.get_power_state(userid) # Power on the vm if it is inactive if power_state == 'off': msg = ('The vm %s is powered off, please start up it ' 'before %s' % (userid, action)) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) def guest_start(self, userid): """Power on VM.""" requestData = "PowerVM " + userid + " on" with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def guest_stop(self, userid, **kwargs): """Power off VM.""" requestData = "PowerVM " + userid + " off" if 'timeout' in kwargs.keys() and kwargs['timeout']: requestData += ' --maxwait ' + str(kwargs['timeout']) if 'poll_interval' in kwargs.keys() and kwargs['poll_interval']: requestData += ' --poll ' + str(kwargs['poll_interval']) with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def guest_softstop(self, userid, **kwargs): """Power off VM gracefully, it will call shutdown os then deactivate vm""" requestData = "PowerVM " + userid + " softoff" if 'timeout' in kwargs.keys() and kwargs['timeout']: requestData += ' --maxwait ' + str(kwargs['timeout']) if 'poll_interval' in kwargs.keys() and kwargs['poll_interval']: requestData += ' --poll ' + str(kwargs['poll_interval']) with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def guest_pause(self, userid): self._check_power_state(userid, 'pause') requestData = "PowerVM " + userid + " pause" with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def guest_unpause(self, userid): self._check_power_state(userid, 'unpause') requestData = "PowerVM " + userid + " unpause" with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def guest_reboot(self, userid): requestData = ' '.join(("PowerVM", userid, "reboot")) with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def guest_reset(self, userid): requestData = ' '.join(("PowerVM", userid, "reset")) with zvmutils.log_and_reraise_smut_request_failed(): self._request(requestData) def live_migrate_move(self, userid, destination, parms): """ moves the specified virtual machine, while it continues to run, to the specified system within the SSI cluster. """ rd = ('migratevm %(uid)s move --destination %(dest)s ' % {'uid': userid, 'dest': destination}) if 'maxtotal' in parms: rd += ('--maxtotal ' + str(parms['maxTotal'])) if 'maxquiesce' in parms: rd += ('--maxquiesce ' + str(parms['maxquiesce'])) if 'immediate' in parms: rd += " --immediate" if 'forcearch' in parms: rd += " --forcearch" if 'forcedomain' in parms: rd += " --forcedomain" if 'forcestorage' in parms: rd += " --forcestorage" action = "move userid '%s' to SSI '%s'" % (userid, destination) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: msg = '' if action is not None: msg = "Failed to %s. " % action msg += "SMUT error: %s" % err.format_message() LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) def live_migrate_test(self, userid, destination): """ tests the specified virtual machine and reports whether or not it is eligible to be relocated to the specified system. """ rd = ('migratevm %(uid)s test --destination %(dest)s ' % {'uid': userid, 'dest': destination}) action = "test to move userid '%s' to SSI '%s'" % (userid, destination) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: msg = '' if action is not None: msg = "Failed to %s. " % action msg += "SMUT error: %s" % err.format_message() LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) def create_vm(self, userid, cpu, memory, disk_list, profile, max_cpu, max_mem): """ Create VM and add disks if specified. """ rd = ('makevm %(uid)s directory LBYONLY %(mem)im %(pri)s ' '--cpus %(cpu)i --profile %(prof)s --maxCPU %(max_cpu)i ' '--maxMemSize %(max_mem)s --setReservedMem' % {'uid': userid, 'mem': memory, 'pri': const.ZVM_USER_DEFAULT_PRIVILEGE, 'cpu': cpu, 'prof': profile, 'max_cpu': max_cpu, 'max_mem': max_mem}) if CONF.zvm.default_admin_userid: rd += (' --logonby "%s"' % CONF.zvm.default_admin_userid) if (disk_list and 'is_boot_disk' in disk_list[0] and disk_list[0]['is_boot_disk']): ipl_disk = CONF.zvm.user_root_vdev rd += (' --ipl %s' % ipl_disk) action = "create userid '%s'" % userid try: self._request(rd) except exception.SDKSMUTRequestFailed as err: if ((err.results['rc'] == 436) and (err.results['rs'] == 4)): result = "Profile '%s'" % profile raise exception.SDKObjectNotExistError(obj_desc=result, modID='guest') else: msg = '' if action is not None: msg = "Failed to %s. " % action msg += "SMUT error: %s" % err.format_message() LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) # Add the guest to db immediately after user created action = "add guest '%s' to database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.add_guest(userid) # Continue to add disk if disk_list: # Add disks for vm return self.add_mdisks(userid, disk_list) def _add_mdisk(self, userid, disk, vdev): """Create one disk for userid NOTE: No read, write and multi password specified, and access mode default as 'MR'. """ size = disk['size'] fmt = disk.get('format', 'ext4') disk_pool = disk.get('disk_pool') or CONF.zvm.disk_pool [diskpool_type, diskpool_name] = disk_pool.split(':') if (diskpool_type.upper() == 'ECKD'): action = 'add3390' else: action = 'add9336' rd = ' '.join(['changevm', userid, action, diskpool_name, vdev, size, '--mode MR']) if fmt: rd += (' --filesystem %s' % fmt.lower()) action = "add mdisk to userid '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) def get_vm_list(self): """Get the list of guests that are created by SDK return userid list""" action = "list all guests in database" with zvmutils.log_and_reraise_sdkbase_error(action): guests_in_db = self._GuestDbOperator.get_guest_list() guests_migrated = self._GuestDbOperator.get_migrated_guest_list() # db query return value in tuple (uuid, userid, metadata, comments) userids_in_db = [g[1].upper() for g in guests_in_db] userids_migrated = [g[1].upper() for g in guests_migrated] userid_list = list(set(userids_in_db) - set(userids_migrated)) return userid_list def _remove_mdisk(self, userid, vdev): rd = ' '.join(('changevm', userid, 'removedisk', vdev)) action = "remove disk with vdev '%s' from userid '%s'" % (vdev, userid) with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) def guest_authorize_iucv_client(self, userid, client=None): """Punch a script that used to set the authorized client userid in vm If the guest is in log off status, the change will take effect when the guest start up at first time. If the guest is in active status, power off and power on are needed for the change to take effect. :param str guest: the user id of the vm :param str client: the user id of the client that can communicate to guest using IUCV""" client = client or zvmutils.get_smut_userid() iucv_path = "/tmp/" + userid if not os.path.exists(iucv_path): os.makedirs(iucv_path) iucv_auth_file = iucv_path + "/iucvauth.sh" zvmutils.generate_iucv_authfile(iucv_auth_file, client) try: requestData = "ChangeVM " + userid + " punchfile " + \ iucv_auth_file + " --class x" self._request(requestData) except exception.SDKSMUTRequestFailed as err: msg = ("Failed to punch IUCV auth file to userid '%s'. SMUT error:" " %s" % (userid, err.format_message())) LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) finally: self._pathutils.clean_temp_folder(iucv_path) def guest_deploy(self, userid, image_name, transportfiles=None, remotehost=None, vdev=None): """ Deploy image and punch config driver to target """ # (TODO: add the support of multiple disks deploy) msg = ('Start to deploy image %(img)s to guest %(vm)s' % {'img': image_name, 'vm': userid}) LOG.info(msg) image_file = '/'.join([self._get_image_path_by_name(image_name), CONF.zvm.user_root_vdev]) # Unpack image file to root disk vdev = vdev or CONF.zvm.user_root_vdev cmd = ['sudo', '/opt/zthin/bin/unpackdiskimage', userid, vdev, image_file] with zvmutils.expect_and_reraise_internal_error(modID='guest'): (rc, output) = zvmutils.execute(cmd) if rc != 0: err_msg = ("unpackdiskimage failed with return code: %d." % rc) err_output = "" output_lines = output.split('\n') for line in output_lines: if line.__contains__("ERROR:"): err_output += ("\\n" + line.strip()) LOG.error(err_msg + err_output) raise exception.SDKGuestOperationError(rs=3, userid=userid, unpack_rc=rc, err=err_output) # Purge guest reader to clean dirty data rd = ("changevm %s purgerdr" % userid) action = "purge reader of '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) # Punch transport files if specified if transportfiles: # Copy transport file to local msg = ('Start to send customized file to vm %s' % userid) LOG.info(msg) try: tmp_trans_dir = tempfile.mkdtemp() local_trans = '/'.join([tmp_trans_dir, os.path.basename(transportfiles)]) if remotehost: cmd = ["/usr/bin/scp", "-B", "-P", CONF.zvm.remotehost_sshd_port, "-o StrictHostKeyChecking=no", ("%s:%s" % (remotehost, transportfiles)), local_trans] else: cmd = ["/usr/bin/cp", transportfiles, local_trans] with zvmutils.expect_and_reraise_internal_error(modID='guest'): (rc, output) = zvmutils.execute(cmd) if rc != 0: err_msg = ('copy config drive with command %(cmd)s ' 'failed with output: %(res)s' % {'cmd': str(cmd), 'res': output}) LOG.error(err_msg) raise exception.SDKGuestOperationError(rs=4, userid=userid, err_info=err_msg) # Punch config drive to guest userid rd = ("changevm %(uid)s punchfile %(file)s --class X" % {'uid': userid, 'file': local_trans}) action = "punch config drive to userid '%s'" % userid with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) finally: # remove the local temp config drive folder self._pathutils.clean_temp_folder(tmp_trans_dir) # Authorize iucv client self.guest_authorize_iucv_client(userid) # Update os version in guest metadata # TODO: may should append to old metadata, not replace image_info = self._ImageDbOperator.image_query_record(image_name) metadata = 'os_version=%s' % image_info[0]['imageosdistro'] self._GuestDbOperator.update_guest_by_userid(userid, meta=metadata) msg = ('Deploy image %(img)s to guest %(vm)s disk %(vdev)s' ' successfully' % {'img': image_name, 'vm': userid, 'vdev': vdev}) LOG.info(msg) def guest_capture(self, userid, image_name, capture_type='rootonly', compress_level=6): if capture_type == "alldisks": func = ('Capture guest with type: %s' % capture_type) msg = ('%s is not supported in current release' % func) LOG.error(msg) raise exception.SDKFunctionNotImplementError(func=func, modID='guest') msg = ('Start to capture %(vm)s to generate image %(img)s with ' 'capture type %(type)s' % {'vm': userid, 'img': image_name, 'type': capture_type}) LOG.info(msg) self._check_power_state(userid, 'capture') # Make sure the iucv channel is ready for communication on source vm try: self.execute_cmd(userid, 'pwd') except exception.SDKSMUTRequestFailed as err: msg = ('Failed to check iucv status on capture source vm ' '%(vm)s with error %(err)s' % {'vm': userid, 'err': err.results['response'][0]}) LOG.error(msg) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) # Get the os version of the vm try: os_version = self._guest_get_os_version(userid) except exception.SDKSMUTRequestFailed as err: msg = ('Failed to execute command on capture source vm %(vm)s' 'to get os version with error %(err)s' % {'vm': userid, 'err': err.results['response'][0]}) LOG.error(msg) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) except Exception as err: msg = ('Error happened when parsing os version on source vm ' '%(vm)s with error: %(err)s' % {'vm': userid, 'err': six.text_type(err)}) LOG.error(msg) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) msg = ('The os version of capture source vm %(vm)s is %(version)s' % {'vm': userid, 'version': os_version}) LOG.info(msg) # Find the root device according to the capture type try: capture_devices = self._get_capture_devices(userid, capture_type) except exception.SDKSMUTRequestFailed as err: msg = ('Failed to execute command on source vm %(vm)s to get the ' 'devices for capture with error %(err)s' % {'vm': userid, 'err': err.results['response'][0]}) LOG.error(msg) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) except Exception as err: msg = ('Internal error happened when getting the devices for ' 'capture on source vm %(vm)s with error %(err)s' % {'vm': userid, 'err': six.text_type(err)}) LOG.error(msg) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) except exception.SDKGuestOperationError: raise # Shutdown the vm before capture self.guest_softstop(userid) # Prepare directory for writing image file image_temp_dir = '/'.join((CONF.image.sdk_image_repository, const.IMAGE_TYPE['CAPTURE'], os_version, image_name)) self._pathutils.mkdir_if_not_exist(image_temp_dir) # Call creatediskimage to capture a vm to generate an image # TODO:(nafei) to support multiple disk capture vdev = capture_devices[0] msg = ('Found the device %(vdev)s of %(vm)s for capture' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) image_file_name = vdev image_file_path = '/'.join((image_temp_dir, image_file_name)) cmd = ['sudo', '/opt/zthin/bin/creatediskimage', userid, vdev, image_file_path, '--compression', str(compress_level)] with zvmutils.expect_and_reraise_internal_error(modID='guest'): (rc, output) = zvmutils.execute(cmd) if rc != 0: err_msg = ("creatediskimage failed with return code: %d." % rc) err_output = "" output_lines = output.split('\n') for line in output_lines: if line.__contains__("ERROR:"): err_output += ("\\n" + line.strip()) LOG.error(err_msg + err_output) self._pathutils.clean_temp_folder(image_temp_dir) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=err_output) # Move the generated image to netboot folder image_final_dir = '/'.join([CONF.image.sdk_image_repository, const.IMAGE_TYPE['DEPLOY'], os_version, image_name]) image_final_path = '/'.join((image_final_dir, image_file_name)) self._pathutils.mkdir_if_not_exist(image_final_dir) cmd = ['mv', image_file_path, image_final_path] with zvmutils.expect_and_reraise_internal_error(modID='guest'): (rc, output) = zvmutils.execute(cmd) if rc != 0: err_msg = ("move image file from staging to netboot " "folder failed with return code: %d." % rc) LOG.error(err_msg) self._pathutils.clean_temp_folder(image_temp_dir) self._pathutils.clean_temp_folder(image_final_dir) raise exception.SDKGuestOperationError(rs=5, userid=userid, err=err_msg) self._pathutils.clean_temp_folder(image_temp_dir) msg = ('Updating the metadata for captured image %s ' % image_name) LOG.info(msg) # Get md5sum of image real_md5sum = self._get_md5sum(image_final_path) # Get disk_size_units of image disk_size_units = self._get_disk_size_units(image_final_path) # Get the image physical size image_size = self._get_image_size(image_final_path) # Create the image record in image database self._ImageDbOperator.image_add_record(image_name, os_version, real_md5sum, disk_size_units, image_size, capture_type) LOG.info('Image %s is captured and imported to image repository ' 'successfully' % image_name) def _guest_get_os_version(self, userid): os_version = '' release_file = self.execute_cmd(userid, 'ls /etc/*-release') if '/etc/os-release' in release_file: # Parse os-release file, part of the output looks like: # NAME="Red Hat Enterprise Linux Server" # ID="rhel" # VERSION_ID="7.0" release_info = self.execute_cmd(userid, 'cat /etc/os-release') release_dict = {} for item in release_info: if item: release_dict[item.split('=')[0]] = item.split('=')[1] distro = release_dict['ID'] version = release_dict['VERSION_ID'] if '"' in distro: distro = eval(distro) if '"' in version: version = eval(version) os_version = '%s%s' % (distro, version) return os_version elif '/etc/redhat-release' in release_file: # The output looks like: # "Red Hat Enterprise Linux Server release 6.7 (Santiago)" distro = 'rhel' release_info = self.execute_cmd(userid, 'cat /etc/redhat-release') distro_version = release_info[0].split()[6] os_version = ''.join((distro, distro_version)) return os_version elif '/etc/SuSE-release' in release_file: # The output for this file looks like: # SUSE Linux Enterprise Server 11 (s390x) # VERSION = 11 # PATCHLEVEL = 3 distro = 'sles' release_info = self.execute_cmd(userid, 'cat /etc/SuSE-release') LOG.debug('OS release info is %s' % release_info) release_version = '.'.join((release_info[1].split('=')[1].strip(), release_info[2].split('=')[1].strip())) os_version = ''.join((distro, release_version)) return os_version elif '/etc/system-release' in release_file: # For some rhel6.7 system, it only have system-release file and # the output looks like: # "Red Hat Enterprise Linux Server release 6.7 (Santiago)" distro = 'rhel' release_info = self.execute_cmd(userid, 'cat /etc/system-release') distro_version = release_info[0].split()[6] os_version = ''.join((distro, distro_version)) return os_version def _get_capture_devices(self, userid, capture_type='rootonly'): capture_devices = [] if capture_type == 'rootonly': # Parse the /proc/cmdline to get root devices proc_cmdline = self.execute_cmd(userid, 'cat /proc/cmdline ' '| tr " " "\\n" | grep -a "^root=" | cut -c6-') root_device_info = proc_cmdline[0] if not root_device_info: msg = ('Unable to get useful info from /proc/cmdline to ' 'locate the device associated with the root directory ' 'on capture source vm %s' % userid) raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) else: if 'UUID=' in root_device_info: uuid = root_device_info.split()[0].split('=')[1] root_device = '/'.join(('/dev/disk/by-uuid', uuid)) elif 'LABEL=' in root_device_info: label = root_device_info.split()[0].split('=')[1] root_device = '/'.join(('/dev/disk/by-label', label)) elif 'mapper' in root_device_info: msg = ('Capturing a disk with root filesystem on logical' ' volume is not supported') raise exception.SDKGuestOperationError(rs=5, userid=userid, msg=msg) else: root_device = root_device_info root_device_node = self.execute_cmd(userid, 'readlink -f %s' % root_device)[0] # Get device node vdev by node name cmd = ('cat /proc/dasd/devices | grep -i "is %s" ' % root_device_node.split('/')[-1].rstrip(string.digits)) result = self.execute_cmd(userid, cmd)[0] root_device_vdev = result.split()[0][4:8] capture_devices.append(root_device_vdev) return capture_devices else: # For sysclone, parse the user directory entry to get the devices # for capture, leave for future pass def grant_user_to_vswitch(self, vswitch_name, userid): """Set vswitch to grant user.""" smut_userid = zvmutils.get_smut_userid() requestData = ' '.join(( 'SMAPI %s API Virtual_Network_Vswitch_Set_Extended' % smut_userid, "--operands", "-k switch_name=%s" % vswitch_name, "-k grant_userid=%s" % userid, "-k persist=YES")) try: self._request(requestData) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to grant user %s to vswitch %s, error: %s" % (userid, vswitch_name, err.format_message())) self._set_vswitch_exception(err, vswitch_name) def _set_vswitch_exception(self, error, switch_name): if ((error.results['rc'] == 212) and (error.results['rs'] == 40)): obj_desc = "Vswitch %s" % switch_name raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 396) and (error.results['rs'] == 2846)): errmsg = ("Operation is not allowed for a " "VLAN UNAWARE vswitch") raise exception.SDKConflictError(modID='network', rs=5, vsw=switch_name, msg=errmsg) elif ((error.results['rc'] == 396) and ((error.results['rs'] == 2838) or (error.results['rs'] == 2853) or (error.results['rs'] == 2856) or (error.results['rs'] == 2858) or (error.results['rs'] == 3022) or (error.results['rs'] == 3033))): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=5, vsw=switch_name, msg=errmsg) else: raise error def revoke_user_from_vswitch(self, vswitch_name, userid): """Revoke user for vswitch.""" smut_userid = zvmutils.get_smut_userid() requestData = ' '.join(( 'SMAPI %s API Virtual_Network_Vswitch_Set_Extended' % smut_userid, "--operands", "-k switch_name=%s" % vswitch_name, "-k revoke_userid=%s" % userid, "-k persist=YES")) try: self._request(requestData) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to revoke user %s from vswitch %s, error: %s" % (userid, vswitch_name, err.format_message())) self._set_vswitch_exception(err, vswitch_name) def image_performance_query(self, uid_list): """Call Image_Performance_Query to get guest current status. :uid_list: A list of zvm userids to be queried """ if uid_list == []: return {} if not isinstance(uid_list, list): uid_list = [uid_list] smut_userid = zvmutils.get_smut_userid() rd = ' '.join(( "SMAPI %s API Image_Performance_Query" % smut_userid, "--operands", '-T "%s"' % (' '.join(uid_list)), "-c %d" % len(uid_list))) action = "get performance info of userid '%s'" % str(uid_list) with zvmutils.log_and_reraise_smut_request_failed(action): results = self._request(rd) ipq_kws = { 'userid': "Guest name:", 'guest_cpus': "Guest CPUs:", 'used_cpu_time': "Used CPU time:", 'elapsed_cpu_time': "Elapsed time:", 'min_cpu_count': "Minimum CPU count:", 'max_cpu_limit': "Max CPU limit:", 'samples_cpu_in_use': "Samples CPU in use:", 'samples_cpu_delay': "Samples CPU delay:", 'used_memory': "Used memory:", 'max_memory': "Max memory:", 'min_memory': "Minimum memory:", 'shared_memory': "Shared memory:", } pi_dict = {} pi = {} rpi_list = ('\n'.join(results['response'])).split("\n\n") for rpi in rpi_list: try: pi = zvmutils.translate_response_to_dict(rpi, ipq_kws) except exception.SDKInternalError as err: emsg = err.format_message() # when there is only one userid queried and this userid is # in 'off'state, the smcli will only returns the queried # userid number, no valid performance info returned. if(emsg.__contains__("No value matched with keywords.")): continue else: raise err for k, v in pi.items(): pi[k] = v.strip('" ') if pi.get('userid') is not None: pi_dict[pi['userid']] = pi return pi_dict def system_image_performance_query(self, namelist): """Call System_Image_Performance_Query to get guest current status. :namelist: A namelist that defined in smapi namelist file. """ smut_userid = zvmutils.get_smut_userid() rd = ' '.join(( "SMAPI %s API System_Image_Performance_Query" % smut_userid, "--operands -T %s" % namelist)) action = "get performance info of namelist '%s'" % namelist with zvmutils.log_and_reraise_smut_request_failed(action): results = self._request(rd) ipq_kws = { 'userid': "Guest name:", 'guest_cpus': "Guest CPUs:", 'used_cpu_time': "Used CPU time:", 'elapsed_cpu_time': "Elapsed time:", 'min_cpu_count': "Minimum CPU count:", 'max_cpu_limit': "Max CPU limit:", 'samples_cpu_in_use': "Samples CPU in use:", 'samples_cpu_delay': "Samples CPU delay:", 'used_memory': "Used memory:", 'max_memory': "Max memory:", 'min_memory': "Minimum memory:", 'shared_memory': "Shared memory:", } pi_dict = {} pi = {} rpi_list = ('\n'.join(results['response'])).split("\n\n") for rpi in rpi_list: try: pi = zvmutils.translate_response_to_dict(rpi, ipq_kws) except exception.SDKInternalError as err: emsg = err.format_message() # when there is only one userid queried and this userid is # in 'off'state, the smcli will only returns the queried # userid number, no valid performance info returned. if(emsg.__contains__("No value matched with keywords.")): continue else: raise err for k, v in pi.items(): pi[k] = v.strip('" ') if pi.get('userid') is not None: pi_dict[pi['userid']] = pi return pi_dict def virtual_network_vswitch_query_byte_stats(self): smut_userid = zvmutils.get_smut_userid() rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Query_Byte_Stats" % smut_userid, "--operands", '-T "%s"' % smut_userid, '-k "switch_name=*"' )) action = "query vswitch usage info" with zvmutils.log_and_reraise_smut_request_failed(action): results = self._request(rd) return self._parse_vswitch_inspect_data(results['response']) def get_host_info(self): with zvmutils.log_and_reraise_smut_request_failed(): results = self._request("getHost general") host_info = zvmutils.translate_response_to_dict( '\n'.join(results['response']), const.RINV_HOST_KEYWORDS) return host_info def get_diskpool_info(self, pool): with zvmutils.log_and_reraise_smut_request_failed(): results = self._request("getHost diskpoolspace %s" % pool) dp_info = zvmutils.translate_response_to_dict( '\n'.join(results['response']), const.DISKPOOL_KEYWORDS) return dp_info def get_vswitch_list(self): smut_userid = zvmutils.get_smut_userid() rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Query" % smut_userid, "--operands", "-s \'*\'")) try: result = self._request(rd) except exception.SDKSMUTRequestFailed as err: if ((err.results['rc'] == 212) and (err.results['rs'] == 40)): LOG.warning("No Virtual switch in the host") return [] else: LOG.error("Failed to get vswitch list, error: %s" % err.format_message()) raise with zvmutils.expect_invalid_resp_data(): if (not result['response'] or not result['response'][0]): return [] else: data = '\n'.join([s for s in result['response'] if isinstance(s, six.string_types)]) output = re.findall('VSWITCH: Name: (.*)', data) return output def set_vswitch_port_vlan_id(self, vswitch_name, userid, vlan_id): smut_userid = zvmutils.get_smut_userid() msg = ('Start to set VLAN ID %(vid)s on vswitch %(vsw)s ' 'for guest %(vm)s' % {'vid': vlan_id, 'vsw': vswitch_name, 'vm': userid}) LOG.info(msg) rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Set_Extended" % smut_userid, "--operands", "-k grant_userid=%s" % userid, "-k switch_name=%s" % vswitch_name, "-k user_vlan_id=%s" % vlan_id, "-k persist=YES")) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to set VLAN ID %s on vswitch %s for user %s, " "error: %s" % (vlan_id, vswitch_name, userid, err.format_message())) self._set_vswitch_exception(err, vswitch_name) msg = ('Set VLAN ID %(vid)s on vswitch %(vsw)s ' 'for guest %(vm)s successfully' % {'vid': vlan_id, 'vsw': vswitch_name, 'vm': userid}) LOG.info(msg) def add_vswitch(self, name, rdev=None, controller='*', connection='CONNECT', network_type='ETHERNET', router="NONROUTER", vid='UNAWARE', port_type='ACCESS', gvrp='GVRP', queue_mem=8, native_vid=1, persist=True): smut_userid = zvmutils.get_smut_userid() msg = ('Start to create vswitch %s' % name) LOG.info(msg) rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Create_Extended" % smut_userid, "--operands", '-k switch_name=%s' % name)) if rdev is not None: rd += " -k real_device_address" +\ "=\'%s\'" % rdev.replace(',', ' ') if controller != '*': rd += " -k controller_name=%s" % controller rd = ' '.join((rd, "-k connection_value=%s" % connection, "-k queue_memory_limit=%s" % queue_mem, "-k transport_type=%s" % network_type, "-k vlan_id=%s" % vid, "-k persist=%s" % (persist and 'YES' or 'NO'))) # Only if vswitch is vlan awared, port_type, gvrp and native_vid are # allowed to specified if isinstance(vid, int) or vid.upper() != 'UNAWARE': rd = ' '.join((rd, "-k port_type=%s" % port_type, "-k gvrp_value=%s" % gvrp, "-k native_vlanid=%s" % native_vid)) if router is not None: rd += " -k routing_value=%s" % router msg = ('Start to create vswitch %s' % name) LOG.info(msg) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to create vswitch %s, error: %s" % (name, err.format_message())) raise msg = ('Create vswitch %s successfully' % name) LOG.info(msg) def set_vswitch(self, switch_name, **kwargs): """Set vswitch""" smut_userid = zvmutils.get_smut_userid() rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Set_Extended" % smut_userid, "--operands", "-k switch_name=%s" % switch_name)) for k, v in kwargs.items(): rd = ' '.join((rd, "-k %(key)s=\'%(value)s\'" % {'key': k, 'value': v})) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to set vswitch %s, error: %s" % (switch_name, err.format_message())) self._set_vswitch_exception(err, switch_name) def delete_vswitch(self, switch_name, persist=True): smut_userid = zvmutils.get_smut_userid() msg = ('Start to delete vswitch %s' % switch_name) LOG.info(msg) rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Delete_Extended" % smut_userid, "--operands", "-k switch_name=%s" % switch_name, "-k persist=%s" % (persist and 'YES' or 'NO'))) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: results = err.results if ((results['rc'] == 212) and (results['rs'] == 40)): LOG.warning("Vswitch %s does not exist", switch_name) return else: LOG.error("Failed to delete vswitch %s, error: %s" % (switch_name, err.format_message())) raise msg = ('Delete vswitch %s successfully' % switch_name) LOG.info(msg) def create_nic(self, userid, vdev=None, nic_id=None, mac_addr=None, active=False): nic_vdev = self._get_available_vdev(userid, vdev=vdev) LOG.debug('Nic attributes: vdev is %(vdev)s, ' 'ID is %(id)s, address is %(address)s', {'vdev': nic_vdev, 'id': nic_id or 'not specified', 'address': mac_addr or 'not specified'}) self._create_nic(userid, nic_vdev, nic_id=nic_id, mac_addr=mac_addr, active=active) return nic_vdev def _create_nic_inactive_exception(self, error, userid, vdev): if ((error.results['rc'] == 400) and (error.results['rs'] == 12)): obj_desc = "Guest %s" % userid raise exception.SDKConflictError(modID='network', rs=7, vdev=vdev, userid=userid, obj=obj_desc) elif ((error.results['rc'] == 404) and (error.results['rs'] == 12)): obj_desc = "Guest device %s" % vdev raise exception.SDKConflictError(modID='network', rs=7, vdev=vdev, userid=userid, obj=obj_desc) elif ((error.results['rc'] == 404) and (error.results['rs'] == 4)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=6, vdev=vdev, userid=userid, msg=errmsg) else: raise error def _create_nic_active_exception(self, error, userid, vdev): if (((error.results['rc'] == 204) and (error.results['rs'] == 4)) or ((error.results['rc'] == 204) and (error.results['rs'] == 28))): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=6, vdev=vdev, userid=userid, msg=errmsg) elif ((error.results['rc'] == 396) and (error.results['rs'] == 2797)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=6, vdev=vdev, userid=userid, msg=errmsg) else: raise error def _is_active(self, userid): # Get the vm status power_state = self.get_power_state(userid) if power_state == 'off': LOG.error('The vm %s is powered off, ' 'active operation is not allowed' % userid) raise exception.SDKConflictError(modID='network', rs=1, userid=userid) def _create_nic(self, userid, vdev, nic_id=None, mac_addr=None, active=False): if active: self._is_active(userid) msg = ('Start to create nic device %(vdev)s for guest %(vm)s' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) requestData = ' '.join(( 'SMAPI %s API Virtual_Network_Adapter_Create_Extended_DM' % userid, "--operands", "-k image_device_number=%s" % vdev, "-k adapter_type=QDIO")) if mac_addr is not None: mac = ''.join(mac_addr.split(':'))[6:] requestData += ' -k mac_id=%s' % mac try: self._request(requestData) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to create nic %s for user %s in " "the guest's user direct, error: %s" % (vdev, userid, err.format_message())) self._create_nic_inactive_exception(err, userid, vdev) if active: if mac_addr is not None: LOG.warning("Ignore the mac address %s when " "adding nic on an active system" % mac_addr) requestData = ' '.join(( 'SMAPI %s API Virtual_Network_Adapter_Create_Extended' % userid, "--operands", "-k image_device_number=%s" % vdev, "-k adapter_type=QDIO")) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err1: msg1 = err1.format_message() persist_OK = True requestData = ' '.join(( 'SMAPI %s API Virtual_Network_Adapter_Delete_DM' % userid, "--operands", '-v %s' % vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err2: results = err2.results msg2 = err2.format_message() if ((results['rc'] == 404) and (results['rs'] == 8)): persist_OK = True else: persist_OK = False if persist_OK: self._create_nic_active_exception(err1, userid, vdev) else: raise exception.SDKNetworkOperationError(rs=4, nic=vdev, userid=userid, create_err=msg1, revoke_err=msg2) self._NetDbOperator.switch_add_record(userid, vdev, port=nic_id) msg = ('Create nic device %(vdev)s for guest %(vm)s successfully' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) def get_user_direct(self, userid): with zvmutils.log_and_reraise_smut_request_failed(): results = self._request("getvm %s directory" % userid) return results.get('response', []) def _delete_nic_active_exception(self, error, userid, vdev): if ((error.results['rc'] == 204) and (error.results['rs'] == 28)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=8, vdev=vdev, userid=userid, msg=errmsg) else: raise error def _delete_nic_inactive_exception(self, error, userid, vdev): if ((error.results['rc'] == 400) and (error.results['rs'] == 12)): obj_desc = "Guest %s" % userid raise exception.SDKConflictError(modID='network', rs=9, vdev=vdev, userid=userid, obj=obj_desc) else: raise error def delete_nic(self, userid, vdev, active=False): if active: self._is_active(userid) vdev_exist = False nic_list = self._NetDbOperator.switch_select_record_for_userid(userid) for p in nic_list: if (int(p['interface'], 16) == int(vdev, 16)): vdev_exist = True vdev_info = p break if not vdev_exist: # Device has already be removed from user direct LOG.warning("Virtual device %s does not exist in the switch table", vdev) if active: try: resp = self.execute_cmd(userid, 'vmcp q %s' % vdev) nic_info = "%s ON NIC" % vdev.zfill(4).upper() osa_info = "%s ON OSA" % vdev.zfill(4).upper() if nic_info in resp[0]: pass elif osa_info in resp[0]: self._undedicate_nic(userid, vdev, active=active, del_active_only=True) return else: LOG.warning("Device %s of guest %s is not " "network adapter" % (vdev, userid)) return except exception.SDKSMUTRequestFailed as err: emsg = err.format_message() ignored_msg = ('Device %s does not exist' % vdev.zfill(4).upper()) if (emsg.__contains__(ignored_msg)): LOG.warning("Virtual device %s does not exist for " "active guest %s" % (vdev, userid)) return else: raise else: return else: # Device hasnot be removed from user direct, # check whether it is related to a dedicated OSA device if ((vdev_info["comments"] is not None) and (vdev_info["comments"].__contains__('OSA='))): self._undedicate_nic(userid, vdev, active=active) return msg = ('Start to delete nic device %(vdev)s for guest %(vm)s' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) if vdev_exist: rd = ' '.join(( "SMAPI %s API Virtual_Network_Adapter_Delete_DM" % userid, "--operands", '-v %s' % vdev)) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: results = err.results emsg = err.format_message() if ((results['rc'] == 404) and (results['rs'] == 8)): LOG.warning("Virtual device %s does not exist in " "the guest's user direct", vdev) else: LOG.error("Failed to delete nic %s for %s in " "the guest's user direct, error: %s" % (vdev, userid, emsg)) self._delete_nic_inactive_exception(err, userid, vdev) self._NetDbOperator.switch_delete_record_for_nic(userid, vdev) if active: rd = ' '.join(( "SMAPI %s API Virtual_Network_Adapter_Delete" % userid, "--operands", '-v %s' % vdev)) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: results = err.results emsg = err.format_message() if ((results['rc'] == 204) and (results['rs'] == 8)): LOG.warning("Virtual device %s does not exist on " "the active guest system", vdev) else: LOG.error("Failed to delete nic %s for %s on " "the active guest system, error: %s" % (vdev, userid, emsg)) self._delete_nic_active_exception(err, userid, vdev) msg = ('Delete nic device %(vdev)s for guest %(vm)s successfully' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) def _couple_active_exception(self, error, userid, vdev, vswitch): if ((error.results['rc'] == 212) and ((error.results['rs'] == 28) or (error.results['rs'] == 8))): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=10, vdev=vdev, userid=userid, vsw=vswitch, msg=errmsg) elif ((error.results['rc'] == 212) and (error.results['rs'] == 40)): obj_desc = "Vswitch %s" % vswitch raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 204) and (error.results['rs'] == 8)): obj_desc = "Guest device %s" % vdev raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 396) and ((error.results['rs'] == 2788) or (error.results['rs'] == 2848) or (error.results['rs'] == 3034) or (error.results['rs'] == 6011))): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=10, vdev=vdev, userid=userid, vsw=vswitch, msg=errmsg) else: raise error def _couple_inactive_exception(self, error, userid, vdev, vswitch): if ((error.results['rc'] == 412) and (error.results['rs'] == 28)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=10, vdev=vdev, userid=userid, vsw=vswitch, msg=errmsg) elif ((error.results['rc'] == 400) and (error.results['rs'] == 12)): obj_desc = "Guest %s" % userid raise exception.SDKConflictError(modID='network', rs=11, vdev=vdev, userid=userid, vsw=vswitch, obj=obj_desc) elif ((error.results['rc'] == 400) and (error.results['rs'] == 4)): obj_desc = "Guest %s" % vdev raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 404) and (error.results['rs'] == 12)): obj_desc = "Guest device %s" % vdev raise exception.SDKConflictError(modID='network', rs=11, vdev=vdev, userid=userid, vsw=vswitch, obj=obj_desc) elif ((error.results['rc'] == 404) and (error.results['rs'] == 8)): obj_desc = "Guest device %s" % vdev raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') else: raise error def _couple_nic(self, userid, vdev, vswitch_name, active=False): """Couple NIC to vswitch by adding vswitch into user direct.""" if active: self._is_active(userid) msg = ('Start to couple nic device %(vdev)s of guest %(vm)s ' 'with vswitch %(vsw)s' % {'vdev': vdev, 'vm': userid, 'vsw': vswitch_name}) LOG.info(msg) requestData = ' '.join(( 'SMAPI %s' % userid, "API Virtual_Network_Adapter_Connect_Vswitch_DM", "--operands", "-v %s" % vdev, "-n %s" % vswitch_name)) try: self._request(requestData) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to couple nic %s to vswitch %s for user %s " "in the guest's user direct, error: %s" % (vdev, vswitch_name, userid, err.format_message())) self._couple_inactive_exception(err, userid, vdev, vswitch_name) # the inst must be active, or this call will failed if active: requestData = ' '.join(( 'SMAPI %s' % userid, 'API Virtual_Network_Adapter_Connect_Vswitch', "--operands", "-v %s" % vdev, "-n %s" % vswitch_name)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err1: results1 = err1.results msg1 = err1.format_message() if ((results1 is not None) and (results1['rc'] == 204) and (results1['rs'] == 20)): LOG.warning("Virtual device %s already connected " "on the active guest system", vdev) else: persist_OK = True requestData = ' '.join(( 'SMAPI %s' % userid, 'API Virtual_Network_Adapter_Disconnect_DM', "--operands", '-v %s' % vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err2: results2 = err2.results msg2 = err2.format_message() if ((results2 is not None) and (results2['rc'] == 212) and (results2['rs'] == 32)): persist_OK = True else: persist_OK = False if persist_OK: self._couple_active_exception(err1, userid, vdev, vswitch_name) else: raise exception.SDKNetworkOperationError(rs=3, nic=vdev, vswitch=vswitch_name, couple_err=msg1, revoke_err=msg2) """Update information in switch table.""" self._NetDbOperator.switch_update_record_with_switch(userid, vdev, vswitch_name) msg = ('Couple nic device %(vdev)s of guest %(vm)s ' 'with vswitch %(vsw)s successfully' % {'vdev': vdev, 'vm': userid, 'vsw': vswitch_name}) LOG.info(msg) def couple_nic_to_vswitch(self, userid, nic_vdev, vswitch_name, active=False): """Couple nic to vswitch.""" if active: msg = ("both in the user direct of guest %s and on " "the active guest system" % userid) else: msg = "in the user direct of guest %s" % userid LOG.debug("Connect nic %s to switch %s %s", nic_vdev, vswitch_name, msg) self._couple_nic(userid, nic_vdev, vswitch_name, active=active) def _uncouple_active_exception(self, error, userid, vdev): if ((error.results['rc'] == 204) and (error.results['rs'] == 8)): obj_desc = "Guest device %s" % vdev raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 204) and (error.results['rs'] == 28)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=12, vdev=vdev, userid=userid, msg=errmsg) else: raise error def _uncouple_inactive_exception(self, error, userid, vdev): if ((error.results['rc'] == 404) and (error.results['rs'] == 8)): obj_desc = "Guest device %s" % vdev raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 400) and (error.results['rs'] == 4)): obj_desc = "Guest %s" % vdev raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') elif ((error.results['rc'] == 400) and (error.results['rs'] == 12)): obj_desc = "Guest %s" % userid raise exception.SDKConflictError(modID='network', rs=13, vdev=vdev, userid=userid, obj=obj_desc) else: raise error def _uncouple_nic(self, userid, vdev, active=False): """Uncouple NIC from vswitch""" if active: self._is_active(userid) msg = ('Start to uncouple nic device %(vdev)s of guest %(vm)s' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) requestData = ' '.join(( 'SMAPI %s' % userid, "API Virtual_Network_Adapter_Disconnect_DM", "--operands", "-v %s" % vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err: results = err.results emsg = err.format_message() if ((results is not None) and (results['rc'] == 212) and (results['rs'] == 32)): LOG.warning("Virtual device %s is already disconnected " "in the guest's user direct", vdev) else: LOG.error("Failed to uncouple nic %s in the guest's user " "direct, error: %s" % (vdev, emsg)) self._uncouple_inactive_exception(err, userid, vdev) """Update information in switch table.""" self._NetDbOperator.switch_update_record_with_switch(userid, vdev, None) # the inst must be active, or this call will failed if active: requestData = ' '.join(( 'SMAPI %s' % userid, 'API Virtual_Network_Adapter_Disconnect', "--operands", "-v %s" % vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err: results = err.results emsg = err.format_message() if ((results is not None) and (results['rc'] == 204) and (results['rs'] == 48)): LOG.warning("Virtual device %s is already " "disconnected on the active " "guest system", vdev) else: LOG.error("Failed to uncouple nic %s on the active " "guest system, error: %s" % (vdev, emsg)) self._uncouple_active_exception(err, userid, vdev) msg = ('Uncouple nic device %(vdev)s of guest %(vm)s successfully' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) def uncouple_nic_from_vswitch(self, userid, nic_vdev, active=False): if active: msg = ("both in the user direct of guest %s and on " "the active guest system" % userid) else: msg = "in the user direct of guest %s" % userid LOG.debug("Disconnect nic %s with network %s", nic_vdev, msg) self._uncouple_nic(userid, nic_vdev, active=active) def delete_userid(self, userid): rd = ' '.join(('deletevm', userid, 'directory')) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: if err.results['rc'] == 400 and err.results['rs'] == 4: # guest vm definition not found LOG.debug("The guest %s does not exist." % userid) return else: msg = "SMUT error: %s" % err.format_message() raise exception.SDKSMUTRequestFailed(err.results, msg) def delete_vm(self, userid): self.delete_userid(userid) # revoke userid from vswitch action = "revoke id %s authority from vswitch" % userid with zvmutils.log_and_reraise_sdkbase_error(action): switch_info = self._NetDbOperator.switch_select_record_for_userid( userid) switch_list = set() for item in switch_info: switch_list.add(item['switch']) for item in switch_list: if item is not None: self.revoke_user_from_vswitch(item, userid) # cleanup db record from network table action = "delete network record for user %s" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._NetDbOperator.switch_delete_record_for_userid(userid) # TODO: cleanup db record from volume table pass # cleanup persistent folder for guest self._pathutils.remove_guest_path(userid) # cleanup db record from guest table action = "delete guest %s from database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.delete_guest_by_userid(userid) def execute_cmd(self, userid, cmdStr): """"cmdVM.""" requestData = 'cmdVM ' + userid + ' CMD \'' + cmdStr + '\'' with zvmutils.log_and_reraise_smut_request_failed(action='execute ' 'command on vm via iucv channel'): results = self._request(requestData) ret = results['response'] return ret def execute_cmd_direct(self, userid, cmdStr): """"cmdVM.""" requestData = 'cmdVM ' + userid + ' CMD \'' + cmdStr + '\'' results = self._smut.request(requestData) return results def image_import(self, image_name, url, image_meta, remote_host=None): """Import the image specified in url to SDK image repository, and create a record in image db, the imported images are located in image_repository/prov_method/os_version/image_name/, for example, /opt/sdk/images/netboot/rhel7.2/90685d2b-167bimage/0100""" image_info = [] try: image_info = self._ImageDbOperator.image_query_record(image_name) except exception.SDKObjectNotExistError: msg = ("The image record %s doens't exist in SDK image datebase," " will import the image and create record now" % image_name) LOG.info(msg) # Ensure the specified image is not exist in image DB if image_info: msg = ("The image name %s has already exist in SDK image " "database, please check if they are same image or consider" " to use a different image name for import" % image_name) LOG.error(msg) raise exception.SDKImageOperationError(rs=13, img=image_name) try: image_os_version = image_meta['os_version'].lower() target_folder = self._pathutils.create_import_image_repository( image_os_version, const.IMAGE_TYPE['DEPLOY'], image_name) except Exception as err: msg = ('Failed to create repository to store image %(img)s with ' 'error: %(err)s, please make sure there are enough space ' 'on zvmsdk server and proper permission to create the ' 'repository' % {'img': image_name, 'err': six.text_type(err)}) LOG.error(msg) raise exception.SDKImageOperationError(rs=14, msg=msg) try: import_image_fn = urlparse.urlparse(url).path.split('/')[-1] import_image_fpath = '/'.join([target_folder, import_image_fn]) self._scheme2backend(urlparse.urlparse(url).scheme).image_import( image_name, url, import_image_fpath, remote_host=remote_host) # Check md5 after import to ensure import a correct image # TODO change to use query image name in DB expect_md5sum = image_meta.get('md5sum') real_md5sum = self._get_md5sum(import_image_fpath) if expect_md5sum and expect_md5sum != real_md5sum: msg = ("The md5sum after import is not same as source image," " the image has been broken") LOG.error(msg) raise exception.SDKImageOperationError(rs=4) # After import to image repository, figure out the image type is # single disk image or multiple-disk image,if multiple disks image, # extract it, if it's single image, rename its name to be same as # specific vdev # TODO: (nafei) use sub-function to check the image type image_type = 'rootonly' if image_type == 'rootonly': final_image_fpath = '/'.join([target_folder, CONF.zvm.user_root_vdev]) os.rename(import_image_fpath, final_image_fpath) elif image_type == 'alldisks': # For multiple disks image, extract it, after extract, the # content under image folder is like: 0100, 0101, 0102 # and remove the image file 0100-0101-0102.tgz pass # TODO: put multiple disk image into consideration, update the # disk_size_units and image_size db field disk_size_units = self._get_disk_size_units(final_image_fpath) image_size = self._get_image_size(final_image_fpath) # TODO: update the real_md5sum field to include each disk image self._ImageDbOperator.image_add_record(image_name, image_os_version, real_md5sum, disk_size_units, image_size, image_type) LOG.info("Image %s is import successfully" % image_name) except Exception: # Cleanup the image from image repository self._pathutils.clean_temp_folder(target_folder) raise def image_export(self, image_name, dest_url, remote_host=None): """Export the specific image to remote host or local file system :param image_name: image name that can be uniquely identify an image :param dest_path: the location to store exported image, eg. /opt/images, the image will be stored in folder /opt/images/ :param remote_host: the server that export image to, the format is username@IP eg. nova@192.168.99.1, if remote_host is None, it means the image will be stored in local server :returns a dictionary that contains the exported image info { 'image_name': the image_name that exported 'image_path': the image_path after exported 'os_version': the os version of the exported image 'md5sum': the md5sum of the original image } """ image_info = self._ImageDbOperator.image_query_record(image_name) if not image_info: msg = ("The image %s does not exist in image repository" % image_name) LOG.error(msg) raise exception.SDKImageOperationError(rs=20, img=image_name) image_type = image_info[0]['type'] # TODO: (nafei) according to image_type, detect image exported path # For multiple disk image, make the tgz firstly, the specify the # source_path to be something like: 0100-0101-0102.tgz if image_type == 'rootonly': source_path = '/'.join([CONF.image.sdk_image_repository, const.IMAGE_TYPE['DEPLOY'], image_info[0]['imageosdistro'], image_name, CONF.zvm.user_root_vdev]) else: pass self._scheme2backend(urlparse.urlparse(dest_url).scheme).image_export( source_path, dest_url, remote_host=remote_host) # TODO: (nafei) for multiple disks image, update the expect_dict # to be the tgz's md5sum export_dict = {'image_name': image_name, 'image_path': dest_url, 'os_version': image_info[0]['imageosdistro'], 'md5sum': image_info[0]['md5sum']} LOG.info("Image %s export successfully" % image_name) return export_dict def _get_image_disk_size_units(self, image_path): """ Return a comma separated string to indicate the image disk size and units for each image disk file under image_path For single disk image , it looks like: 0100=3338:CYL For multiple disk image, it looks like: 0100=3338:CYL,0101=4194200:BLK, 0102=4370:CYL""" pass def _get_disk_size_units(self, image_path): command = 'hexdump -n 48 -C %s' % image_path (rc, output) = zvmutils.execute(command) LOG.debug("hexdump result is %s" % output) if rc: msg = ("Error happened when executing command hexdump with" "reason: %s" % output) LOG.error(msg) raise exception.SDKImageOperationError(rs=5) 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 by SDK" % image_path) LOG.error(msg) raise exception.SDKImageOperationError(rs=6) if 'FBA' not in output and 'CKD' not in output: raise exception.SDKImageOperationError(rs=7) LOG.debug("The image's root_disk_units is %s" % root_disk_units) return root_disk_units def _get_image_size(self, image_path): """Return disk size in bytes""" command = 'du -b %s' % image_path (rc, output) = zvmutils.execute(command) if rc: msg = ("Error happened when executing command du -b with" "reason: %s" % output) LOG.error(msg) raise exception.SDKImageOperationError(rs=8) size = output.split()[0] return size def _get_image_path_by_name(self, image_name): try: target_info = self._ImageDbOperator.image_query_record(image_name) except exception.SDKObjectNotExistError: msg = ("The image %s does not exist in image repository" % image_name) LOG.error(msg) raise exception.SDKImageOperationError(rs=20, img=image_name) # TODO: (nafei) Handle multiple disks image deploy image_path = '/'.join([CONF.image.sdk_image_repository, const.IMAGE_TYPE['DEPLOY'], target_info[0]['imageosdistro'], image_name]) return image_path def _scheme2backend(self, scheme): try: return { "file": FilesystemBackend, "http": HTTPBackend, # "https": HTTPSBackend }[scheme] except KeyError: msg = ("No backend found for '%s'" % scheme) LOG.err(msg) raise exception.SDKImageOperationError(rs=2, schema=scheme) def _get_md5sum(self, fpath): """Calculate the md5sum of the specific image file""" try: current_md5 = hashlib.md5() if isinstance(fpath, six.string_types) and os.path.exists(fpath): with open(fpath, "rb") as fh: for chunk in self._read_chunks(fh): current_md5.update(chunk) elif (fpath.__class__.__name__ in ["StringIO", "StringO"] or isinstance(fpath, file)): for chunk in self._read_chunks(fpath): current_md5.update(chunk) else: return "" return current_md5.hexdigest() except Exception: msg = ("Failed to calculate the image's md5sum") LOG.error(msg) raise exception.SDKImageOperationError(rs=3) def _read_chunks(self, fh): fh.seek(0) chunk = fh.read(CHUNKSIZE) while chunk: yield chunk chunk = fh.read(CHUNKSIZE) else: fh.seek(0) def image_delete(self, image_name): # Delete image file try: self._delete_image_file(image_name) # Delete image record from db self._ImageDbOperator.image_delete_record(image_name) except exception.SDKImageOperationError as err: results = err.results if ((results['rc'] == 300) and (results['rs'] == 20)): LOG.warning("Image %s does not exist", image_name) return else: LOG.error("Failed to delete image %s, error: %s" % (image_name, err.format_message())) raise msg = ('Delete image %s successfully' % image_name) LOG.info(msg) def _delete_image_file(self, image_name): image_path = self._get_image_path_by_name(image_name) self._pathutils.clean_temp_folder(image_path) def image_query(self, imagename=None): return self._ImageDbOperator.image_query_record(imagename) def image_get_root_disk_size(self, image_name): """Return the root disk units of the specified image image_name: the unique image name in db Return the disk units in format like 3339:CYL or 467200:BLK """ image_info = self.image_query(image_name) if not image_info: raise exception.SDKImageOperationError(rs=20, img=image_name) disk_size_units = image_info[0]['disk_size_units'].split(':')[0] return disk_size_units def punch_file(self, userid, fn, fclass): rd = ("changevm %(uid)s punchfile %(file)s --class %(class)s" % {'uid': userid, 'file': fn, 'class': fclass}) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: LOG.error("Failed to punch file to userid '%s'," "error: %s" % (userid, err.format_message())) raise finally: os.remove(fn) def get_guest_connection_status(self, userid): '''Get guest vm connection status.''' rd = ' '.join(('getvm', userid, 'isreachable')) results = self._request(rd) if results['rs'] == 1: return True else: return False def _generate_disk_parmline(self, vdev, fmt, mntdir): parms = [ 'action=' + 'addMdisk', 'vaddr=' + vdev, 'filesys=' + fmt, 'mntdir=' + mntdir ] parmline = ' '.join(parms) parmstr = "'" + parmline + "'" return parmstr def process_additional_minidisks(self, userid, disk_info): '''Generate and punch the scripts used to process additional disk into target vm's reader. ''' for idx, disk in enumerate(disk_info): vdev = disk.get('vdev') or self.generate_disk_vdev( offset = (idx + 1)) fmt = disk.get('format') mount_dir = disk.get('mntdir') or ''.join(['/mnt/ephemeral', str(vdev)]) disk_parms = self._generate_disk_parmline(vdev, fmt, mount_dir) func_name = '/var/lib/zvmsdk/setupDisk' self.aemod_handler(userid, func_name, disk_parms) # trigger do-script if self.get_power_state(userid) == 'on': self.execute_cmd(userid, "/usr/bin/zvmguestconfigure start") def aemod_handler(self, instance_name, func_name, parms): rd = ' '.join(['changevm', instance_name, 'aemod', func_name, '--invparms', parms]) action = parms[0] + instance_name with zvmutils.log_and_reraise_smut_request_failed(action): self._request(rd) def get_user_console_output(self, userid): # get console into reader rd = 'getvm %s consoleoutput' % userid action = 'get console log reader file list for guest vm: %s' % userid with zvmutils.log_and_reraise_smut_request_failed(action): resp = self._request(rd) with zvmutils.expect_invalid_resp_data(resp): rf_list = resp['response'][0].rpartition(':')[2].strip().split() # TODO: make sure reader device is online # via 'cat /sys/bus/ccw/drivers/vmur/0.0.000c/online' # 'sudo /sbin/cio_ignore -r 000c; sudo /sbin/chccwdev -e 000c' # 'which udevadm &> /dev/null && udevadm settle || udevsettle' logs = [] for rf in rf_list: cmd = 'sudo /usr/sbin/vmur re -t -O %s' % rf rc, output = zvmutils.execute(cmd) if rc == 0: logs.append(output) return ''.join(logs) def query_vswitch(self, switch_name): smut_userid = zvmutils.get_smut_userid() rd = ' '.join(( "SMAPI %s API Virtual_Network_Vswitch_Query_Extended" % smut_userid, "--operands", '-k switch_name=%s' % switch_name )) try: results = self._request(rd) rd_list = results['response'] except exception.SDKSMUTRequestFailed as err: if ((err.results['rc'] == 212) and (err.results['rs'] == 40)): msg = 'Vswitch %s does not exist' % switch_name LOG.error(msg) obj_desc = "Vswitch %s" % switch_name raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID='network') else: action = "query vswitch details info" msg = "Failed to %s. " % action msg += "SMUT error: %s" % err.format_message() LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) vsw_info = {} with zvmutils.expect_invalid_resp_data(): # ignore user_vlan_id part and jump to the vswitch basic info idx_end = len(rd_list) idx = 0 while((idx < idx_end) and not rd_list[idx].__contains__('switch_name')): idx = idx + 1 # The next 21 lines contains the vswitch basic info # eg, name, type, port_type, vlan_awareness, etc for i in range(21): rd = rd_list[idx + i].split(':') vsw_info[rd[0].strip()] = rd[1].strip() idx = idx + 21 # Skip the vepa_status while((idx < idx_end) and not rd_list[idx].__contains__('real_device_address') and not rd_list[idx].__contains__('port_num') and not rd_list[idx].__contains__('adapter_owner')): idx = idx + 1 def _parse_value(data_list, idx, keyword, offset=1): value = data_list[idx].rpartition(keyword)[2].strip() if value == '(NONE)': value = 'NONE' return idx + offset, value def _parse_dev_status(value): if value in const.DEV_STATUS.keys(): return const.DEV_STATUS[value] else: return 'Unknown' def _parse_dev_err(value): if value in const.DEV_ERROR.keys(): return const.DEV_ERROR[value] else: return 'Unknown' # Start to analyse the real devices info vsw_info['real_devices'] = {} while((idx < idx_end) and rd_list[idx].__contains__('real_device_address')): # each rdev has 6 lines' info idx, rdev_addr = _parse_value(rd_list, idx, 'real_device_address: ') idx, vdev_addr = _parse_value(rd_list, idx, 'virtual_device_address: ') idx, controller = _parse_value(rd_list, idx, 'controller_name: ') idx, port_name = _parse_value(rd_list, idx, 'port_name: ') idx, dev_status = _parse_value(rd_list, idx, 'device_status: ') idx, dev_err = _parse_value(rd_list, idx, 'device_error_status ') vsw_info['real_devices'][rdev_addr] = {'vdev': vdev_addr, 'controller': controller, 'port_name': port_name, 'dev_status': _parse_dev_status( dev_status), 'dev_err': _parse_dev_err( dev_err) } # Under some case there would be an error line in the output # "Error controller_name is NULL!!", skip this line if ((idx < idx_end) and rd_list[idx].__contains__( 'Error controller_name is NULL!!')): idx += 1 # Start to get the authorized userids vsw_info['authorized_users'] = {} while((idx < idx_end) and rd_list[idx].__contains__('port_num')): # each authorized userid has 6 lines' info at least idx, port_num = _parse_value(rd_list, idx, 'port_num: ') idx, userid = _parse_value(rd_list, idx, 'grant_userid: ') idx, prom_mode = _parse_value(rd_list, idx, 'promiscuous_mode: ') idx, osd_sim = _parse_value(rd_list, idx, 'osd_sim: ') idx, vlan_count = _parse_value(rd_list, idx, 'vlan_count: ') vlan_ids = [] for i in range(int(vlan_count)): idx, id = _parse_value(rd_list, idx, 'user_vlan_id: ') vlan_ids.append(id) # For vlan unaware vswitch, the query smcli would # return vlan_count as 1, here we just set the count to 0 if (vsw_info['vlan_awareness'] == 'UNAWARE'): vlan_count = 0 vlan_ids = [] vsw_info['authorized_users'][userid] = { 'port_num': port_num, 'prom_mode': prom_mode, 'osd_sim': osd_sim, 'vlan_count': vlan_count, 'vlan_ids': vlan_ids } # Start to get the connected adapters info # OWNER_VDEV would be used as the dict key for each adapter vsw_info['adapters'] = {} while((idx < idx_end) and rd_list[idx].__contains__('adapter_owner')): # each adapter has four line info: owner, vdev, macaddr, type idx, owner = _parse_value(rd_list, idx, 'adapter_owner: ') idx, vdev = _parse_value(rd_list, idx, 'adapter_vdev: ') idx, mac = _parse_value(rd_list, idx, 'adapter_macaddr: ') idx, type = _parse_value(rd_list, idx, 'adapter_type: ') key = owner + '_' + vdev vsw_info['adapters'][key] = { 'mac': mac, 'type': type } # Todo: analyze and add the uplink NIC info and global member info def _parse_switch_status(value): if value in const.SWITCH_STATUS.keys(): return const.SWITCH_STATUS[value] else: return 'Unknown' if 'switch_status' in vsw_info.keys(): vsw_info['switch_status'] = _parse_switch_status( vsw_info['switch_status']) return vsw_info def get_nic_info(self, userid=None, nic_id=None, vswitch=None): nic_info = self._NetDbOperator.switch_select_record(userid=userid, nic_id=nic_id, vswitch=vswitch) return nic_info def is_first_network_config(self, userid): action = "get guest '%s' to database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): info = self._GuestDbOperator.get_guest_by_userid(userid) # check net_set if int(info[3]) == 0: return True else: return False def update_guestdb_with_net_set(self, userid): action = "update guest '%s' in database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.update_guest_by_userid(userid, net_set='1') def _is_OSA_free(self, OSA_device): osa_info = self._query_OSA() if 'OSA' not in osa_info.keys(): return False elif len(osa_info['OSA']['FREE']) == 0: return False else: dev1 = str(OSA_device).zfill(4).upper() dev2 = str(str(hex(int(OSA_device, 16) + 1))[2:]).zfill(4).upper() dev3 = str(str(hex(int(OSA_device, 16) + 2))[2:]).zfill(4).upper() if ((dev1 in osa_info['OSA']['FREE']) and (dev2 in osa_info['OSA']['FREE']) and (dev3 in osa_info['OSA']['FREE'])): return True else: return False def _query_OSA(self): smut_userid = zvmutils.get_smut_userid() rd = "SMAPI %s API Virtual_Network_OSA_Query" % smut_userid OSA_info = {} try: results = self._request(rd) rd_list = results['response'] except exception.SDKSMUTRequestFailed as err: if ((err.results['rc'] == 4) and (err.results['rs'] == 4)): msg = 'No OSAs on system' LOG.info(msg) return OSA_info else: action = "query OSA details info" msg = "Failed to %s. " % action msg += "SMUT error: %s" % err.format_message() LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) with zvmutils.expect_invalid_resp_data(): idx_end = len(rd_list) idx = 0 def _parse_value(data_list, idx, keyword, offset=1): value = data_list[idx].rpartition(keyword)[2].strip() return idx + offset, value # Start to analyse the osa devices info while((idx < idx_end) and rd_list[idx].__contains__('OSA Address')): idx, osa_addr = _parse_value(rd_list, idx, 'OSA Address: ') idx, osa_status = _parse_value(rd_list, idx, 'OSA Status: ') idx, osa_type = _parse_value(rd_list, idx, 'OSA Type: ') if osa_type != 'UNKNOWN': idx, CHPID_addr = _parse_value(rd_list, idx, 'CHPID Address: ') idx, Agent_status = _parse_value(rd_list, idx, 'Agent Status: ') if osa_type not in OSA_info.keys(): OSA_info[osa_type] = {} OSA_info[osa_type]['FREE'] = [] OSA_info[osa_type]['BOXED'] = [] OSA_info[osa_type]['OFFLINE'] = [] OSA_info[osa_type]['ATTACHED'] = [] if osa_status.__contains__('ATT'): id = osa_status.split()[1] item = (id, osa_addr) OSA_info[osa_type]['ATTACHED'].append(item) else: OSA_info[osa_type][osa_status].append(osa_addr) return OSA_info def _get_available_vdev(self, userid, vdev=None): ports_info = self._NetDbOperator.switch_select_table() vdev_info = [] for p in ports_info: if p['userid'] == userid.upper(): vdev_info.append(p['interface']) if len(vdev_info) == 0: # no nic defined for the guest if vdev is None: nic_vdev = CONF.zvm.default_nic_vdev else: nic_vdev = vdev else: if vdev is None: used_vdev = max(vdev_info) nic_vdev = str(hex(int(used_vdev, 16) + 3))[2:] else: if self._is_vdev_valid(vdev, vdev_info): nic_vdev = vdev else: errmsg = ("The specified virtual device number %s " "has already been used." % vdev) raise exception.SDKConflictError(modID='network', rs=6, vdev=vdev, userid=userid, msg=errmsg) if ((len(nic_vdev) > 4) or (len(str(hex(int(nic_vdev, 16) + 2))[2:]) > 4)): errmsg = ("Virtual device number %s is not valid" % nic_vdev) raise exception.SDKInvalidInputFormat(msg=errmsg) return nic_vdev def dedicate_OSA(self, userid, OSA_device, vdev=None, active=False): nic_vdev = self._get_available_vdev(userid, vdev=vdev) if not self._is_OSA_free(OSA_device): errmsg = ("The specified OSA device number %s " "is not free" % OSA_device) raise exception.SDKConflictError(modID='network', rs=14, osa=OSA_device, userid=userid, msg=errmsg) LOG.debug('Nic attributes: vdev is %(vdev)s, ' 'dedicated OSA device is %(osa)s', {'vdev': nic_vdev, 'osa': OSA_device}) self._dedicate_OSA(userid, OSA_device, nic_vdev, active=active) return nic_vdev def _dedicate_OSA_inactive_exception(self, error, userid, vdev, OSA_device): if ((error.results['rc'] == 400) and (error.results['rs'] == 12)): obj_desc = "Guest %s" % userid raise exception.SDKConflictError(modID='network', rs=15, osa=OSA_device, userid=userid, obj=obj_desc) elif ((error.results['rc'] == 404) and (error.results['rs'] == 12)): obj_desc = "Guest device %s" % vdev raise exception.SDKConflictError(modID='network', rs=15, osa=OSA_device, userid=userid, obj=obj_desc) elif ((error.results['rc'] == 404) and (error.results['rs'] == 4)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=14, osa=OSA_device, userid=userid, msg=errmsg) else: raise error def _dedicate_OSA_active_exception(self, error, userid, OSA_device): if (((error.results['rc'] == 204) and (error.results['rs'] == 4)) or ((error.results['rc'] == 204) and (error.results['rs'] == 8)) or ((error.results['rc'] == 204) and (error.results['rs'] == 16))): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=14, osa=OSA_device, userid=userid, msg=errmsg) else: raise error def _dedicate_OSA(self, userid, OSA_device, vdev, active=False): if active: self._is_active(userid) msg = ('Start to dedicate nic device %(vdev)s of guest %(vm)s ' 'to OSA device %(osa)s' % {'vdev': vdev, 'vm': userid, 'osa': OSA_device}) LOG.info(msg) def_vdev = vdev att_OSA_device = OSA_device for i in range(3): requestData = ' '.join(( 'SMAPI %s API Image_Device_Dedicate_DM' % userid, "--operands", "-v %s" % def_vdev, "-r %s" % att_OSA_device)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err: LOG.error("Failed to dedicate OSA %s to nic %s for user %s " "in the guest's user direct, error: %s" % (att_OSA_device, def_vdev, userid, err.format_message())) # TODO revoke the dedicated OSA in user direct while (int(def_vdev, 16) != int(vdev, 16)): def_vdev = str(hex(int(def_vdev, 16) - 1))[2:] requestData = ' '.join(( 'SMAPI %s API Image_Device_Undedicate_DM' % userid, "--operands", "-v %s" % def_vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err2: if ((err2.results['rc'] == 404) and (err2.results['rs'] == 8)): pass else: LOG.error("Failed to Undedicate nic %s for user" " %s in the guest's user direct, " "error: %s" % (def_vdev, userid, err2.format_message())) pass self._dedicate_OSA_inactive_exception(err, userid, vdev, OSA_device) def_vdev = str(hex(int(def_vdev, 16) + 1))[2:] att_OSA_device = str(hex(int(att_OSA_device, 16) + 1))[2:] if active: def_vdev = vdev att_OSA_device = OSA_device for i in range(3): requestData = ' '.join(( 'SMAPI %s API Image_Device_Dedicate' % userid, "--operands", "-v %s" % def_vdev, "-r %s" % att_OSA_device)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err: LOG.error("Failed to dedicate OSA %s to nic %s for user " "%s on the active guest system, error: %s" % (att_OSA_device, def_vdev, userid, err.format_message())) # TODO revoke the dedicated OSA in user direct and active detach_vdev = vdev for j in range(3): requestData = ' '.join(( 'SMAPI %s API Image_Device_Undedicate_DM' % userid, "--operands", "-v %s" % detach_vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err2: if ((err2.results['rc'] == 404) and (err2.results['rs'] == 8)): pass else: LOG.error("Failed to Undedicate nic %s for " "user %s in the guest's user " "direct, error: %s" % (def_vdev, userid, err2.format_message())) pass detach_vdev = str(hex(int(detach_vdev, 16) + 1))[2:] while (int(def_vdev, 16) != int(vdev, 16)): def_vdev = str(hex(int(def_vdev, 16) - 1))[2:] requestData = ' '.join(( 'SMAPI %s API Image_Device_Undedicate' % userid, "--operands", "-v %s" % def_vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err3: if ((err3.results['rc'] == 204) and (err3.results['rs'] == 8)): pass else: LOG.error("Failed to Undedicate nic %s for " "user %s on the active guest " "system, error: %s" % (def_vdev, userid, err3.format_message())) pass self._dedicate_OSA_active_exception(err, userid, OSA_device) def_vdev = str(hex(int(def_vdev, 16) + 1))[2:] att_OSA_device = str(hex(int(att_OSA_device, 16) + 1))[2:] OSA_desc = 'OSA=%s' % OSA_device self._NetDbOperator.switch_add_record(userid, vdev, comments=OSA_desc) msg = ('Dedicate nic device %(vdev)s of guest %(vm)s ' 'to OSA device %(osa)s successfully' % {'vdev': vdev, 'vm': userid, 'osa': OSA_device}) LOG.info(msg) def _undedicate_nic_active_exception(self, error, userid, vdev): if ((error.results['rc'] == 204) and (error.results['rs'] == 44)): errmsg = error.format_message() raise exception.SDKConflictError(modID='network', rs=16, userid=userid, vdev=vdev, msg=errmsg) else: raise error def _undedicate_nic_inactive_exception(self, error, userid, vdev): if ((error.results['rc'] == 400) and (error.results['rs'] == 12)): obj_desc = "Guest %s" % userid raise exception.SDKConflictError(modID='network', rs=17, userid=userid, vdev=vdev, obj=obj_desc) else: raise error def _undedicate_nic(self, userid, vdev, active=False, del_active_only=False): if active: self._is_active(userid) msg = ('Start to undedicate nic device %(vdev)s of guest %(vm)s' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) if not del_active_only: def_vdev = vdev for i in range(3): requestData = ' '.join(( 'SMAPI %s API Image_Device_Undedicate_DM' % userid, "--operands", "-v %s" % def_vdev)) try: self._request(requestData) except (exception.SDKSMUTRequestFailed, exception.SDKInternalError) as err: results = err.results emsg = err.format_message() if ((results['rc'] == 404) and (results['rs'] == 8)): LOG.warning("Virtual device %s does not exist in " "the guest's user direct", vdev) else: LOG.error("Failed to undedicate nic %s for %s in " "the guest's user direct, error: %s" % (vdev, userid, emsg)) self._undedicate_nic_inactive_exception(err, userid, vdev) def_vdev = str(hex(int(def_vdev, 16) + 1))[2:] self._NetDbOperator.switch_delete_record_for_nic(userid, vdev) if active: def_vdev = vdev for i in range(3): rd = ' '.join(( "SMAPI %s API Image_Device_Undedicate" % userid, "--operands", '-v %s' % def_vdev)) try: self._request(rd) except exception.SDKSMUTRequestFailed as err: results = err.results emsg = err.format_message() if ((results['rc'] == 204) and (results['rs'] == 8)): LOG.warning("Virtual device %s does not exist on " "the active guest system", vdev) else: LOG.error("Failed to undedicate nic %s for %s on " "the active guest system, error: %s" % (vdev, userid, emsg)) self._undedicate_nic_active_exception(err, userid, vdev) def_vdev = str(hex(int(def_vdev, 16) + 1))[2:] msg = ('Undedicate nic device %(vdev)s of guest %(vm)s successfully' % {'vdev': vdev, 'vm': userid}) LOG.info(msg) def _request_with_error_ignored(self, rd): """Send smut request, log and ignore any errors.""" try: return self._request(rd) except Exception as err: # log as warning and ignore namelist operation failures LOG.warn(six.text_type(err)) def namelist_add(self, namelist, userid): rd = ''.join(("SMAPI %s API Name_List_Add " % namelist, "--operands -n %s" % userid)) self._request_with_error_ignored(rd) def namelist_remove(self, namelist, userid): rd = ''.join(("SMAPI %s API Name_List_Remove " % namelist, "--operands -n %s" % userid)) self._request_with_error_ignored(rd) def namelist_query(self, namelist): rd = "SMAPI %s API Name_List_Query" % namelist resp = self._request_with_error_ignored(rd) if resp is not None: return resp['response'] else: return [] def namelist_destroy(self, namelist): rd = "SMAPI %s API Name_List_Destroy" % namelist self._request_with_error_ignored(rd) def _get_defined_cpu_addrs(self, userid): user_direct = self.get_user_direct(userid) defined_addrs = [] max_cpus = 0 for ent in user_direct: if ent.startswith("CPU"): cpu_addr = ent.split()[1].strip().upper() defined_addrs.append(cpu_addr) if ent.startswith("MACHINE ESA"): max_cpus = int(ent.split()[2].strip()) return (max_cpus, defined_addrs) def _get_available_cpu_addrs(self, used_addrs, max_cpus): # Get available CPU addresses that are not defined in user entry used_set = set(used_addrs) available_addrs = set([hex(i)[2:].rjust(2, '0').upper() for i in range(0, max_cpus)]) available_addrs.difference_update(used_set) return list(available_addrs) def _get_active_cpu_addrs(self, userid): # Get the active cpu addrs in two-digit hex string in upper case # Sample output for 'lscpu --parse=ADDRESS': # # The following is the parsable format, which can be fed to other # # programs. Each different item in every column has an unique ID # # starting from zero. # # Address # 0 # 1 active_addrs = [] active_cpus = self.execute_cmd(userid, "lscpu --parse=ADDRESS") for c in active_cpus: # Skip the comment lines at beginning if c.startswith("# "): continue addr = hex(int(c.strip()))[2:].rjust(2, '0').upper() active_addrs.append(addr) return active_addrs def resize_cpus(self, userid, count): # Check defined cpus in user entry. If greater than requested, then # delete cpus. Otherwise, add new cpus. # Return value: for revert usage, a tuple of # action: The action taken for this resize, possible values: # 0: no action, 1: add cpu, 2: delete cpu # cpu_addrs: list of influenced cpu addrs action = 0 updated_addrs = [] (max_cpus, defined_addrs) = self._get_defined_cpu_addrs(userid) defined_count = len(defined_addrs) # Check maximum cpu count defined if max_cpus == 0: LOG.error("Resize for guest '%s' cann't be done. The maximum " "number of cpus is not defined in user directory." % userid) raise exception.SDKConflictError(modID='guest', rs=3, userid=userid) # Check requested count is less than the maximum cpus if count > max_cpus: LOG.error("Resize for guest '%s' cann't be done. The " "requested number of cpus: '%i' exceeds the maximum " "number of cpus allowed: '%i'." % (userid, count, max_cpus)) raise exception.SDKConflictError(modID='guest', rs=4, userid=userid, req=count, max=max_cpus) # Check count and take action if defined_count == count: LOG.info("The number of current defined CPUs in user '%s' equals " "to requested count: %i, no action for static resize" "needed." % (userid, count)) return (action, updated_addrs, max_cpus) elif defined_count < count: action = 1 # add more CPUs available_addrs = self._get_available_cpu_addrs(defined_addrs, max_cpus) # sort the list and get the first few addrs to use available_addrs.sort() # Define new cpus in user directory rd = ''.join(("SMAPI %s API Image_Definition_Update_DM " % userid, "--operands")) updated_addrs = available_addrs[0:count - defined_count] for addr in updated_addrs: rd += (" -k CPU=CPUADDR=%s" % addr) try: self._request(rd) except exception.SDKSMUTRequestFailed as e: msg = ("Define new cpus in user directory for '%s' failed with" " SMUT error: %s" % (userid, e.format_message())) LOG.error(msg) raise exception.SDKGuestOperationError(rs=6, userid=userid, err=e.format_message()) LOG.info("New CPUs defined in user directory for '%s' " "successfully" % userid) return (action, updated_addrs, max_cpus) else: action = 2 # Delete CPUs defined_addrs.sort() updated_addrs = defined_addrs[-(defined_count - count):] # Delete the last few cpus in user directory rd = ''.join(("SMAPI %s API Image_Definition_Delete_DM " % userid, "--operands")) for addr in updated_addrs: rd += (" -k CPU=CPUADDR=%s" % addr) try: self._request(rd) except exception.SDKSMUTRequestFailed as e: msg = ("Delete CPUs in user directory for '%s' failed with" " SMUT error: %s" % (userid, e.format_message())) LOG.error(msg) raise exception.SDKGuestOperationError(rs=6, userid=userid, err=e.format_message()) LOG.info("CPUs '%s' deleted from user directory for '%s' " "successfully" % (str(updated_addrs), userid)) return (action, updated_addrs, max_cpus) def live_resize_cpus(self, userid, count): # Get active cpu count and compare with requested count # If request count is smaller than the current count, then report # error and exit immediately. active_addrs = self._get_active_cpu_addrs(userid) active_count = len(active_addrs) if active_count > count: LOG.error("Failed to live resize cpus of guest: %(uid)s, " "current active cpu count: %(cur)i is greater than " "the requested count: %(req)i." % {'uid': userid, 'cur': active_count, 'req': count}) raise exception.SDKConflictError(modID='guest', rs=2, userid=userid, active=active_count, req=count) # Static resize CPUs. (add or delete CPUs from user directory) (action, updated_addrs, max_cpus) = self.resize_cpus(userid, count) if active_count == count: # active count equals to requested LOG.info("Current active cpu count of guest: '%s' equals to the " "requested count: '%i', no more actions needed for " "live resize." % (userid, count)) LOG.info("Live resize cpus for guest: '%s' finished successfully." % userid) return else: # Get the number of cpus to add to active and check address active_free = self._get_available_cpu_addrs(active_addrs, max_cpus) active_free.sort() active_new = active_free[0:count - active_count] # Do live resize # Define new cpus cmd_str = "vmcp def cpu " + ' '.join(active_new) try: self.execute_cmd(userid, cmd_str) except exception.SDKSMUTRequestFailed as err1: # rollback and return msg1 = ("Define cpu of guest: '%s' to active failed with . " "error: %s." % (userid, err1.format_message())) # Start to do rollback if action == 0: LOG.error(msg1) else: LOG.error(msg1 + (" Will revert the user directory " "change.")) # Combine influenced cpu addrs cpu_entries = "" for addr in updated_addrs: cpu_entries += (" -k CPU=CPUADDR=%s" % addr) rd = '' if action == 1: # Delete added CPUs rd = ''.join(("SMAPI %s API Image_Definition_Delete_DM" % userid, " --operands")) else: # Add deleted CPUs rd = ''.join(("SMAPI %s API Image_Definition_Create_DM" % userid, " --operands")) rd += cpu_entries try: self._request(rd) except exception.SDKSMUTRequestFailed as err2: msg = ("Failed to revert user directory change for '" "%s', SMUT error: %s" % (userid, err2.format_message())) LOG.error(msg) else: LOG.info("Revert user directory change for '%s' " "successfully." % userid) # Finally raise the exception raise exception.SDKGuestOperationError( rs=7, userid=userid, err=err1.format_message()) # Activate successfully, rescan in Linux layer to hot-plug new cpus LOG.info("Added new CPUs to active configuration of guest '%s'" % userid) try: self.execute_cmd(userid, "chcpu -r") except exception.SDKSMUTRequestFailed as err: msg = err.format_message() LOG.error("Rescan cpus to hot-plug new defined cpus for guest: " "'%s' failed with error: %s. No rollback is done and you" "may need to check the status and restart the guest to " "make the defined cpus online." % (userid, msg)) raise exception.SDKGuestOperationError(rs=8, userid=userid, err=msg) LOG.info("Live resize cpus for guest: '%s' finished successfully." % userid) def _get_defined_memory(self, userid): user_direct = self.get_user_direct(userid) defined_mem = max_mem = reserved_mem = -1 for ent in user_direct: # u'USER userid password storage max privclass' if ent.startswith("USER "): fields = ent.split(' ') if len(fields) != 6: # This case should not exist if the target user # is created by zcc and not updated manually by user break defined_mem = int(zvmutils.convert_to_mb(fields[3])) max_mem = int(zvmutils.convert_to_mb(fields[4])) # For legacy guests, the reserved memory may not be defined if ent.startswith("COMMAND DEF STOR RESERVED"): reserved_mem = int(zvmutils.convert_to_mb(ent.split(' ')[4])) return (defined_mem, max_mem, reserved_mem, user_direct) def _replace_user_direct(self, userid, user_entry): # user_entry can be a list or a string entry_str = "" if isinstance(user_entry, list): for ent in user_entry: if ent == "": # skip empty line continue else: entry_str += (ent + '\n') else: entry_str = user_entry tmp_folder = tempfile.mkdtemp() tmp_user_direct = os.path.join(tmp_folder, userid) with open(tmp_user_direct, 'w') as f: f.write(entry_str) rd = ''.join(("SMAPI %s API Image_Replace_DM " % userid, "--operands ", "-f %s" % tmp_user_direct)) try: self._request(rd) except exception.SDKSMUTRequestFailed as err1: msg = ("Replace definition of guest '%s' failed with " "SMUT error: %s." % (userid, err1.format_message())) LOG.error(msg) LOG.debug("Unlocking the user directory.") rd = ("SMAPI %s API Image_Unlock_DM " % userid) try: self._request(rd) except exception.SDKSMUTRequestFailed as err2: # ignore 'not locked' error if ((err2.results['rc'] == 400) and ( err2.results['rs'] == 24)): LOG.debug("Guest '%s' unlocked successfully." % userid) pass else: # just print error and ignore this unlock error msg = ("Unlock definition of guest '%s' failed " "with SMUT error: %s" % (userid, err2.format_message())) LOG.error(msg) else: LOG.debug("Guest '%s' unlocked successfully." % userid) # at the end, raise the replace error for upper layer to handle raise err1 finally: self._pathutils.clean_temp_folder(tmp_folder) def _lock_user_direct(self, userid): rd = ("SMAPI %s API Image_Lock_DM " % userid) try: self._request(rd) except exception.SDKSMUTRequestFailed as e: # ignore the "already locked" error if ((e.results['rc'] == 400) and (e.results['rs'] == 12)): LOG.debug("Image is already unlocked.") else: msg = ("Lock definition of guest '%s' failed with" " SMUT error: %s" % (userid, e.format_message())) LOG.error(msg) raise e def resize_memory(self, userid, memory): # Check defined storage in user entry. # Update STORAGE and RESERVED accordingly. size = int(zvmutils.convert_to_mb(memory)) (defined_mem, max_mem, reserved_mem, user_direct) = self._get_defined_memory(userid) # Check max memory is properly defined if max_mem == -1 or reserved_mem == -1: LOG.error("Memory resize for guest '%s' cann't be done." "Failed to get the defined/max/reserved memory size " "from user directory." % userid) raise exception.SDKConflictError(modID='guest', rs=19, userid=userid) action = 0 # Make sure requested size is less than the maximum memory size if size > max_mem: LOG.error("Memory resize for guest '%s' cann't be done. The " "requested memory size: '%im' exceeds the maximum " "size allowed: '%im'." % (userid, size, max_mem)) raise exception.SDKConflictError(modID='guest', rs=20, userid=userid, req=size, max=max_mem) # check if already satisfy request if defined_mem == size: LOG.info("The current defined memory size in user '%s' equals " "to requested size: %im, no action for memory resize " "needed." % (userid, size)) return (action, defined_mem, max_mem, user_direct) else: # set action to 1 to represent that revert need to be done when # live resize failed. action = 1 # get the new reserved memory size new_reserved = max_mem - size # prepare the new user entry content entry_str = "" for ent in user_direct: if ent == '': # Avoid adding an empty line in the entry file # otherwise Image_Replace_DM would return syntax error. continue new_ent = "" if ent.startswith("USER "): fields = ent.split(' ') for i in range(len(fields)): # update fields[3] to new defined size if i != 3: new_ent += (fields[i] + ' ') else: new_ent += (str(size) + 'M ') # remove the last space new_ent = new_ent.strip() elif ent.startswith("COMMAND DEF STOR RESERVED"): new_ent = ("COMMAND DEF STOR RESERVED %iM" % new_reserved) else: new_ent = ent # append this new entry entry_str += (new_ent + '\n') # Lock and replace user definition with the new_entry content try: self._lock_user_direct(userid) except exception.SDKSMUTRequestFailed as e: raise exception.SDKGuestOperationError(rs=9, userid=userid, err=e.format_message()) LOG.debug("User directory Locked successfully for guest '%s' " % userid) # Replace user directory try: self._replace_user_direct(userid, entry_str) except exception.SDKSMUTRequestFailed as e: raise exception.SDKGuestOperationError(rs=10, userid=userid, err=e.format_message()) # Finally return useful info return (action, defined_mem, max_mem, user_direct) def _revert_user_direct(self, userid, user_entry): # user_entry can be a list or a string try: self._lock_user_direct(userid) except exception.SDKSMUTRequestFailed: # print revert error and return msg = ("Failed to revert user direct of guest '%s'." % userid) LOG.error(msg) return LOG.debug("User directory Locked successfully for guest '%s'." % userid) # Replace user directory try: self._replace_user_direct(userid, user_entry) except exception.SDKSMUTRequestFailed: msg = ("Failed to revert user direct of guest '%s'." % userid) LOG.error(msg) return LOG.debug("User directory reverted successfully for guest '%s'." % userid) def _get_active_memory(self, userid): # Return an integer value representing the active memory size in mb output = self.execute_cmd(userid, "lsmem") # cmd output contains following line: # Total online memory : 8192 MB active_mem = 0 for e in output: if e.startswith("Total online memory : "): try: mem_info = e.split(' : ')[1].split(' ') # sample mem_info: [u'2048', u'MB'] active_mem = int(zvmutils.convert_to_mb(mem_info[0] + mem_info[1][0])) except (IndexError, ValueError, KeyError, TypeError): errmsg = ("Failed to get active storage size for guest: %s" % userid) LOG.error(errmsg) raise exception.SDKInternalError(msg=errmsg) break return active_mem def live_resize_memory(self, userid, memory): # Get active memory size and compare with requested size # If request size is smaller than the current size, then report # error and exit immediately. size = int(zvmutils.convert_to_mb(memory)) active_size = self._get_active_memory(userid) if active_size > size: LOG.error("Failed to live resize memory of guest: %(uid)s, " "current active memory size: %(cur)im is greater than " "the requested size: %(req)im." % {'uid': userid, 'cur': active_size, 'req': size}) raise exception.SDKConflictError(modID='guest', rs=18, userid=userid, active=active_size, req=size) # Static resize memory. (increase/decrease memory from user directory) (action, defined_mem, max_mem, user_direct) = self.resize_memory(userid, memory) # Compare active size and requested size, then update accordingly if active_size == size: # online memory already satisfied LOG.info("Current active memory size of guest: '%s' equals to the " "requested size: '%iM', no more actions needed for " "live resize." % (userid, size)) LOG.info("Live resize memory for guest: '%s' finished " "successfully." % userid) return else: # Do live resize. update memory size increase_size = size - active_size # Step1: Define new standby storage cmd_str = ("vmcp def storage standby %sM" % increase_size) try: self.execute_cmd(userid, cmd_str) except exception.SDKSMUTRequestFailed as e: # rollback and return msg = ("Define standby memory of guest: '%s' failed with " "error: %s." % (userid, e.format_message())) LOG.error(msg) # Start to do rollback if action == 1: LOG.debug("Start to revert user definition of guest '%s'." % userid) self._revert_user_direct(userid, user_direct) # Finally, raise the error and exit raise exception.SDKGuestOperationError(rs=11, userid=userid, err=e.format_message()) # Step 2: Online new memory cmd_str = ("chmem -e %sM" % increase_size) try: self.execute_cmd(userid, cmd_str) except exception.SDKSMUTRequestFailed as err1: # rollback and return msg1 = ("Online memory of guest: '%s' failed with " "error: %s." % (userid, err1.format_message())) LOG.error(msg1) # Start to do rollback LOG.info("Start to do revert.") LOG.debug("Reverting the standby memory.") try: self.execute_cmd(userid, "vmcp def storage standby 0M") except exception.SDKSMUTRequestFailed as err2: # print revert error info and continue msg2 = ("Revert standby memory of guest: '%s' failed with " "error: %s." % (userid, err2.format_message())) LOG.error(msg2) # Continue to do the user directory change. if action == 1: LOG.debug("Reverting the user directory change of guest " "'%s'." % userid) self._revert_user_direct(userid, user_direct) # Finally raise the exception raise exception.SDKGuestOperationError( rs=7, userid=userid, err=err1.format_message()) LOG.info("Live resize memory for guest: '%s' finished successfully." % userid) class FilesystemBackend(object): @classmethod def image_import(cls, image_name, url, target, **kwargs): """Import image from remote host to local image repository using scp. If remote_host not specified, it means the source file exist in local file system, just copy the image to image repository """ source = urlparse.urlparse(url).path if kwargs['remote_host']: if '@' in kwargs['remote_host']: source_path = ':'.join([kwargs['remote_host'], source]) command = ' '.join(['/usr/bin/scp', "-P", CONF.zvm.remotehost_sshd_port, "-o StrictHostKeyChecking=no", '-r ', source_path, target]) (rc, output) = zvmutils.execute(command) if rc: msg = ("Copying image file from remote filesystem failed" " with reason: %s" % output) LOG.error(msg) raise exception.SDKImageOperationError(rs=10, err=output) else: msg = ("The specified remote_host %s format invalid" % kwargs['remote_host']) LOG.error(msg) raise exception.SDKImageOperationError(rs=11, rh=kwargs['remote_host']) else: LOG.debug("Remote_host not specified, will copy from local") try: shutil.copyfile(source, target) except Exception as err: msg = ("Import image from local file system failed" " with reason %s" % six.text_type(err)) LOG.error(msg) raise exception.SDKImageOperationError(rs=12, err=six.text_type(err)) @classmethod def image_export(cls, source_path, dest_url, **kwargs): """Export the specific image to remote host or local file system """ dest_path = urlparse.urlparse(dest_url).path if kwargs['remote_host']: target_path = ':'.join([kwargs['remote_host'], dest_path]) command = ' '.join(['/usr/bin/scp', "-P", CONF.zvm.remotehost_sshd_port, "-o StrictHostKeyChecking=no", '-r ', source_path, target_path]) (rc, output) = zvmutils.execute(command) if rc: msg = ("Error happened when copying image file to remote " "host with reason: %s" % output) LOG.error(msg) raise exception.SDKImageOperationError(rs=21, msg=output) else: # Copy to local file system LOG.debug("Remote_host not specified, will copy to local server") try: shutil.copyfile(source_path, dest_path) except Exception as err: msg = ("Export image from %(src)s to local file system" " %(dest)s failed: %(err)s" % {'src': source_path, 'dest': dest_path, 'err': six.text_type(err)}) LOG.error(msg) raise exception.SDKImageOperationError(rs=22, err=six.text_type(err)) class HTTPBackend(object): @classmethod def image_import(cls, image_name, url, target, **kwargs): import_image = MultiThreadDownloader(image_name, url, target) import_image.run() class MultiThreadDownloader(threading.Thread): def __init__(self, image_name, url, target): super(MultiThreadDownloader, self).__init__() self.url = url # Set thread number self.threadnum = 8 r = requests.head(self.url) # Get the size of the download resource self.totalsize = int(r.headers['Content-Length']) self.target = target def handle_download_errors(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): try: return func(self, *args, **kwargs) except Exception as err: self.fd.close() msg = ("Download image from http server failed: %s" % six.text_type(err)) LOG.error(msg) raise exception.SDKImageOperationError(rs=9, err=six.text_type(err)) return wrapper def get_range(self): ranges = [] offset = int(self.totalsize / self.threadnum) for i in range(self.threadnum): if i == self.threadnum - 1: ranges.append((i * offset, '')) else: # Get the process range for each thread ranges.append((i * offset, (i + 1) * offset)) return ranges def download(self, start, end): headers = {'Range': 'Bytes=%s-%s' % (start, end), 'Accept-Encoding': '*'} # Get the data res = requests.get(self.url, headers=headers) # seek to the right position for writing data LOG.debug("Downloading file range %s:%s success" % (start, end)) with _LOCK: self.fd.seek(start) self.fd.write(res.content) @handle_download_errors def run(self): self.fd = open(self.target, 'w') thread_list = [] n = 0 for ran in self.get_range(): start, end = ran LOG.debug('thread %d start:%s,end:%s' % (n, start, end)) n += 1 # Open thread thread = threading.Thread(target=self.download, args=(start, end)) thread.start() thread_list.append(thread) for i in thread_list: i.join() LOG.info('Download %s success' % (self.name)) self.fd.close() zVMCloudConnector-1.4.1/zvmsdk/__init__.py0000775000175000017510000000000013371225174020143 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/api.py0000775000175000017510000020465313442676317017211 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr import six from zvmsdk import config from zvmsdk import constants from zvmsdk import exception from zvmsdk import hostops from zvmsdk import imageops from zvmsdk import log from zvmsdk import monitor from zvmsdk import networkops from zvmsdk import vmops from zvmsdk import smutclient from zvmsdk import volumeop from zvmsdk import database from zvmsdk import utils as zvmutils CONF = config.CONF LOG = log.LOG def check_guest_exist(check_index=0): """Check guest exist in database. :param check_index: The parameter index of userid(s), default as 0 """ def outer(f): @six.wraps(f) def inner(self, *args, **kw): userids = args[check_index] if isinstance(userids, list): # convert all userids to upper case userids = [uid.upper() for uid in userids] new_args = (args[:check_index] + (userids,) + args[check_index + 1:]) else: # convert the userid to upper case userids = userids.upper() new_args = (args[:check_index] + (userids,) + args[check_index + 1:]) userids = [userids] self._vmops.check_guests_exist_in_db(userids) return f(self, *new_args, **kw) return inner return outer class SDKAPI(object): """Compute action interfaces.""" def __init__(self, **kwargs): self._vmops = vmops.get_vmops() self._smutclient = smutclient.get_smutclient() self._hostops = hostops.get_hostops() self._networkops = networkops.get_networkops() self._imageops = imageops.get_imageops() self._monitor = monitor.get_monitor() self._volumeop = volumeop.get_volumeop() self._GuestDbOperator = database.GuestDbOperator() self._NetworkDbOperator = database.NetworkDbOperator() @check_guest_exist() def guest_start(self, userid): """Power on a virtual machine. :param str userid: the id of the virtual machine to be power on :returns: None """ action = "start guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_start(userid) @check_guest_exist() def guest_stop(self, userid, **kwargs): """Power off a virtual machine. :param str userid: the id of the virtual machine to be power off :param dict kwargs: - timeout=: Integer, time to wait for vm to be deactivate, the recommended value is 300 - poll_interval= Integer, how often to signal guest while waiting for it to be deactivate, the recommended value is 20 :returns: None """ action = "stop guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_stop(userid, **kwargs) @check_guest_exist() def guest_softstop(self, userid, **kwargs): """Issue a shutdown command to shutdown the OS in a virtual machine and then log the virtual machine off z/VM.. :param str userid: the id of the virtual machine to be power off :param dict kwargs: - timeout=: Integer, time to wait for vm to be deactivate, the recommended value is 300 - poll_interval= Integer, how often to signal guest while waiting for it to be deactivate, the recommended value is 20 :returns: None """ action = "soft stop guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_softstop(userid, **kwargs) @check_guest_exist() def guest_reboot(self, userid): """Reboot a virtual machine :param str userid: the id of the virtual machine to be reboot :returns: None """ action = "reboot guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_reboot(userid) @check_guest_exist() def guest_reset(self, userid): """reset a virtual machine :param str userid: the id of the virtual machine to be reset :returns: None """ action = "reset guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_reset(userid) @check_guest_exist() def guest_pause(self, userid): """Pause a virtual machine. :param str userid: the id of the virtual machine to be paused :returns: None """ action = "pause guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_pause(userid) @check_guest_exist() def guest_unpause(self, userid): """Unpause a virtual machine. :param str userid: the id of the virtual machine to be unpaused :returns: None """ action = "unpause guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_unpause(userid) @check_guest_exist() def guest_get_power_state(self, userid): """Returns power state.""" action = "get power state of guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.get_power_state(userid) @check_guest_exist() def guest_get_info(self, userid): """Get the status of a virtual machine. :param str userid: the id of the virtual machine :returns: Dictionary contains: power_state: (str) the running state, one of on | off max_mem_kb: (int) the maximum memory in KBytes allowed mem_kb: (int) the memory in KBytes used by the instance num_cpu: (int) the number of virtual CPUs for the instance cpu_time_us: (int) the CPU time used in microseconds """ action = "get info of guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.get_info(userid) def guest_list(self): """list names of all the VMs on this host. :returns: names of the vm on this host, in a list. """ action = "list guests on host" with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.guest_list() def host_get_info(self): """ Retrieve host information including host, memory, disk etc. :returns: Dictionary describing resources """ action = "get host information" with zvmutils.log_and_reraise_sdkbase_error(action): return self._hostops.get_info() def host_diskpool_get_info(self, disk_pool=None): """ Retrieve diskpool information. :param str disk_pool: the disk pool info. It use ':' to separate disk pool type and pool name, eg "ECKD:eckdpool" or "FBA:fbapool" :returns: Dictionary describing disk pool usage info """ # disk_pool must be assigned. disk_pool default to None because # it is more convenient for users to just type function name when # they want to get the disk pool info of CONF.zvm.disk_pool if disk_pool is None: disk_pool = CONF.zvm.disk_pool if ':' not in disk_pool: msg = ('Invalid input parameter disk_pool, expect ":" in' 'disk_pool, eg. ECKD:eckdpool') LOG.error(msg) raise exception.SDKInvalidInputFormat(msg) diskpool_type = disk_pool.split(':')[0].upper() diskpool_name = disk_pool.split(':')[1] if diskpool_type not in ('ECKD', 'FBA'): msg = ('Invalid disk pool type found in disk_pool, expect' 'disk_pool like ECKD:eckdpool or FBA:fbapool') LOG.error(msg) raise exception.SDKInvalidInputFormat(msg) action = "get information of disk pool: '%s'" % disk_pool with zvmutils.log_and_reraise_sdkbase_error(action): return self._hostops.diskpool_get_info(diskpool_name) def image_delete(self, image_name): """Delete image from image repository :param image_name: the name of the image to be deleted """ try: self._imageops.image_delete(image_name) except exception.SDKBaseException: LOG.error("Failed to delete image '%s'" % image_name) raise def image_get_root_disk_size(self, image_name): """Get the root disk size of the image :param image_name: the image name in image Repository :returns: the disk size in units CYL or BLK """ try: return self._imageops.image_get_root_disk_size(image_name) except exception.SDKBaseException: LOG.error("Failed to get root disk size units of image '%s'" % image_name) raise def image_import(self, image_name, url, image_meta, remote_host=None): """Import image to zvmsdk image repository :param image_name: image name that can be uniquely identify an image :param str url: image url to specify the location of image such as http://netloc/path/to/file.tar.gz.0 https://netloc/path/to/file.tar.gz.0 file:///path/to/file.tar.gz.0 :param dict image_meta: a dictionary to describe the image info, such as md5sum, os_version. For example: {'os_version': 'rhel6.2', 'md5sum': ' 46f199c336eab1e35a72fa6b5f6f11f5'} :param string remote_host: if the image url schema is file, the remote_host is used to indicate where the image comes from, the format is username@IP eg. nova@192.168.99.1, the default value is None, it indicate the image is from a local file system. If the image url schema is http/https, this value will be useless """ try: self._imageops.image_import(image_name, url, image_meta, remote_host=remote_host) except exception.SDKBaseException: LOG.error("Failed to import image '%s'" % image_name) raise def image_query(self, imagename=None): """Get the list of image info in image repository :param imagename: Used to retrieve the specified image info, if not specified, all images info will be returned :returns: A list that contains the specified or all images info """ try: return self._imageops.image_query(imagename) except exception.SDKBaseException: LOG.error("Failed to query image") raise def image_export(self, image_name, dest_url, remote_host=None): """Export the image to the specified location :param image_name: image name that can be uniquely identify an image :param dest_url: the location of exported image, eg. file:///opt/images/export.img, now only support export to remote server or local server's file system :param remote_host: the server that the image will be export to, if remote_host is None, the image will be stored in the dest_path in local server, the format is username@IP eg. nova@9.x.x.x :returns a dictionary that contains the exported image info { 'image_name': the image_name that exported 'image_path': the image_path after exported 'os_version': the os version of the exported image 'md5sum': the md5sum of the original image } """ try: return self._imageops.image_export(image_name, dest_url, remote_host) except exception.SDKBaseException: LOG.error("Failed to export image '%s'" % image_name) raise @check_guest_exist() def guest_deploy(self, userid, image_name, transportfiles=None, remotehost=None, vdev=None, hostname=None): """ Deploy the image to vm. :param userid: (str) the user id of the vm :param image_name: (str) the name of image that used to deploy the vm :param transportfiles: (str) the files that used to customize the vm :param remotehost: the server where the transportfiles located, the format is username@IP, eg nova@192.168.99.1 :param vdev: (str) the device that image will be deploy to :param hostname: (str) the hostname of the vm. This parameter will be ignored if transportfiles present. """ action = ("deploy image '%(img)s' to guest '%(vm)s'" % {'img': image_name, 'vm': userid}) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_deploy(userid, image_name, transportfiles, remotehost, vdev, hostname) @check_guest_exist() def guest_capture(self, userid, image_name, capture_type='rootonly', compress_level=6): """ Capture the guest to generate a image :param userid: (str) the user id of the vm :param image_name: (str) the unique image name after capture :param capture_type: (str) the type of capture, the value can be: rootonly: indicate just root device will be captured alldisks: indicate all the devices of the userid will be captured :param compress_level: the compression level of the image, default is 6 """ action = ("capture guest '%(vm)s' to generate image '%(img)s'" % {'vm': userid, 'img': image_name}) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_capture(userid, image_name, capture_type=capture_type, compress_level=compress_level) @check_guest_exist() def guest_create_nic(self, userid, vdev=None, nic_id=None, mac_addr=None, active=False): """ Create the nic for the vm, add NICDEF record into the user direct. :param str userid: the user id of the vm :param str vdev: nic device number, 1- to 4- hexadecimal digits :param str nic_id: nic identifier :param str mac_addr: mac address, it is only be used when changing the guest's user direct. Format should be xx:xx:xx:xx:xx:xx, and x is a hexadecimal digit :param bool active: whether add a nic on active guest system :returns: nic device number, 1- to 4- hexadecimal digits :rtype: str """ if mac_addr is not None: if not zvmutils.valid_mac_addr(mac_addr): raise exception.SDKInvalidInputFormat( msg=("Invalid mac address, format should be " "xx:xx:xx:xx:xx:xx, and x is a hexadecimal digit")) return self._networkops.create_nic(userid, vdev=vdev, nic_id=nic_id, mac_addr=mac_addr, active=active) @check_guest_exist() def guest_delete_nic(self, userid, vdev, active=False): """ delete the nic for the vm :param str userid: the user id of the vm :param str vdev: nic device number, 1- to 4- hexadecimal digits :param bool active: whether delete a nic on active guest system """ self._networkops.delete_nic(userid, vdev, active=active) @check_guest_exist() def guest_get_definition_info(self, userid, **kwargs): """Get definition info for the specified guest vm, also could be used to check specific info. :param str userid: the user id of the guest vm :param dict kwargs: Dictionary used to check specific info in user direct. Valid keywords for kwargs: nic_coupled=, where is the virtual device number of the nic to be checked the couple status. :returns: Dictionary describing user direct and check info result :rtype: dict """ action = "get the definition info of guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.get_definition_info(userid, **kwargs) def guest_register(self, userid, meta, net_set): """DB operation for migrate vm from another z/VM host in same SSI :param userid: (str) the userid of the vm to be relocated or tested :param meta: (str) the metadata of the vm to be relocated or tested :param net_set: (str) the net_set of the vm, default is 1. """ userid = userid.upper() if not zvmutils.check_userid_exist(userid): LOG.error("User directory '%s' does not exist." % userid) raise exception.SDKObjectNotExistError( obj_desc=("Guest '%s'" % userid), modID='guest') else: action = "list all guests in database which has been migrated." with zvmutils.log_and_reraise_sdkbase_error(action): guests = self._GuestDbOperator.get_migrated_guest_list() if userid in str(guests): """change comments for vm""" comments = self._GuestDbOperator.get_comments_by_userid( userid) comments['migrated'] = 0 action = "update guest '%s' in database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.update_guest_by_userid(userid, comments=comments) else: """add one record for new vm""" action = "add guest '%s' to database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.add_guest_migrated(userid, meta, net_set) action = "add switches of guest '%s' to database" % userid info = self._vmops.get_definition_info(userid) user_direct = info['user_direct'] for nic_info in user_direct: if nic_info.startswith('NICDEF'): nic_list = nic_info.split() interface = nic_list[1] switch = nic_list[6] with zvmutils.log_and_reraise_sdkbase_error(action): self._NetworkDbOperator.switch_add_record_migrated( userid, interface, switch) LOG.info("Guest %s registered." % userid) @check_guest_exist() def guest_live_migrate(self, userid, dest_zcc_userid, destination, parms, lgr_action): """Move an eligible, running z/VM(R) virtual machine transparently from one z/VM system to another within an SSI cluster. :param userid: (str) the userid of the vm to be relocated or tested :param dest_zcc_userid: (str) the userid of zcc on destination :param destination: (str) the system ID of the z/VM system to which the specified vm will be relocated or tested. :param parms: (dict) a dictionary of options for relocation. It has one dictionary that contains some of the below keys: {'maxtotal': i, 'maxquiesce': i, 'immediate': str} In which, 'maxtotal':indicates the maximum total time (in seconds) that the command issuer is willing to wait for the entire relocation to complete or -1 to indicate there is no limit for time. 'maxquiesce':indicates the maximum quiesce time for this relocation. This is the amount of time (in seconds) a virtual machine may be stopped during a relocation attempt or -1 to indicate there is no limit for time. 'immediate':If present, immediate=YES is set, which causes the VMRELOCATE command to do one early pass through virtual machine storage and then go directly to the quiesce stage. :param lgr_action: (str) indicates the action is move or test for vm. """ if lgr_action.lower() == 'move': if dest_zcc_userid == '': errmsg = ("'dest_zcc_userid' is required if the value of " "'lgr_action' equals 'move'.") LOG.error(errmsg) raise exception.SDKMissingRequiredInput(msg=errmsg) # Add authorization for new zcc. cmd = ('echo -n %s > /etc/iucv_authorized_userid\n' % dest_zcc_userid) rc = self._smutclient.execute_cmd(userid, cmd) if rc != 0: err_msg = ("Add authorization for new zcc failed") LOG.error(err_msg) # Live_migrate the guest operation = "Move guest '%s' to SSI '%s'" % (userid, destination) with zvmutils.log_and_reraise_sdkbase_error(operation): self._vmops.live_migrate_vm(userid, destination, parms, lgr_action) comments = self._GuestDbOperator.get_comments_by_userid(userid) comments['migrated'] = 1 action = "update guest '%s' in database" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.update_guest_by_userid(userid, comments=comments) if lgr_action.lower() == 'test': operation = "Test move guest '%s' to SSI '%s'" % (userid, destination) with zvmutils.log_and_reraise_sdkbase_error(operation): self._vmops.live_migrate_vm(userid, destination, parms, lgr_action) def guest_create(self, userid, vcpus, memory, disk_list=None, user_profile=CONF.zvm.user_profile, max_cpu=CONF.zvm.user_default_max_cpu, max_mem=CONF.zvm.user_default_max_memory): """create a vm in z/VM :param userid: (str) the userid of the vm to be created :param vcpus: (int) amount of vcpus :param memory: (int) size of memory in MB :param disk_list: (dict) a list of disks info for the guest. It has one dictionary that contain some of the below keys for each disk, the root disk should be the first element in the list, the format is: {'size': str, 'format': str, 'is_boot_disk': bool, 'disk_pool': str} In which, 'size': case insensitive, the unit can be in Megabytes (M), Gigabytes (G), or number of cylinders/blocks, eg 512M, 1g or just 2000. 'format': can be ext2, ext3, ext4, xfs. 'is_boot_disk': For root disk, this key must be set to indicate the image that will be deployed on this disk. 'disk_pool': optional, if not specified, the disk will be created by using the value from configure file,the format is ECKD:eckdpoolname or FBA:fbapoolname. For example: [{'size': '1g', 'is_boot_disk': True, 'disk_pool': 'ECKD:eckdpool1'}, {'size': '200000', 'disk_pool': 'FBA:fbapool1', 'format': 'ext3'}] In this case it will create one disk 0100(in case the vdev for root disk is 0100) with size 1g from ECKD disk pool eckdpool1 for guest , then set IPL 0100 in guest's user directory, and it will create 0101 with 200000 blocks from FBA disk pool fbapool1, and formated with ext3. :param user_profile: (str) the profile for the guest :param max_cpu: (int) the maximum number of virtual cpu this user can define. The value should be a decimal value between 1 and 64. :param max_mem: (str) the maximum size of memory the user can define. The value should be specified by 1-4 bits of number suffixed by either M (Megabytes) or G (Gigabytes). And the number should be an integer. """ userid = userid.upper() if disk_list: for disk in disk_list: if not isinstance(disk, dict): errmsg = ('Invalid "disk_list" input, it should be a ' 'dictionary. Details could be found in doc.') LOG.error(errmsg) raise exception.SDKInvalidInputFormat(msg=errmsg) # 'size' is required for each disk if 'size' not in disk.keys(): errmsg = ('Invalid "disk_list" input, "size" is required ' 'for each disk.') LOG.error(errmsg) raise exception.SDKInvalidInputFormat(msg=errmsg) # 'disk_pool' format check disk_pool = disk.get('disk_pool') or CONF.zvm.disk_pool if ':' not in disk_pool or (disk_pool.split(':')[0].upper() not in ['ECKD', 'FBA']): errmsg = ("Invalid disk_pool input, it should be in format" " ECKD:eckdpoolname or FBA:fbapoolname") LOG.error(errmsg) raise exception.SDKInvalidInputFormat(msg=errmsg) # 'format' value check if ('format' in disk.keys()) and (disk['format'].lower() not in ('ext2', 'ext3', 'ext4', 'xfs')): errmsg = ("Invalid disk_pool input, supported 'format' " "includes 'ext2', 'ext3', 'ext4', 'xfs'") LOG.error(errmsg) raise exception.SDKInvalidInputFormat(msg=errmsg) action = "create guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.create_vm(userid, vcpus, memory, disk_list, user_profile, max_cpu, max_mem) @check_guest_exist() def guest_live_resize_cpus(self, userid, cpu_cnt): """Live resize virtual cpus of guests. :param userid: (str) the userid of the guest to be live resized :param cpu_cnt: (int) The number of virtual cpus that the guest should have in active state after live resize. The value should be an integer between 1 and 64. """ action = "live resize guest '%s' to have '%i' virtual cpus" % (userid, cpu_cnt) LOG.info("Begin to %s" % action) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.live_resize_cpus(userid, cpu_cnt) LOG.info("%s successfully." % action) @check_guest_exist() def guest_resize_cpus(self, userid, cpu_cnt): """Resize virtual cpus of guests. :param userid: (str) the userid of the guest to be resized :param cpu_cnt: (int) The number of virtual cpus that the guest should have defined in user directory after resize. The value should be an integer between 1 and 64. """ action = "resize guest '%s' to have '%i' virtual cpus" % (userid, cpu_cnt) LOG.info("Begin to %s" % action) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.resize_cpus(userid, cpu_cnt) LOG.info("%s successfully." % action) @check_guest_exist() def guest_live_resize_mem(self, userid, size): """Live resize memory of guests. :param userid: (str) the userid of the guest to be live resized :param size: (str) The memory size that the guest should have in available status after live resize. The value should be specified by 1-4 bits of number suffixed by either M (Megabytes) or G (Gigabytes). And the number should be an integer. """ action = "live resize guest '%s' to have '%s' memory" % (userid, size) LOG.info("Begin to %s" % action) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.live_resize_memory(userid, size) LOG.info("%s successfully." % action) @check_guest_exist() def guest_resize_mem(self, userid, size): """Resize memory of guests. :param userid: (str) the userid of the guest to be resized :param size: (str) The memory size that the guest should have defined in user directory after resize. The value should be specified by 1-4 bits of number suffixed by either M (Megabytes) or G (Gigabytes). And the number should be an integer. """ action = "resize guest '%s' to have '%s' memory" % (userid, size) LOG.info("Begin to %s" % action) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.resize_memory(userid, size) LOG.info("%s successfully." % action) @check_guest_exist() def guest_create_disks(self, userid, disk_list): """Add disks to an existing guest vm. :param userid: (str) the userid of the vm to be created :param disk_list: (list) a list of disks info for the guest. It has one dictionary that contain some of the below keys for each disk, the root disk should be the first element in the list, the format is: {'size': str, 'format': str, 'is_boot_disk': bool, 'disk_pool': str} In which, 'size': case insensitive, the unit can be in Megabytes (M), Gigabytes (G), or number of cylinders/blocks, eg 512M, 1g or just 2000. 'format': optional, can be ext2, ext3, ext4, xfs, if not specified, the disk will not be formatted. 'is_boot_disk': For root disk, this key must be set to indicate the image that will be deployed on this disk. 'disk_pool': optional, if not specified, the disk will be created by using the value from configure file,the format is ECKD:eckdpoolname or FBA:fbapoolname. For example: [{'size': '1g', 'is_boot_disk': True, 'disk_pool': 'ECKD:eckdpool1'}, {'size': '200000', 'disk_pool': 'FBA:fbapool1', 'format': 'ext3'}] In this case it will create one disk 0100(in case the vdev for root disk is 0100) with size 1g from ECKD disk pool eckdpool1 for guest , then set IPL 0100 in guest's user directory, and it will create 0101 with 200000 blocks from FBA disk pool fbapool1, and formated with ext3. """ if disk_list == [] or disk_list is None: # nothing to do LOG.debug("No disk specified when calling guest_create_disks, " "nothing happened") return action = "create disks '%s' for guest '%s'" % (str(disk_list), userid) with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.create_disks(userid, disk_list) @check_guest_exist() def guest_delete_disks(self, userid, disk_vdev_list): """Delete disks from an existing guest vm. :param userid: (str) the userid of the vm to be deleted :param disk_vdev_list: (list) the vdev list of disks to be deleted, for example: ['0101', '0102'] """ action = "delete disks '%s' from guest '%s'" % (str(disk_vdev_list), userid) with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.delete_disks(userid, disk_vdev_list) @check_guest_exist() def guest_nic_couple_to_vswitch(self, userid, nic_vdev, vswitch_name, active=False): """ Couple nic device to specified vswitch. :param str userid: the user's name who owns the nic :param str nic_vdev: nic device number, 1- to 4- hexadecimal digits :param str vswitch_name: the name of the vswitch :param bool active: whether make the change on active guest system """ self._networkops.couple_nic_to_vswitch(userid, nic_vdev, vswitch_name, active=active) @check_guest_exist() def guest_nic_uncouple_from_vswitch(self, userid, nic_vdev, active=False): """ Disonnect nic device with network. :param str userid: the user's name who owns the nic :param str nic_vdev: nic device number, 1- to 4- hexadecimal digits :param bool active: whether make the change on active guest system """ self._networkops.uncouple_nic_from_vswitch(userid, nic_vdev, active=active) def vswitch_get_list(self): """ Get the vswitch list. :returns: vswitch name list :rtype: list """ return self._networkops.get_vswitch_list() def vswitch_create(self, name, rdev=None, controller='*', connection='CONNECT', network_type='ETHERNET', router="NONROUTER", vid='UNAWARE', port_type='ACCESS', gvrp='GVRP', queue_mem=8, native_vid=1, persist=True): """ Create vswitch. :param str name: the vswitch name :param str rdev: the real device number, a maximum of three devices, all 1-4 characters in length, delimited by blanks. 'NONE' may also be specified :param str controller: the vswitch's controller, it could be the userid controlling the real device, or '*' to specifies that any available controller may be used :param str connection: - CONnect: Activate the real device connection. - DISCONnect: Do not activate the real device connection. - NOUPLINK: The vswitch will never have connectivity through the UPLINK port :param str network_type: Specifies the transport mechanism to be used for the vswitch, as follows: IP, ETHERNET :param str router: - NONrouter: The OSA-Express device identified in real_device_address= will not act as a router to the vswitch - PRIrouter: The OSA-Express device identified in real_device_address= will act as a primary router to the vswitch - Note: If the network_type is ETHERNET, this value must be unspecified, otherwise, if this value is unspecified, default is NONROUTER :param str/int vid: the VLAN ID. This can be any of the following values: UNAWARE, AWARE or 1-4094 :param str port_type: - ACCESS: The default porttype attribute for guests authorized for the virtual switch. The guest is unaware of VLAN IDs and sends and receives only untagged traffic - TRUNK: The default porttype attribute for guests authorized for the virtual switch. The guest is VLAN aware and sends and receives tagged traffic for those VLANs to which the guest is authorized. If the guest is also authorized to the natvid, untagged traffic sent or received by the guest is associated with the native VLAN ID (natvid) of the virtual switch. :param str gvrp: - GVRP: Indicates that the VLAN IDs in use on the virtual switch should be registered with GVRP-aware switches on the LAN. This provides dynamic VLAN registration and VLAN registration removal for networking switches. This eliminates the need to manually configure the individual port VLAN assignments. - NOGVRP: Do not register VLAN IDs with GVRP-aware switches on the LAN. When NOGVRP is specified VLAN port assignments must be configured manually :param int queue_mem: A number between 1 and 8, specifying the QDIO buffer size in megabytes. :param int native_vid: the native vlan id, 1-4094 or None :param bool persist: whether create the vswitch in the permanent configuration for the system """ if ((queue_mem < 1) or (queue_mem > 8)): errmsg = ('API vswitch_create: Invalid "queue_mem" input, ' 'it should be 1-8') raise exception.SDKInvalidInputFormat(msg=errmsg) if isinstance(vid, int) or vid.upper() != 'UNAWARE': if ((native_vid is not None) and ((native_vid < 1) or (native_vid > 4094))): errmsg = ('API vswitch_create: Invalid "native_vid" input, ' 'it should be 1-4094 or None') raise exception.SDKInvalidInputFormat(msg=errmsg) if network_type.upper() == 'ETHERNET': router = None self._networkops.add_vswitch(name, rdev=rdev, controller=controller, connection=connection, network_type=network_type, router=router, vid=vid, port_type=port_type, gvrp=gvrp, queue_mem=queue_mem, native_vid=native_vid, persist=persist) @check_guest_exist() def guest_get_console_output(self, userid): """Get the console output of the guest virtual machine. :param str userid: the user id of the vm :returns: console log string :rtype: str """ action = "get the console output of guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): output = self._vmops.get_console_output(userid) return output def guest_delete(self, userid): """Delete guest. :param userid: the user id of the vm """ # check guest exist in database or not userid = userid.upper() if not self._vmops.check_guests_exist_in_db(userid, raise_exc=False): if zvmutils.check_userid_exist(userid): LOG.error("Guest '%s' does not exist in guests database" % userid) raise exception.SDKObjectNotExistError( obj_desc=("Guest '%s'" % userid), modID='guest') else: LOG.debug("The guest %s does not exist." % userid) return action = "delete guest '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): return self._vmops.delete_vm(userid) @check_guest_exist() def guest_inspect_stats(self, userid_list): """Get the statistics including cpu and mem of the guests :param userid_list: a single userid string or a list of guest userids :returns: dictionary describing the cpu statistics of the vm in the form {'UID1': { 'guest_cpus': xx, 'used_cpu_time_us': xx, 'elapsed_cpu_time_us': xx, 'min_cpu_count': xx, 'max_cpu_limit': xx, 'samples_cpu_in_use': xx, 'samples_cpu_delay': xx, 'used_mem_kb': xx, 'max_mem_kb': xx, 'min_mem_kb': xx, 'shared_mem_kb': xx }, 'UID2': { 'guest_cpus': xx, 'used_cpu_time_us': xx, 'elapsed_cpu_time_us': xx, 'min_cpu_count': xx, 'max_cpu_limit': xx, 'samples_cpu_in_use': xx, 'samples_cpu_delay': xx, 'used_mem_kb': xx, 'max_mem_kb': xx, 'min_mem_kb': xx, 'shared_mem_kb': xx } } for the guests that are shutdown or not exist, no data returned in the dictionary """ if not isinstance(userid_list, list): userid_list = [userid_list] action = "get the statistics of guest '%s'" % str(userid_list) with zvmutils.log_and_reraise_sdkbase_error(action): return self._monitor.inspect_stats(userid_list) @check_guest_exist() def guest_inspect_vnics(self, userid_list): """Get the vnics statistics of the guest virtual machines :param userid_list: a single userid string or a list of guest userids :returns: dictionary describing the vnics statistics of the vm in the form {'UID1': [{ 'vswitch_name': xx, 'nic_vdev': xx, 'nic_fr_rx': xx, 'nic_fr_tx': xx, 'nic_fr_rx_dsc': xx, 'nic_fr_tx_dsc': xx, 'nic_fr_rx_err': xx, 'nic_fr_tx_err': xx, 'nic_rx': xx, 'nic_tx': xx }, ], 'UID2': [{ 'vswitch_name': xx, 'nic_vdev': xx, 'nic_fr_rx': xx, 'nic_fr_tx': xx, 'nic_fr_rx_dsc': xx, 'nic_fr_tx_dsc': xx, 'nic_fr_rx_err': xx, 'nic_fr_tx_err': xx, 'nic_rx': xx, 'nic_tx': xx }, ] } for the guests that are shutdown or not exist, no data returned in the dictionary """ if not isinstance(userid_list, list): userid_list = [userid_list] action = "get the vnics statistics of guest '%s'" % str(userid_list) with zvmutils.log_and_reraise_sdkbase_error(action): return self._monitor.inspect_vnics(userid_list) @check_guest_exist(check_index=1) def vswitch_grant_user(self, vswitch_name, userid): """Set vswitch to grant user :param str vswitch_name: the name of the vswitch :param str userid: the user id of the vm """ self._networkops.grant_user_to_vswitch(vswitch_name, userid) @check_guest_exist(check_index=1) def vswitch_revoke_user(self, vswitch_name, userid): """Revoke user for vswitch :param str vswitch_name: the name of the vswitch :param str userid: the user id of the vm """ self._networkops.revoke_user_from_vswitch(vswitch_name, userid) @check_guest_exist(check_index=1) def vswitch_set_vlan_id_for_user(self, vswitch_name, userid, vlan_id): """Set vlan id for user when connecting to the vswitch :param str vswitch_name: the name of the vswitch :param str userid: the user id of the vm :param int vlan_id: the VLAN id """ self._networkops.set_vswitch_port_vlan_id(vswitch_name, userid, vlan_id) @check_guest_exist() def guest_config_minidisks(self, userid, disk_info): """Punch the script that used to process additional disks to vm :param str userid: the user id of the vm :param disk_info: a list contains disks info for the guest. It contains dictionaries that describes disk info for each disk. Each dictionary has 3 keys, format is required, vdev and mntdir are optional. For example, if vdev is not specified, it will start from the next vdev of CONF.zvm.user_root_vdev, eg. if CONF.zvm.user_root_vdev is 0100, zvmsdk will use 0101 as the vdev for first additional disk in disk_info, and if mntdir is not specified, zvmsdk will use /mnt/ephemeral0 as the mount point of first additional disk Here are some examples: [{'vdev': '0101', 'format': 'ext3', 'mntdir': '/mnt/ephemeral0'}] In this case, the zvmsdk will treat 0101 as additional disk's vdev, and it's formatted with ext3, and will be mounted to /mnt/ephemeral0 [{'format': 'ext3'}, {'format': 'ext4'}] In this case, if CONF.zvm.user_root_vdev is 0100, zvmsdk will configure the first additional disk as 0101, mount it to /mnt/ephemeral0 with ext3, and configure the second additional disk 0102, mount it to /mnt/ephemeral1 with ext4. """ action = "config disks for userid '%s'" % userid with zvmutils.log_and_reraise_sdkbase_error(action): self._vmops.guest_config_minidisks(userid, disk_info) def vswitch_set(self, vswitch_name, **kwargs): """Change the configuration of an existing virtual switch :param str vswitch_name: the name of the virtual switch :param dict kwargs: - grant_userid=: A userid to be added to the access list - user_vlan_id=: user VLAN ID. Support following ways: 1. As single values between 1 and 4094. A maximum of four values may be specified, separated by blanks. Example: 1010 2020 3030 4040 2. As a range of two numbers, separated by a dash (-). A maximum of two ranges may be specified. Example: 10-12 20-22 - revoke_userid=: A userid to be removed from the access list - real_device_address=: The real device address or the real device address and OSA Express port number of a QDIO OSA Express device to be used to create the switch to the virtual adapter. If using a real device and an OSA Express port number, specify the real device number followed by a period(.), the letter 'P' (or 'p'), followed by the port number as a hexadecimal number. A maximum of three device addresses, all 1-7 characters in length, may be specified, delimited by blanks. 'None' may also be specified - port_name=: The name used to identify the OSA Expanded adapter. A maximum of three port names, all 1-8 characters in length, may be specified, delimited by blanks. - controller_name=: One of the following: 1. The userid controlling the real device. A maximum of eight userids, all 1-8 characters in length, may be specified, delimited by blanks. 2. '*': Specifies that any available controller may be used - connection_value=: One of the following values: CONnect: Activate the real device connection. DISCONnect: Do not activate the real device connection. - queue_memory_limit=: A number between 1 and 8 specifying the QDIO buffer size in megabytes. - routing_value=: Specifies whether the OSA-Express QDIO device will act as a router to the virtual switch, as follows: NONrouter: The OSA-Express device identified in real_device_address= will not act as a router to the vswitch PRIrouter: The OSA-Express device identified in real_device_address= will act as a primary router to the vswitch - port_type=: Specifies the port type, ACCESS or TRUNK - persist=: one of the following values: NO: The vswitch is updated on the active system, but is not updated in the permanent configuration for the system. YES: The vswitch is updated on the active system and also in the permanent configuration for the system. If not specified, the default is NO. - gvrp_value=: GVRP or NOGVRP - mac_id=: A unique identifier (up to six hexadecimal digits) used as part of the vswitch MAC address - uplink=: One of the following: NO: The port being enabled is not the vswitch's UPLINK port. YES: The port being enabled is the vswitch's UPLINK port. - nic_userid=: One of the following: 1. The userid of the port to/from which the UPLINK port will be connected or disconnected. If a userid is specified, then nic_vdev= must also be specified 2. '*': Disconnect the currently connected guest port to/from the special virtual switch UPLINK port. (This is equivalent to specifying NIC NONE on CP SET VSWITCH). - nic_vdev=: The virtual device to/from which the the UPLINK port will be connected/disconnected. If this value is specified, nic_userid= must also be specified, with a userid. - lacp=: One of the following values: ACTIVE: Indicates that the virtual switch will initiate negotiations with the physical switch via the link aggregation control protocol (LACP) and will respond to LACP packets sent by the physical switch. INACTIVE: Indicates that aggregation is to be performed, but without LACP. - Interval=: The interval to be used by the control program (CP) when doing load balancing of conversations across multiple links in the group. This can be any of the following values: 1 - 9990: Indicates the number of seconds between load balancing operations across the link aggregation group. OFF: Indicates that no load balancing is done. - group_rdev=: The real device address or the real device address and OSA Express port number of a QDIO OSA Express devcie to be affected within the link aggregation group associated with this vswitch. If using a real device and an OSA Express port number, specify the real device number followed by a period (.), the letter 'P' (or 'p'), followed by the port number as a hexadecimal number. A maximum of eight device addresses all 1-7 characters in length, may be specified, delimited by blanks. Note: If a real device address is specified, this device will be added to the link aggregation group associated with this vswitch. (The link aggregation group will be created if it does not already exist.) - iptimeout=: A number between 1 and 240 specifying the length of time in minutes that a remote IP address table entry remains in the IP address table for the virtual switch. - port_isolation=: ON or OFF - promiscuous=: One of the following: NO: The userid or port on the grant is not authorized to use the vswitch in promiscuous mode YES: The userid or port on the grant is authorized to use the vswitch in promiscuous mode. - MAC_protect=: ON, OFF or UNSPECified - VLAN_counters=: ON or OFF """ for k in kwargs.keys(): if k not in constants.SET_VSWITCH_KEYWORDS: errmsg = ('API vswitch_set: Invalid keyword %s' % k) raise exception.SDKInvalidInputFormat(msg=errmsg) self._networkops.set_vswitch(vswitch_name, **kwargs) def vswitch_delete(self, vswitch_name, persist=True): """ Delete vswitch. :param str name: the vswitch name :param bool persist: whether delete the vswitch from the permanent configuration for the system """ self._networkops.delete_vswitch(vswitch_name, persist) def get_volume_connector(self, userid): """Get connector information of the guest for attaching to volumes. This API is for Openstack Cinder driver only now. Connector information is a dictionary representing the ip of the machine that will be making the connection, the name of the iscsi initiator and the hostname of the machine as follows:: { 'zvm_fcp': fcp 'wwpns': [wwpn] 'host': host } This information will be used by IBM storwize FC driver in Cinder. :param str userid: the user id of the guest """ return self._volumeop.get_volume_connector(userid) def volume_attach(self, connection_info): """ Attach a volume to a guest. It's prerequisite to active multipath feature on the guest before utilizing persistent volumes. :param dict connection_info: - alias: of type string. A constant valid alias of the volume after it being attached onto the guest, i.e. '/dev/vda'. Because the system generating device name could change after each rebooting, it's necessary to have a constant name to represent the volume in its life time. - protocol: of type string. The protocol by which the volume is connected to the guest. The only one supported now is 'fc' which implies FibreChannel. - fcps: of type list. The address of the FCP devices used by the guest to connect to the volume. They should belong to different channel path IDs in order to work properly. - wwpns: of type list. The WWPN values through which the volume can be accessed, excluding prefixing '0x'. - dedicate: of type list. The address of the FCP devices which will be dedicated to the guest before accessing the volume. They should belong to different channel path IDs in order to work properly. """ self._volumeop.attach_volume_to_instance(connection_info) def volume_detach(self, connection_info): """ Detach a volume from a guest. It's prerequisite to active multipath feature on the guest before utilizing persistent volumes. :param dict connection_info: A dict comprised of a list of information used to establish host-volume connection, including: - alias: of type string. A constant valid alias of the volume after it being attached onto the guest, i.e. '/dev/vda'. Because the system generating device name could change after each rebooting, it's necessary to have a constant name to represent the volume in its life time. - protocol: of type string. The protocol by which the volume is connected to the guest. The only one supported now is 'fc' which implies FibreChannel. - fcps: of type list. The address of the FCP devices used by the guest to connect to the volume. - wwpns: of type list. The WWPN values through which the volume can be accessed, excluding prefixing '0x'. - dedicate: of type list. The address of the FCP devices which will be undedicated from the guest after removing the volume. """ self._volumeop.detach_volume_from_instance(connection_info) @check_guest_exist() def guest_create_network_interface(self, userid, os_version, guest_networks, active=False): """ Create network interface(s) for the guest inux system. It will create the nic for the guest, add NICDEF record into the user direct. It will also construct network interface configuration files and punch the files to the guest. These files will take effect when initializing and configure guest. :param str userid: the user id of the guest :param str os_version: operating system version of the guest :param list guest_networks: a list of network info for the guest. It has one dictionary that contain some of the below keys for each network, the format is: {'ip_addr': (str) IP address or None, 'dns_addr': (list) dns addresses or None, 'gateway_addr': (str) gateway address or None, 'cidr': (str) cidr format, 'nic_vdev': (str)nic VDEV, 1- to 4- hexadecimal digits or None, 'nic_id': (str) nic identifier or None, 'mac_addr': (str) mac address or None, it is only be used when changing the guest's user direct. Format should be xx:xx:xx:xx:xx:xx, and x is a hexadecimal digit 'osa_device': (str) OSA address or None} Example for guest_networks: [{'ip_addr': '192.168.95.10', 'dns_addr': ['9.0.2.1', '9.0.3.1'], 'gateway_addr': '192.168.95.1', 'cidr': "192.168.95.0/24", 'nic_vdev': '1000', 'mac_addr': '02:00:00:12:34:56'}, {'ip_addr': '192.168.96.10', 'dns_addr': ['9.0.2.1', '9.0.3.1'], 'gateway_addr': '192.168.96.1', 'cidr': "192.168.96.0/24", 'nic_vdev': '1003}] :param bool active: whether add a nic on active guest system :returns: guest_networks list, including nic_vdev for each network :rtype: list """ if len(guest_networks) == 0: errmsg = ("API guest_create_network_interface: " "Network information is required but not provided") raise exception.SDKInvalidInputFormat(msg=errmsg) for network in guest_networks: vdev = nic_id = mac_addr = ip_addr = OSA = None if 'nic_vdev' in network.keys(): vdev = network['nic_vdev'] if 'osa_device' in network.keys(): OSA = network['osa_device'] if 'nic_id' in network.keys(): nic_id = network['nic_id'] if (('mac_addr' in network.keys()) and (network['mac_addr'] is not None)): mac_addr = network['mac_addr'] if not zvmutils.valid_mac_addr(mac_addr): errmsg = ("API guest_create_network_interface: " "Invalid mac address, format should be " "xx:xx:xx:xx:xx:xx, and x is a hexadecimal " "digit") raise exception.SDKInvalidInputFormat(msg=errmsg) if (('ip_addr' in network.keys()) and (network['ip_addr'] is not None)): ip_addr = network['ip_addr'] if not netaddr.valid_ipv4(ip_addr): errmsg = ("API guest_create_network_interface: " "Invalid management IP address, it should be " "the value between 0.0.0.0 and 255.255.255.255") raise exception.SDKInvalidInputFormat(msg=errmsg) if (('dns_addr' in network.keys()) and (network['dns_addr'] is not None)): if not isinstance(network['dns_addr'], list): raise exception.SDKInvalidInputTypes( 'guest_config_network', str(list), str(type(network['dns_addr']))) for dns in network['dns_addr']: if not netaddr.valid_ipv4(dns): errmsg = ("API guest_create_network_interface: " "Invalid dns IP address, it should be the " "value between 0.0.0.0 and 255.255.255.255") raise exception.SDKInvalidInputFormat(msg=errmsg) if (('gateway_addr' in network.keys()) and (network['gateway_addr'] is not None)): if not netaddr.valid_ipv4( network['gateway_addr']): errmsg = ("API guest_create_network_interface: " "Invalid gateway IP address, it should be " "the value between 0.0.0.0 and 255.255.255.255") raise exception.SDKInvalidInputFormat(msg=errmsg) if (('cidr' in network.keys()) and (network['cidr'] is not None)): if not zvmutils.valid_cidr(network['cidr']): errmsg = ("API guest_create_network_interface: " "Invalid CIDR, format should be a.b.c.d/n, and " "a.b.c.d is IP address, n is the value " "between 0-32") raise exception.SDKInvalidInputFormat(msg=errmsg) try: if OSA is None: used_vdev = self._networkops.create_nic(userid, vdev=vdev, nic_id=nic_id, mac_addr=mac_addr, active=active) else: used_vdev = self._networkops.dedicate_OSA(userid, OSA, vdev=vdev, active=active) network['nic_vdev'] = used_vdev except exception.SDKBaseException: LOG.error(('Failed to create nic on vm %s') % userid) raise try: self._networkops.network_configuration(userid, os_version, guest_networks, active=active) except exception.SDKBaseException: LOG.error(('Failed to set network configuration file on vm %s') % userid) raise return guest_networks def guests_get_nic_info(self, userid=None, nic_id=None, vswitch=None): """ Retrieve nic information in the network database according to the requirements, the nic information will include the guest name, nic device number, vswitch name that the nic is coupled to, nic identifier and the comments. :param str userid: the user id of the vm :param str nic_id: nic identifier :param str vswitch: the name of the vswitch :returns: list describing nic information, format is [ (userid, interface, vswitch, nic_id, comments), (userid, interface, vswitch, nic_id, comments) ], such as [ ('VM01', '1000', 'xcatvsw2', '1111-2222', None), ('VM02', '2000', 'xcatvsw3', None, None) ] :rtype: list """ action = "get nic information" with zvmutils.log_and_reraise_sdkbase_error(action): return self._networkops.get_nic_info(userid=userid, nic_id=nic_id, vswitch=vswitch) def vswitch_query(self, vswitch_name): """Check the virtual switch status :param str vswitch_name: the name of the virtual switch :returns: Dictionary describing virtual switch info :rtype: dict """ action = "get virtual switch information" with zvmutils.log_and_reraise_sdkbase_error(action): return self._networkops.vswitch_query(vswitch_name) @check_guest_exist() def guest_delete_network_interface(self, userid, os_version, vdev, active=False): """ delete the nic and network configuration for the vm :param str userid: the user id of the guest :param str os_version: operating system version of the guest :param str vdev: nic device number, 1- to 4- hexadecimal digits :param bool active: whether delete a nic on active guest system """ self._networkops.delete_nic(userid, vdev, active=active) self._networkops.delete_network_configuration(userid, os_version, vdev, active=active) zVMCloudConnector-1.4.1/zvmsdk/config.py0000775000175000017510000005712013442676324017676 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from six.moves import configparser class Opt(object): def __init__(self, opt_name, section='default', opt_type='str', help='', default=None, required=False): self.name = opt_name self.section = section self.opt_type = opt_type self.default = default self.required = required self.help = help zvm_opts = [ # logging options Opt('log_dir', section='logging', default='/var/log/zvmsdk/', help=''' Directory where log file to be put into. SDK has a set of logs to help administrator to debug and aduit actions performed through SDK. Edit this option if you want to put logs into specified place. Please ensure the service running on the consume which consumes SDK has the authorization to write to the path. '''), Opt('log_level', section='logging', default='logging.INFO', help=''' Level of the log. SDK utilize python logging package to help admin debug or analyze issues. it's recommend to set this value to logging.DEBUG to get more detailed logs and set it to logging.INFO(default) in normal situation. recommend values: logging.ERROR: level above ERROR will be written to log file. logging.WARNINS: level above WARNING(ERROR, WARNING) will be written to log file. logging.INFO: level above INFO(ERROR, WARNING, INFO) will be written to log file. logging.DEBUG: All log level (ERROR, WARNING, INFO, DEBUG) will be written to log file. '''), # zvm options Opt('default_nic_vdev', section='zvm', default='1000', help=''' Virtual device number for default NIC address. This value is the first NIC virtual device number, each NIC needs 3 numbers for control/read/write, so by default the first NIC's address is 1000, the second one is 1003 etc. Possible values: An integer value in hex format, between 0 and 65536 (x'FFFF'). It should not conflict with other device numbers in the z/VM guest's configuration, for example device numbers of the root or ephemeral or persistent disks. Sample NIC definitions in the z/VM user directory: NICDEF 1000 TYPE QDIO LAN SYSTEM MACID NICDEF 1003 TYPE QDIO LAN SYSTEM MACID ''' ), Opt('default_admin_userid', section='zvm', 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 SDK 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 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. '''), # FIXME: remove this option when switch to smut Opt('user_default_password', section='zvm'), Opt('disk_pool', section='zvm', required=True, help=''' zVM disk pool and type for root/ephemeral disks. The option is combined by 2 parts and use : as separator. The first part is the type of disks in the disk pool. The disks in one disk pool must in same type (ECKD or FBA). Possible values of the disk pool type: A string, either ECKD or FBA. The second part is the volume group name defined in your directory manager on your z/VM system, which will be used for allocating disks for new guest. A dollar sign ($) is not allowed in the name. Sample disk_pool values: ECKD:diskpo1 FBA:testpool '''), Opt('user_profile', section='zvm', required=True, help=''' PROFILE name to use when creating a z/VM guest. When SDK deploys an guest 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. '''), Opt('user_root_vdev', section='zvm', default='0100', help=''' Virtual device number for root disk. When SDK deploys an guest, it creates a root disk and potentially several data disks. This value is the virtual device number of the root disk. 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 '''), Opt('user_default_max_cpu', section='zvm', default=32, opt_type='int', help=''' The default maximum number of virtual processers the user can define. This value is used as the default value for maximum vcpu number when create a guest with no max_cpu specified. The number must be a decimal value between 1 and 64. '''), Opt('user_default_max_memory', section='zvm', default='64G', help=''' The default maximum size of memory the user can define. This value is used as the default value for maximum memory size when create a guest with no max_mem specified. The value can be specified by 1-4 bits of number suffixed by either M (Megabytes) or G (Gigabytes) and the number must be a whole number, values such as 4096.8M or 32.5G are not supported. The value should be adjusted based on your system capacity. '''), Opt('namelist', section='zvm', help=''' The name of a list containing names of virtual servers to be queried. The list which contains the userid list by default is named: VSMWORK1 NAMELIST, see DMSSICNF COPY key: NameListFileIdAny. The list has to be accessible to the SMAPI servers. The length of namelist must no longer than 64. '''), Opt('remotehost_sshd_port', section='zvm', default='22', help=''' The port number of remotehost sshd. '''), # image options Opt('sdk_image_repository', section='image', default='/var/lib/zvmsdk/images', help=''' Directory to store sdk images. SDK image repository to store the imported images and the staging images that is in snapshotting. Once snapshot finished, the image will be removed to the netboot directory accordingly. Two kinds of image repository looks like: /var/lib/zvmsdk/images/netboot// /var/lib/zvmsdk/images/staging// '''), # file options Opt('file_repository', section='file', default='/var/lib/zvmsdk/files', help=''' Directory to store sdk imported or exported files. SDK file repository to store the imported files and the files that will be exported, the imported files will be put into /imported the files to be exported will be put into /exported '''), # network options Opt('my_ip', section='network', required=True, help=''' IP address of the Linux machine which is running SDK on. Some remote copy operations need to be performed during guest creation, this option tell the SDK the host ip which can be used to perform copy from and copy to operations. '''), # guest options Opt('console_log_size', section='guest', default=100, opt_type='int', help=''' The maximum allowed console log size, in kilobytes. Console logs might be transferred to sdk user, this option 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. '''), # monitor options Opt('cache_interval', section='monitor', default=300, opt_type='int', help=''' Cached monitor data update interval This is used to prevent excessive effort spent retrieving the monitor data by calling the SDK backend utilities. When this cache is enabled, a inspect call will only call the SDK backend utilities when the inspected guest's info does not exist in the cache or when the cache data is expired. And when an cache update is needed, all the existing guests' data will be retrieved in a single call to the backend. When this value is below or equal to zero, the cache will be disabled and each inspect call will need to call the backend utilities to get the inspected guest's monitor data. ''' ), # wsgi options # this option is used when sending http request # to sdk wsgi, default to none so no token validation # will be used. Opt('auth', section='wsgi', default='none', opt_type='str', help=''' Whether auth will be used. When sending http request from outside to running zvmsdk, Client will be requested to input username/password in order to authorize the call. Set this to 'none' indicated no auth will be used and 'auth' means username and password need to be specified. Possible value: 'none': no auth will be required 'auth': need auth, currently pyjwt is used to return a token to caller if the username and password is correct. ''', ), Opt('token_validation_period', section='wsgi', default=3600, opt_type='int', help=''' How long the token is valid. If a token auth is used, the token return to user will be expired after the period passed. This ensure an user who get this token will not be authorized all the time, a new token need to be recreated after certain time period. ''', ), Opt('token_path', section='wsgi', default='/etc/zvmsdk/token.dat', opt_type='str', help=''' file path that contains admin-token to access sdk http server. Admin-token in order to get a user-token from zvm sdk, and the user-token will be used to validate request before user-token expire. ''' ), Opt('max_concurrent_deploy_capture', section='wsgi', default=20, opt_type='int', help=''' The max total number of concurrent deploy and capture requests allowed in a single z/VM Cloud Connector process. If more requests than this value are revieved concurrently, the z/VM Cloud Connector would reject the requests and return error to avoid resource exhaustion. . ''' ), # Daemon server options Opt('bind_addr', section='sdkserver', default='127.0.0.1', opt_type='str', help=''' The IP address that the SDK server is listen on. When the SDK server deamon starts, it will try to bind to this address and port bind_port, and wait for the SDK client connection to handle API request. ''' ), Opt('bind_port', section='sdkserver', opt_type='int', default=2000, help=''' The port that the SDK server is listen on. This will work as a pair with bind_addr when the SDK server daemon starts, more info can be found in that configuration description. ''' ), Opt('request_queue_size', section='sdkserver', opt_type='int', default=128, help=''' The size of request queue in SDK server. SDK server maintains a queue to keep all the accepted but not handled requests, and the SDK server workers fetch requests from this queue. To some extend, this queue size decides the max socket opened in SDK server. This value should be adjusted according to the system resource. ''' ), Opt('max_worker_count', section='sdkserver', opt_type='int', default=64, help=''' The maximum number of worker thread in SDK server to handle client requests. These worker threads would work concurrently to handle requests from client. This value should be adjusted according to the system resource and workload. ''' ), # database options Opt('dir', section='database', default='/var/lib/zvmsdk/databases/', opt_type='str', help=''' Directory to store database. SDK databases are used to store a set of tables which contain the information of network, volume, image, etc. This option is used to tell SDK where to store the database files, make sure the process running SDK is able to read write and execute the directory. ''' ), # volume options Opt('fcp_list', section='volume', default='', opt_type='str', help=''' volume fcp list. SDK will only use the fcp devices in the scope of this value. '''), # tests options Opt('images', section='tests', opt_type='str', ), Opt('userid_prefix', section='tests', default='tst', opt_type='str', ), Opt('ip_addr_list', section='tests', default='192.168.0.2 192.168.0.3 192.168.0.4 192.168.0.5 192.168.0.6', opt_type='str', ), Opt('vswitch', section='tests', opt_type='str', ), Opt('gateway_v4', section='tests'), Opt('cidr', section='tests'), Opt('restapi_url', section='tests', default='http://127.0.0.1:8888'), Opt('zvm_fcp', section='tests'), Opt('target_wwpn', section='tests'), Opt('target_lun', section='tests'), Opt('mount_point', section='tests'), ] class ConfigOpts(object): def __init__(self): self.dicts = {} def get_config_dicts_default(self, opts): _dict = {} for opt in opts: sec = opt.section if _dict.get(sec) is None: _dict[sec] = {} _dict[sec][opt.name] = {'required': opt.required, 'default': opt.default, 'type': opt.opt_type, 'help': opt.help} return _dict def register(self, opts): # Register the defined options and parse to dict self.dicts = self.get_config_dicts_default(opts) return self.clear_and_to_dict() def config(self): # Load config file and merge with default definitions # read config file override_dicts = self.read_config_file_to_dicts() # overwrite default value try: self.dicts = self.merge(self.dicts, override_dicts) except ImportError: pass # Check config value self._check_value(self.dicts) # Clear unused attributes of each option, and parse to our defined dict return self.clear_and_to_dict() def read_config_file_to_dicts(self): configs = {} read_file = self.find_config_file(project="zvmsdk") if read_file is None: raise ConfFileMissingError() else: cf = configparser.ConfigParser() cf.read(read_file) # read each section and option to dict secs = cf.sections() for sec in secs: configs[sec] = {} # get all options of the section in a list opts = cf.options(sec) for opt in opts: val = cf.get(sec, opt) configs[sec][opt] = val return configs def merge(self, defaults, override): # merge the defaults and overridden # The overridden options would only have 'default' set in the # resulted dicts r = {} for k, v in defaults.items(): if k in override: if isinstance(v, dict) and isinstance(override[k], dict): r[k] = self.merge(v, override[k]) elif isinstance(v, dict): if override[k] is not None: v['default'] = override[k] r[k] = v else: r[k] = override[k] else: r[k] = v return r def clear_and_to_dict(self): # This function would clear the dict to remove the unused keys # ('required', 'default', 'type', 'help'), set the opt value to # the final value merged in 'default'. # And then, convert the python dict to our defined Dict object clear_dict = {} pydict = self.dicts for k1, v1 in pydict.items(): r_con = {} for k2, v2 in v1.items(): r_con[k2] = v2['default'] clear_dict[k1] = r_con return self.toDict(clear_dict) def _check_value(self, conf): for k1, v1 in conf.items(): for k2, v2 in v1.items(): # Check required options if v2['required'] and (v2['default'] is None): raise RequiredOptMissingError(k1, k2) # Convert type if v2['type'] == 'int': v2['default'] = int(v2['default']) # Check format if (k2 == "disk_pool") and (v2['default'] is not None): self._check_zvm_disk_pool(v2['default']) # check user_default_max_memory if (k2 == "user_default_max_memory") and ( v2['default'] is not None): self._check_user_default_max_memory(v2['default']) # check user_default_max_cpu if (k2 == "user_default_max_cpu") and ( v2['default'] is not None): self._check_user_default_max_cpu(v2['default']) def _check_zvm_disk_pool(self, value): disks = value.split(':') if (len(disks) != 2) or (disks[0].upper() not in ['FBA', 'ECKD']) or ( disks[1] == ''): raise OptFormatError("zvm", "disk_pool", value) def _check_user_default_max_memory(self, value): suffix = value[-1].upper() size = value[:-1] if (suffix not in ['G', 'M']) or (len(size) > 4) or ( size.strip('0123456789') != ''): raise OptFormatError("zvm", "user_default_max_memory", value) def _check_user_default_max_cpu(self, value): if (value < 1) or (value > 64): raise OptFormatError("zvm", "user_default_max_cpu", value) def toDict(self, d): D = Dict() for k, v in d.items(): D[k] = self.toDict(v) if isinstance(v, dict) else v return D def _fixpath(self, p): """Apply tilde expansion and absolutization to a path.""" return os.path.abspath(os.path.expanduser(p)) def _get_config_dirs(self): """Return a list of directories where config files may be located. following directories are returned:: ./ ../etc ~/ /etc/zvmsdk/ """ _cwd = os.path.split(os.path.abspath(__file__))[0] _pdir = os.path.split(_cwd)[0] _etcdir = ''.join((_pdir, '/', 'etc/')) cfg_dirs = [ self._fixpath(_cwd), self._fixpath('/etc/zvmsdk/'), self._fixpath('/etc/'), self._fixpath('~'), self._fixpath(_etcdir), ] return [x for x in cfg_dirs if x] def _search_dirs(self, dirs, basename, extension=""): """Search a list of directories for a given filename or directory name. Iterator over the supplied directories, returning the first file found with the supplied name and extension. :param dirs: a list of directories :param basename: the filename :param extension: the file extension, for example '.conf' :returns: the path to a matching file, or None """ for d in dirs: path = os.path.join(d, '%s%s' % (basename, extension)) if os.path.exists(path): return path return None def find_config_file(self, project=None, extension='.conf'): """Return the config file. :param project: "zvmsdk" :param extension: the type of the config file """ cfg_dirs = self._get_config_dirs() config_files = self._search_dirs(cfg_dirs, project, extension) return config_files class Dict(dict): ''' Simple dict but support access as x.y style. ''' def __init__(self, names=(), values=(), **kw): super(Dict, self).__init__(**kw) for k, v in zip(names, values): self[k] = v def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'CONF' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value class RequiredOptMissingError(Exception): """Raised if an option is required but no value is supplied by the user.""" def __init__(self, grp_name, opt_name): self.grp_name = grp_name self.opt_name = opt_name def __str__(self): return "value required for option %s - %s" % (self.grp_name, self.opt_name) class ConfFileMissingError(Exception): """Raised if the configuration file zvmsdk.conf cann't be found.""" def __init__(self): message = "zvmsdk.conf is not found." super(ConfFileMissingError, self).__init__(message) class OptFormatError(Exception): """Raised if an option is required but no value is supplied by the user.""" def __init__(self, grp_name, opt_name, value): self.grp_name = grp_name self.opt_name = opt_name self.value = value def __str__(self): return "value %s for option %s - %s is invalid" % (self.value, self.grp_name, self.opt_name) CONFOPTS = ConfigOpts() CONF = CONFOPTS.register(zvm_opts) def load_config(): global CONF CONF = CONFOPTS.config() zVMCloudConnector-1.4.1/zvmsdk/volumeop.py0000775000175000017510000006074013442676317020303 0ustar ruirui00000000000000# 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. import abc import re import shutil import six import os from zvmsdk import config from zvmsdk import database from zvmsdk import dist from zvmsdk import exception from zvmsdk import log from zvmsdk import smutclient from zvmsdk import utils as zvmutils from zvmsdk import vmops _VolumeOP = None CONF = config.CONF LOG = log.LOG # instance parameters: NAME = 'name' OS_TYPE = 'os_type' # volume parameters: SIZE = 'size' TYPE = 'type' LUN = 'lun' # connection_info parameters: ALIAS = 'alias' PROTOCOL = 'protocol' FCPS = 'fcps' WWPNS = 'wwpns' DEDICATE = 'dedicate' def get_volumeop(): global _VolumeOP if not _VolumeOP: _VolumeOP = VolumeOperatorAPI() return _VolumeOP @six.add_metaclass(abc.ABCMeta) class VolumeOperatorAPI(object): """Volume operation APIs oriented towards SDK driver. The reason to design these APIs is to facilitate the SDK driver issuing a volume related request without knowing details. The details among different distributions, different instance status, different volume types and so on are all hidden behind these APIs. The only thing the issuer need to know is what it want to do on which targets. In fact, that's an ideal case. In the real world, something like connection_info still depends on different complex and the issuer needs to know how to deal with its case. Even so, these APIs can still make things much easier. """ _fcp_manager_obj = None def __init__(self): if not VolumeOperatorAPI._fcp_manager_obj: VolumeOperatorAPI._fcp_manager_obj = FCPVolumeManager() self._volume_manager = VolumeOperatorAPI._fcp_manager_obj def attach_volume_to_instance(self, connection_info): self._volume_manager.attach(connection_info) def detach_volume_from_instance(self, connection_info): self._volume_manager.detach(connection_info) def get_volume_connector(self, assigner_id): return self._volume_manager.get_volume_connector(assigner_id) @six.add_metaclass(abc.ABCMeta) class VolumeConfiguratorAPI(object): """Volume configure APIs to implement volume config jobs on the target instance, like: attach, detach, and so on. The reason to design these APIs is to hide the details among different Linux distributions and releases. """ def __init__(self): self._vmop = vmops.get_vmops() self._dist_manager = dist.LinuxDistManager() self._smutclient = smutclient.get_smutclient() def config_attach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point): linuxdist = self._dist_manager.get_linux_dist(os_version)() self.configure_volume_attach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point, linuxdist) # active mode should restart zvmguestconfigure to execute reader file if self._vmop.is_reachable(assigner_id): active_cmds = linuxdist.create_active_net_interf_cmd() self._smutclient.execute_cmd(assigner_id, active_cmds) def config_force_attach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version): pass def config_detach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point): linuxdist = self._dist_manager.get_linux_dist(os_version)() self.configure_volume_detach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point, linuxdist) # active mode should restart zvmguestconfigure to execute reader file if self._vmop.is_reachable(assigner_id): active_cmds = linuxdist.create_active_net_interf_cmd() self._smutclient.execute_cmd(assigner_id, active_cmds) def config_force_detach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version): pass def _create_file(self, assigner_id, file_name, data): temp_folder = self._smutclient.get_guest_temp_path(assigner_id) file_path = os.path.join(temp_folder, file_name) with open(file_path, "w") as f: f.write(data) return file_path, temp_folder def configure_volume_attach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point, linuxdist): # get configuration commands config_cmds = linuxdist.get_volume_attach_configuration_cmds( fcp, target_wwpn, target_lun, multipath, mount_point) LOG.debug('Got volume attachment configuation cmds for %s,' 'the content is:%s' % (assigner_id, config_cmds)) # write commands into script file config_file, config_file_path = self._create_file(assigner_id, 'atvol.sh', config_cmds) LOG.debug('Creating file %s to contain volume attach ' 'configuration file' % config_file) # punch file into guest fileClass = "X" try: self._smutclient.punch_file(assigner_id, config_file, fileClass) finally: LOG.debug('Removing the folder %s ', config_file_path) shutil.rmtree(config_file_path) def configure_volume_detach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point, linuxdist): # get configuration commands config_cmds = linuxdist.get_volume_detach_configuration_cmds( fcp, target_wwpn, target_lun, multipath, mount_point) LOG.debug('Got volume detachment configuation cmds for %s,' 'the content is:%s' % (assigner_id, config_cmds)) # write commands into script file config_file, config_file_path = self._create_file(assigner_id, 'devol.sh', config_cmds) LOG.debug('Creating file %s to contain volume detach ' 'configuration file' % config_file) # punch file into guest fileClass = "X" try: self._smutclient.punch_file(assigner_id, config_file, fileClass) finally: LOG.debug('Removing the folder %s ', config_file_path) shutil.rmtree(config_file_path) class FCP(object): def __init__(self, init_info): self._dev_no = None self._npiv_port = None self._chpid = None self._physical_port = None self._assigned_id = None self._parse(init_info) @staticmethod def _get_wwpn_from_line(info_line): wwpn = info_line.split(':')[-1].strip().lower() return wwpn if (wwpn and wwpn.upper() != 'NONE') else None @staticmethod def _get_dev_number_from_line(info_line): dev_no = info_line.split(':')[-1].strip().lower() return dev_no if dev_no else None @staticmethod def _get_chpid_from_line(info_line): chpid = info_line.split(':')[-1].strip().upper() return chpid if chpid else None def _parse(self, init_info): """Initialize a FCP device object from several lines of string describing properties of the FCP device. Here is a sample: opnstk1: FCP device number: B83D opnstk1: Status: Free opnstk1: NPIV world wide port number: NONE opnstk1: Channel path ID: 59 opnstk1: Physical world wide port number: 20076D8500005181 The format comes from the response of xCAT, do not support arbitrary format. """ if isinstance(init_info, list) and (len(init_info) == 5): self._dev_no = self._get_dev_number_from_line(init_info[0]) self._npiv_port = self._get_wwpn_from_line(init_info[2]) self._chpid = self._get_chpid_from_line(init_info[3]) self._physical_port = self._get_wwpn_from_line(init_info[4]) def get_dev_no(self): return self._dev_no def get_npiv_port(self): return self._npiv_port def get_physical_port(self): return self._physical_port def get_chpid(self): return self._chpid def is_valid(self): # FIXME: add validation later return True class FCPManager(object): def __init__(self): self._fcp_pool = {} self.db = database.FCPDbOperator() self._smutclient = smutclient.get_smutclient() def init_fcp(self, assigner_id): """init_fcp to init the FCP managed by this host""" # TODO master_fcp_list (zvm_zhcp_fcp_list) really need? fcp_list = CONF.volume.fcp_list if fcp_list == '': errmsg = ("because CONF.volume.fcp_list is empty, " "no volume functions available") LOG.info(errmsg) return self._fcp_info = self._init_fcp_pool(fcp_list, assigner_id) self._sync_db_fcp_list() def _init_fcp_pool(self, fcp_list, assigner_id): """The FCP infomation got from smut(zthin) looks like : host: FCP device number: xxxx host: Status: Active host: NPIV world wide port number: xxxxxxxx host: Channel path ID: xx host: Physical world wide port number: xxxxxxxx ...... host: FCP device number: xxxx host: Status: Active host: NPIV world wide port number: xxxxxxxx host: Channel path ID: xx host: Physical world wide port number: xxxxxxxx """ complete_fcp_set = self._expand_fcp_list(fcp_list) fcp_info = self._get_all_fcp_info(assigner_id) lines_per_item = 5 num_fcps = len(fcp_info) // lines_per_item for n in range(0, num_fcps): fcp_init_info = fcp_info[(5 * n):(5 * (n + 1))] fcp = FCP(fcp_init_info) dev_no = fcp.get_dev_no() if dev_no in complete_fcp_set: if fcp.is_valid(): self._fcp_pool[dev_no] = fcp else: errmsg = ("Find an invalid FCP device with properties {" "dev_no: %(dev_no)s, " "NPIV_port: %(NPIV_port)s, " "CHPID: %(CHPID)s, " "physical_port: %(physical_port)s} !") % { 'dev_no': fcp.get_dev_no(), 'NPIV_port': fcp.get_npiv_port(), 'CHPID': fcp.get_chpid(), 'physical_port': fcp.get_physical_port()} LOG.warning(errmsg) else: # normal, FCP not used by cloud connector at all msg = "Found a fcp %s not in fcp_list" % dev_no LOG.debug(msg) @staticmethod def _expand_fcp_list(fcp_list): """Expand fcp list string into a python list object which contains each fcp devices in the list string. A fcp list is composed of fcp device addresses, range indicator '-', and split indicator ';'. For example, if fcp_list is "0011-0013;0015;0017-0018", expand_fcp_list(fcp_list) will return [0011, 0012, 0013, 0015, 0017, 0018]. """ LOG.debug("Expand FCP list %s" % fcp_list) if not fcp_list: return set() range_pattern = '[0-9a-fA-F]{1,4}(-[0-9a-fA-F]{1,4})?' match_pattern = "^(%(range)s)(;%(range)s)*$" % {'range': range_pattern} if not re.match(match_pattern, fcp_list): errmsg = ("Invalid FCP address %s") % fcp_list raise exception.SDKInternalError(msg=errmsg) fcp_devices = set() for _range in fcp_list.split(';'): if '-' not in _range: # single device fcp_addr = int(_range, 16) fcp_devices.add("%04x" % fcp_addr) else: # a range of address (_min, _max) = _range.split('-') _min = int(_min, 16) _max = int(_max, 16) for fcp_addr in range(_min, _max + 1): fcp_devices.add("%04x" % fcp_addr) # remove duplicate entries return fcp_devices def _report_orphan_fcp(self, fcp): """check there is record in db but not in FCP configuration""" LOG.warning("WARNING: fcp %s found in db but not in " "CONF.volume.fcp_list which is %s" % (fcp, CONF.volume.fcp_list)) def _add_fcp(self, fcp): """add fcp to db if it's not in db but in fcp list and init it""" try: LOG.info("fcp %s found in CONF.volume.fcp_list, add it to db" % fcp) self.db.new(fcp) except Exception: LOG.info("failed to add fcp %s into db", fcp) def _sync_db_fcp_list(self): """sync db records from given fcp list, for example, you need warn if some FCP already removed while it's still in use, or info about the new FCP added""" fcp_db_list = self.db.get_all() for fcp_rec in fcp_db_list: if not fcp_rec[0].lower() in self._fcp_pool: self._report_orphan_fcp(fcp_rec[0]) for fcp_conf_rec, v in self._fcp_pool.items(): res = self.db.get_from_fcp(fcp_conf_rec) # if not found this record, a [] will be returned if len(res) == 0: self._add_fcp(fcp_conf_rec) def _list_fcp_details(self, userid, status): return self._smutclient.get_fcp_info_by_status(userid, status) def _get_all_fcp_info(self, assigner_id): fcp_info = [] free_fcp_info = self._list_fcp_details(assigner_id, 'free') active_fcp_info = self._list_fcp_details(assigner_id, 'active') if free_fcp_info: fcp_info.extend(free_fcp_info) if active_fcp_info: fcp_info.extend(active_fcp_info) return fcp_info def find_and_reserve_fcp(self, assigner_id): """reserve the fcp to assigner_id The function to reserve a fcp for user 1. Check whether assigner_id has a fcp already if yes, make the reserve of that record to 1 2. No fcp, then find a fcp and reserve it fcp will be returned, or None indicate no fcp """ fcp_list = self.db.get_from_assigner(assigner_id) if not fcp_list: new_fcp = self.db.find_and_reserve() if new_fcp is None: LOG.info("no more fcp to be allocated") return None LOG.debug("allocated %s fcp for %s assigner" % (new_fcp, assigner_id)) return new_fcp else: # we got it from db, let's reuse it old_fcp = fcp_list[0][0] self.db.reserve(fcp_list[0][0]) return old_fcp def increase_fcp_usage(self, fcp, assigner_id=None): """Incrase fcp usage of given fcp Returns True if it's a new fcp, otherwise return False """ # TODO: check assigner_id to make sure on the correct fcp record connections = self.db.get_connections_from_assigner(assigner_id) new = False if connections == 0: self.db.assign(fcp, assigner_id) new = True else: self.db.increase_usage(fcp) return new def decrease_fcp_usage(self, fcp, assigner_id=None): # TODO: check assigner_id to make sure on the correct fcp record connections = self.db.decrease_usage(fcp) return connections def unreserve_fcp(self, fcp, assigner_id=None): # TODO: check assigner_id to make sure on the correct fcp record self.db.unreserve(fcp) def is_reserved(self, fcp): self.db.is_reserved(fcp) def get_available_fcp(self): """get all the fcps not reserved""" # get the unreserved FCP devices belongs to assigner_id available_list = [] free_unreserved = self.db.get_all_free_unreserved() for item in free_unreserved: available_list.append(item[0]) return available_list def get_wwpn(self, fcp_no): fcp = self._fcp_pool.get(fcp_no) if not fcp: return None npiv = fcp.get_npiv_port() physical = fcp.get_physical_port() if npiv: return npiv if physical: return physical return None # volume manager for FCP protocol class FCPVolumeManager(object): def __init__(self): self.fcp_mgr = FCPManager() self.config_api = VolumeConfiguratorAPI() self._smutclient = smutclient.get_smutclient() def _dedicate_fcp(self, fcp, assigner_id): self._smutclient.dedicate_device(assigner_id, fcp, fcp, 0) def _add_disk(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point): self.config_api.config_attach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) def _attach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point): """Attach a volume First, we need translate fcp into local wwpn, then dedicate fcp to the user if it's needed, after that call smut layer to call linux command """ LOG.info('Start to attach device to %s' % assigner_id) self.fcp_mgr.init_fcp(assigner_id) new = self.fcp_mgr.increase_fcp_usage(fcp, assigner_id) try: if new: self._dedicate_fcp(fcp, assigner_id) self._add_disk(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) except exception.SDKBaseException as err: errmsg = 'rollback attach because error:' + err.format_message() LOG.error(errmsg) connections = self.fcp_mgr.decrease_fcp_usage(fcp, assigner_id) # if connections less than 1, undedicate the device if not connections: with zvmutils.ignore_errors(): self._undedicate_fcp(fcp, assigner_id) raise exception.SDKBaseException(msg=errmsg) # TODO: other exceptions? LOG.info('Attaching device to %s is done.' % assigner_id) def attach(self, connection_info): """Attach a volume to a guest connection_info contains info from host and storage side this mostly includes host side FCP: this can get host side wwpn storage side wwpn storage side lun all the above assume the storage side info is given by caller """ fcp = connection_info['zvm_fcp'] fcp = fcp.lower() target_wwpn = connection_info['target_wwpn'] target_lun = connection_info['target_lun'] assigner_id = connection_info['assigner_id'] assigner_id = assigner_id.upper() multipath = connection_info['multipath'] os_version = connection_info['os_version'] mount_point = connection_info['mount_point'] # TODO: check exist in db? if not zvmutils.check_userid_exist(assigner_id): LOG.error("User directory '%s' does not exist." % assigner_id) raise exception.SDKObjectNotExistError( obj_desc=("Guest '%s'" % assigner_id), modID='volume') else: self._attach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) def _undedicate_fcp(self, fcp, assigner_id): self._smutclient.undedicate_device(assigner_id, fcp) def _remove_disk(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point): self.config_api.config_detach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) def _detach(self, fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point): """Detach a volume from a guest""" LOG.info('Start to detach device from %s' % assigner_id) connections = self.fcp_mgr.decrease_fcp_usage(fcp, assigner_id) try: self._remove_disk(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) if not connections: self._undedicate_fcp(fcp, assigner_id) except (exception.SDKBaseException, exception.SDKSMUTRequestFailed) as err: errmsg = 'rollback detach because error:' + err.format_message() LOG.error(errmsg) self.fcp_mgr.increase_fcp_usage(fcp, assigner_id) with zvmutils.ignore_errors(): self._add_disk(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) raise exception.SDKBaseException(msg=errmsg) LOG.info('Detaching device to %s is done.' % assigner_id) def detach(self, connection_info): """Detach a volume from a guest """ fcp = connection_info['zvm_fcp'] fcp = fcp.lower() target_wwpn = connection_info['target_wwpn'] target_lun = connection_info['target_lun'] assigner_id = connection_info['assigner_id'] assigner_id = assigner_id.upper() multipath = connection_info['multipath'] os_version = connection_info['os_version'] mount_point = connection_info['mount_point'] if not zvmutils.check_userid_exist(assigner_id): LOG.error("Guest '%s' does not exist" % assigner_id) raise exception.SDKObjectNotExistError( obj_desc=("Guest '%s'" % assigner_id), modID='volume') else: self._detach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) def get_volume_connector(self, assigner_id): """Get connector information of the instance for attaching to volumes. Connector information is a dictionary representing the ip of the machine that will be making the connection, the name of the iscsi initiator and the hostname of the machine as follows:: { 'zvm_fcp': fcp 'wwpns': [wwpn] 'host': host } """ empty_connector = {'zvm_fcp': [], 'wwpns': [], 'host': ''} # init fcp pool self.fcp_mgr.init_fcp(assigner_id) fcp_list = self.fcp_mgr.get_available_fcp() if not fcp_list: errmsg = "No available FCP device found." LOG.warning(errmsg) return empty_connector wwpns = [] for fcp_no in fcp_list: wwpn = self.fcp_mgr.get_wwpn(fcp_no) if not wwpn: errmsg = "FCP device %s has no available WWPN." % fcp_no LOG.warning(errmsg) else: wwpns.append(wwpn) if not wwpns: errmsg = "No available WWPN found." LOG.warning(errmsg) return empty_connector inv_info = self._smutclient.get_host_info() zvm_host = inv_info['zvm_host'] if zvm_host == '': errmsg = "zvm host not specified." LOG.warning(errmsg) return empty_connector connector = {'zvm_fcp': fcp_list, 'wwpns': wwpns, 'host': zvm_host} LOG.debug('get_volume_connector returns %s for %s' % (connector, assigner_id)) return connector zVMCloudConnector-1.4.1/zvmsdk/version.py0000664000175000017510000000154413442723330020100 0ustar ruirui00000000000000# 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. import sys __all__ = ['__version__'] __version__ = '1.4.1' # Check supported Python versions _PYTHON_M = sys.version_info[0] _PYTHON_N = sys.version_info[1] if _PYTHON_M == 2 and _PYTHON_N < 7: raise RuntimeError('On Python 2, zvm sdk requires Python 2.7') zVMCloudConnector-1.4.1/zvmsdk/hostops.py0000664000175000017510000000667013442676317020133 0ustar ruirui00000000000000# 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. from zvmsdk import config from zvmsdk import constants as const from zvmsdk import exception from zvmsdk import log from zvmsdk import smutclient from zvmsdk import utils as zvmutils _HOSTOPS = None CONF = config.CONF LOG = log.LOG def get_hostops(): global _HOSTOPS if _HOSTOPS is None: _HOSTOPS = HOSTOps() return _HOSTOPS class HOSTOps(object): def __init__(self): self._smutclient = smutclient.get_smutclient() def get_info(self): inv_info = self._smutclient.get_host_info() host_info = {} with zvmutils.expect_invalid_resp_data(inv_info): host_info['zcc_userid'] = inv_info['zcc_userid'] host_info['zvm_host'] = inv_info['zvm_host'] host_info['vcpus'] = int(inv_info['lpar_cpu_total']) host_info['vcpus_used'] = int(inv_info['lpar_cpu_used']) host_info['cpu_info'] = {} host_info['cpu_info'] = {'architecture': const.ARCHITECTURE, 'cec_model': inv_info['cec_model'], } mem_mb = zvmutils.convert_to_mb(inv_info['lpar_memory_total']) host_info['memory_mb'] = mem_mb mem_mb_used = zvmutils.convert_to_mb(inv_info['lpar_memory_used']) host_info['memory_mb_used'] = mem_mb_used host_info['hypervisor_type'] = const.HYPERVISOR_TYPE verl = inv_info['hypervisor_os'].split()[1].split('.') version = int(''.join(verl)) host_info['hypervisor_version'] = version host_info['hypervisor_hostname'] = inv_info['hypervisor_name'] host_info['ipl_time'] = inv_info['ipl_time'] diskpool_name = CONF.zvm.disk_pool.split(':')[1] dp_info = self.diskpool_get_info(diskpool_name) host_info.update(dp_info) return host_info def diskpool_get_info(self, pool): dp_info = self._smutclient.get_diskpool_info(pool) with zvmutils.expect_invalid_resp_data(dp_info): for k in list(dp_info.keys()): s = dp_info[k].strip().upper() if s.endswith('G'): sl = s[:-1].split('.') n1, n2 = int(sl[0]), int(sl[1]) if n2 >= 5: n1 += 1 dp_info[k] = n1 elif s.endswith('M'): n_mb = int(s[:-3]) n_gb, n_ad = n_mb // 1024, n_mb % 1024 if n_ad >= 512: n_gb += 1 dp_info[k] = n_gb else: exp = "ending with a 'G' or 'M'" errmsg = ("Invalid diskpool size format: %(invalid)s; " "Expected: %(exp)s") % {'invalid': s, 'exp': exp} LOG.error(errmsg) raise exception.SDKInternalError(msg=errmsg) return dp_info zVMCloudConnector-1.4.1/zvmsdk/database.py0000664000175000017510000006563413401106372020165 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import os import six import sqlite3 import threading import uuid import json from zvmsdk import config from zvmsdk import constants as const from zvmsdk import exception from zvmsdk import log CONF = config.CONF LOG = log.LOG _DIR_MODE = 0o755 _VOLUME_CONN = None _NETWORK_CONN = None _IMAGE_CONN = None _GUEST_CONN = None _FCP_CONN = None _DBLOCK_VOLUME = threading.RLock() _DBLOCK_NETWORK = threading.RLock() _DBLOCK_IMAGE = threading.RLock() _DBLOCK_GUEST = threading.RLock() _DBLOCK_FCP = threading.RLock() @contextlib.contextmanager def get_network_conn(): global _NETWORK_CONN, _DBLOCK_NETWORK if not _NETWORK_CONN: _NETWORK_CONN = _init_db_conn(const.DATABASE_NETWORK) _DBLOCK_NETWORK.acquire() try: yield _NETWORK_CONN except Exception as err: msg = "Execute SQL statements error: %s" % six.text_type(err) LOG.error(msg) raise exception.SDKNetworkOperationError(rs=1, msg=msg) finally: _DBLOCK_NETWORK.release() @contextlib.contextmanager def get_image_conn(): global _IMAGE_CONN, _DBLOCK_IMAGE if not _IMAGE_CONN: _IMAGE_CONN = _init_db_conn(const.DATABASE_IMAGE) _DBLOCK_IMAGE.acquire() try: yield _IMAGE_CONN except Exception as err: LOG.error("Execute SQL statements error: %s", six.text_type(err)) raise exception.SDKDatabaseException(msg=err) finally: _DBLOCK_IMAGE.release() @contextlib.contextmanager def get_guest_conn(): global _GUEST_CONN, _DBLOCK_GUEST if not _GUEST_CONN: _GUEST_CONN = _init_db_conn(const.DATABASE_GUEST) _DBLOCK_GUEST.acquire() try: yield _GUEST_CONN except Exception as err: msg = "Execute SQL statements error: %s" % six.text_type(err) LOG.error(msg) raise exception.SDKGuestOperationError(rs=1, msg=msg) finally: _DBLOCK_GUEST.release() @contextlib.contextmanager def get_fcp_conn(): global _FCP_CONN, _DBLOCK_FCP if not _FCP_CONN: _FCP_CONN = _init_db_conn(const.DATABASE_FCP) _DBLOCK_FCP.acquire() try: yield _FCP_CONN except Exception as err: msg = "Execute SQL statements error: %s" % six.text_type(err) LOG.error(msg) raise exception.SDKGuestOperationError(rs=1, msg=msg) finally: _DBLOCK_FCP.release() def _init_db_conn(db_file): db_dir = CONF.database.dir if not os.path.exists(db_dir): os.makedirs(db_dir, _DIR_MODE) database = os.path.join(db_dir, db_file) return sqlite3.connect(database, check_same_thread=False, isolation_level=None) class NetworkDbOperator(object): def __init__(self): self._module_id = 'network' self._create_switch_table() def _create_switch_table(self): create_table_sql = ' '.join(( 'create table if not exists switch (', 'userid varchar(8) COLLATE NOCASE,', 'interface varchar(4) COLLATE NOCASE,', 'switch varchar(8) COLLATE NOCASE,', 'port varchar(128) COLLATE NOCASE,', 'comments varchar(128),', 'primary key (userid, interface));')) with get_network_conn() as conn: conn.execute(create_table_sql) def _get_switch_by_user_interface(self, userid, interface): with get_network_conn() as conn: res = conn.execute("SELECT * FROM switch " "WHERE userid=? and interface=?", (userid, interface)) switch_record = res.fetchall() if len(switch_record) == 1: return switch_record[0] elif len(switch_record) == 0: return None def switch_delete_record_for_userid(self, userid): """Remove userid switch record from switch table.""" with get_network_conn() as conn: conn.execute("DELETE FROM switch WHERE userid=?", (userid,)) LOG.debug("Switch record for user %s is removed from " "switch table" % userid) def switch_delete_record_for_nic(self, userid, interface): """Remove userid switch record from switch table.""" with get_network_conn() as conn: conn.execute("DELETE FROM switch WHERE userid=? and interface=?", (userid, interface)) LOG.debug("Switch record for user %s with nic %s is removed from " "switch table" % (userid, interface)) def switch_add_record(self, userid, interface, port=None, switch=None, comments=None): """Add userid and nic name address into switch table.""" with get_network_conn() as conn: conn.execute("INSERT INTO switch VALUES (?, ?, ?, ?, ?)", (userid, interface, switch, port, comments)) LOG.debug("New record in the switch table: user %s, " "nic %s, port %s" % (userid, interface, port)) def switch_add_record_migrated(self, userid, interface, switch, port=None, comments=None): """Add userid and interfaces and switch into switch table.""" with get_network_conn() as conn: conn.execute("INSERT INTO switch VALUES (?, ?, ?, ?, ?)", (userid, interface, switch, port, comments)) LOG.debug("New record in the switch table: user %s, " "nic %s, switch %s" % (userid, interface, switch)) def switch_update_record_with_switch(self, userid, interface, switch=None): """Update information in switch table.""" if not self._get_switch_by_user_interface(userid, interface): msg = "User %s with nic %s does not exist in DB" % (userid, interface) LOG.error(msg) obj_desc = ('User %s with nic %s' % (userid, interface)) raise exception.SDKObjectNotExistError(obj_desc, modID=self._module_id) if switch is not None: with get_network_conn() as conn: conn.execute("UPDATE switch SET switch=? " "WHERE userid=? and interface=?", (switch, userid, interface)) LOG.debug("Set switch to %s for user %s with nic %s " "in switch table" % (switch, userid, interface)) else: with get_network_conn() as conn: conn.execute("UPDATE switch SET switch=NULL " "WHERE userid=? and interface=?", (userid, interface)) LOG.debug("Set switch to None for user %s with nic %s " "in switch table" % (userid, interface)) def _parse_switch_record(self, switch_list): # Map each switch record to be a dict, with the key is the field name # in switch DB switch_keys_list = ['userid', 'interface', 'switch', 'port', 'comments'] switch_result = [] for item in switch_list: switch_item = dict(zip(switch_keys_list, item)) switch_result.append(switch_item) return switch_result def switch_select_table(self): with get_network_conn() as conn: result = conn.execute("SELECT * FROM switch") nic_settings = result.fetchall() return self._parse_switch_record(nic_settings) def switch_select_record_for_userid(self, userid): with get_network_conn() as conn: result = conn.execute("SELECT * FROM switch " "WHERE userid=?", (userid,)) switch_info = result.fetchall() return self._parse_switch_record(switch_info) def switch_select_record(self, userid=None, nic_id=None, vswitch=None): if ((userid is None) and (nic_id is None) and (vswitch is None)): return self.switch_select_table() sql_cmd = "SELECT * FROM switch WHERE" sql_var = [] if userid is not None: sql_cmd += " userid=? and" sql_var.append(userid) if nic_id is not None: sql_cmd += " port=? and" sql_var.append(nic_id) if vswitch is not None: sql_cmd += " switch=?" sql_var.append(vswitch) # remove the tailing ' and' sql_cmd = sql_cmd.strip(' and') with get_network_conn() as conn: result = conn.execute(sql_cmd, sql_var) switch_list = result.fetchall() return self._parse_switch_record(switch_list) class FCPDbOperator(object): def __init__(self): self._module_id = 'FCP' self._initialize_table() def _initialize_table(self): sql = ' '.join(( 'CREATE TABLE IF NOT EXISTS fcp(', 'fcp_id char(4) PRIMARY KEY COLLATE NOCASE,', 'assigner_id varchar(8) COLLATE NOCASE,', # foreign key of a VM 'connections integer,', # 0 means no assigner 'reserved integer,', # 0 for not reserved 'comment varchar(128))')) with get_fcp_conn() as conn: conn.execute(sql) def _update_reserve(self, fcp, reserved): with get_fcp_conn() as conn: conn.execute("UPDATE fcp SET reserved=? " "WHERE fcp_id=?", (reserved, fcp)) def unreserve(self, fcp): self._update_reserve(fcp, 0) def reserve(self, fcp): self._update_reserve(fcp, 1) def is_reserved(self, fcp): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp WHERE " "fcp_id=?", (fcp,)) fcp_list = result.fetchall() reserved = fcp_list[0][3] return reserved == 1 def find_and_reserve(self): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp where connections=0 " "and reserved=0") fcp_list = result.fetchall() if len(fcp_list) == 0: LOG.info("no more fcp to be allocated") return None # allocate first fcp found fcp = fcp_list[0][0] self._update_reserve(fcp, 1) return fcp def new(self, fcp): with get_fcp_conn() as conn: conn.execute("INSERT INTO fcp (fcp_id, assigner_id, " "connections, reserved, comment) VALUES " "(?, ?, ?, ?, ?)", (fcp, '', 0, 0, '')) def assign(self, fcp, assigner_id): with get_fcp_conn() as conn: conn.execute("UPDATE fcp SET assigner_id=?, connections=? " "WHERE fcp_id=?", (assigner_id, 1, fcp)) def delete(self, fcp): with get_fcp_conn() as conn: conn.execute("DELETE FROM fcp " "WHERE fcp_id=?", (fcp,)) def increase_usage(self, fcp): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp WHERE " "fcp_id=?", (fcp,)) fcp_list = result.fetchall() connections = fcp_list[0][2] connections += 1 conn.execute("UPDATE fcp SET connections=? " "WHERE fcp_id=?", (connections, fcp)) return connections def decrease_usage(self, fcp): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp WHERE " "fcp_id=?", (fcp,)) fcp_list = result.fetchall() connections = fcp_list[0][2] if connections == 0: msg = 'FCP with id: %s no connections in DB.' % fcp LOG.error(msg) obj_desc = "FCP with id: %s" % fcp raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID=self._module_id) else: connections -= 1 if connections < 0: connections = 0 LOG.warning("Warning: connections of fcp is negative", fcp) conn.execute("UPDATE fcp SET connections=? " "WHERE fcp_id=?", (connections, fcp)) return connections def get_connections_from_assigner(self, assigner_id): connections = 0 with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp WHERE " "assigner_id=?", (assigner_id,)) fcp_list = result.fetchall() if not fcp_list: connections = 0 else: connections = fcp_list[0][2] return connections def get_from_assigner(self, assigner_id): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp WHERE " "assigner_id=?", (assigner_id,)) fcp_list = result.fetchall() return fcp_list def get_all(self): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp") fcp_list = result.fetchall() return fcp_list def get_from_fcp(self, fcp): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp where fcp_id=?", (fcp,)) fcp_list = result.fetchall() return fcp_list def get_all_free_unreserved(self): with get_fcp_conn() as conn: result = conn.execute("SELECT * FROM fcp where connections=0 " "and reserved=0") fcp_list = result.fetchall() return fcp_list class ImageDbOperator(object): def __init__(self): self._create_image_table() self._module_id = 'image' def _create_image_table(self): create_image_table_sql = ' '.join(( 'CREATE TABLE IF NOT EXISTS image (', 'imagename varchar(128) PRIMARY KEY COLLATE NOCASE,', 'imageosdistro varchar(16),', 'md5sum varchar(512),', 'disk_size_units varchar(512),', 'image_size_in_bytes varchar(512),', 'type varchar(16),', 'comments varchar(128))')) with get_image_conn() as conn: conn.execute(create_image_table_sql) def image_add_record(self, imagename, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type, comments=None): if comments is not None: with get_image_conn() as conn: conn.execute("INSERT INTO image (imagename, imageosdistro," "md5sum, disk_size_units, image_size_in_bytes," " type, comments) VALUES (?, ?, ?, ?, ?, ?, ?)", (imagename, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type, comments)) else: with get_image_conn() as conn: conn.execute("INSERT INTO image (imagename, imageosdistro," "md5sum, disk_size_units, image_size_in_bytes," " type) VALUES (?, ?, ?, ?, ?, ?)", (imagename, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type)) def image_query_record(self, imagename=None): """Query the image record from database, if imagename is None, all of the image records will be returned, otherwise only the specified image record will be returned.""" if imagename: with get_image_conn() as conn: result = conn.execute("SELECT * FROM image WHERE " "imagename=?", (imagename,)) image_list = result.fetchall() if not image_list: obj_desc = "Image with name: %s" % imagename raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID=self._module_id) else: with get_image_conn() as conn: result = conn.execute("SELECT * FROM image") image_list = result.fetchall() # Map each image record to be a dict, with the key is the field name in # image DB image_keys_list = ['imagename', 'imageosdistro', 'md5sum', 'disk_size_units', 'image_size_in_bytes', 'type', 'comments'] image_result = [] for item in image_list: image_item = dict(zip(image_keys_list, item)) image_result.append(image_item) return image_result def image_delete_record(self, imagename): """Delete the record of specified imagename from image table""" with get_image_conn() as conn: conn.execute("DELETE FROM image WHERE imagename=?", (imagename,)) class GuestDbOperator(object): def __init__(self): self._create_guests_table() self._module_id = 'guest' def _create_guests_table(self): """ net_set: it is used to describe network interface status, the initial value is 0, no network interface. It will be updated to be 1 after the network interface is configured """ sql = ' '.join(( 'CREATE TABLE IF NOT EXISTS guests(', 'id char(36) PRIMARY KEY COLLATE NOCASE,', 'userid varchar(8) NOT NULL UNIQUE COLLATE NOCASE,', 'metadata varchar(255),', 'net_set smallint DEFAULT 0,', 'comments text)')) with get_guest_conn() as conn: conn.execute(sql) def _check_existence_by_id(self, guest_id, ignore=False): guest = self.get_guest_by_id(guest_id) if guest is None: msg = 'Guest with id: %s does not exist in DB.' % guest_id if ignore: # Just print a warning message LOG.info(msg) else: LOG.error(msg) obj_desc = "Guest with id: %s" % guest_id raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID=self._module_id) return guest def _check_existence_by_userid(self, userid, ignore=False): guest = self.get_guest_by_userid(userid) if guest is None: msg = 'Guest with userid: %s does not exist in DB.' % userid if ignore: # Just print a warning message LOG.info(msg) else: LOG.error(msg) obj_desc = "Guest with userid: %s" % userid raise exception.SDKObjectNotExistError(obj_desc=obj_desc, modID=self._module_id) return guest def add_guest_migrated(self, userid, meta, net_set, comments=None): # Add guest which is migrated from other host. guest_id = str(uuid.uuid4()) with get_guest_conn() as conn: conn.execute( "INSERT INTO guests VALUES (?, ?, ?, ?, ?)", (guest_id, userid, meta, net_set, comments)) def add_guest(self, userid, meta='', comments=''): # Generate uuid automatically guest_id = str(uuid.uuid4()) net_set = '0' with get_guest_conn() as conn: conn.execute( "INSERT INTO guests VALUES (?, ?, ?, ?, ?)", (guest_id, userid, meta, net_set, comments)) def delete_guest_by_id(self, guest_id): # First check whether the guest exist in db table guest = self._check_existence_by_id(guest_id, ignore=True) if guest is None: return # Update guest if exist with get_guest_conn() as conn: conn.execute( "DELETE FROM guests WHERE id=?", (guest_id,)) def delete_guest_by_userid(self, userid): # First check whether the guest exist in db table guest = self._check_existence_by_userid(userid, ignore=True) if guest is None: return with get_guest_conn() as conn: conn.execute( "DELETE FROM guests WHERE userid=?", (userid,)) def update_guest_by_id(self, uuid, userid=None, meta=None, net_set=None, comments=None): if ((userid is None) and (meta is None) and (net_set is None) and (comments is None)): msg = ("Update guest with id: %s failed, no field " "specified to be updated." % uuid) LOG.error(msg) raise exception.SDKInternalError(msg=msg, modID=self._module_id) # First check whether the guest exist in db table self._check_existence_by_id(uuid) # Start update sql_cmd = "UPDATE guests SET" sql_var = [] if userid is not None: sql_cmd += " userid=?," sql_var.append(userid) if meta is not None: sql_cmd += " metadata=?," sql_var.append(meta) if net_set is not None: sql_cmd += " net_set=?," sql_var.append(net_set) if comments is not None: sql_cmd += " comments=?," sql_var.append(comments) # remove the tailing comma sql_cmd = sql_cmd.strip(',') # Add the id filter sql_cmd += " WHERE id=?" sql_var.append(uuid) with get_guest_conn() as conn: conn.execute(sql_cmd, sql_var) def update_guest_by_userid(self, userid, meta=None, net_set=None, comments=None): userid = userid if (meta is None) and (net_set is None) and (comments is None): msg = ("Update guest with userid: %s failed, no field " "specified to be updated." % userid) LOG.error(msg) raise exception.SDKInternalError(msg=msg, modID=self._module_id) # First check whether the guest exist in db table self._check_existence_by_userid(userid) # Start update sql_cmd = "UPDATE guests SET" sql_var = [] if meta is not None: sql_cmd += " metadata=?," sql_var.append(meta) if net_set is not None: sql_cmd += " net_set=?," sql_var.append(net_set) if comments is not None: new_comments = json.dumps(comments) sql_cmd += " comments=?," sql_var.append(new_comments) # remove the tailing comma sql_cmd = sql_cmd.strip(',') # Add the id filter sql_cmd += " WHERE userid=?" sql_var.append(userid) with get_guest_conn() as conn: conn.execute(sql_cmd, sql_var) def get_guest_list(self): with get_guest_conn() as conn: res = conn.execute("SELECT * FROM guests") guests = res.fetchall() return guests def get_migrated_guest_list(self): with get_guest_conn() as conn: res = conn.execute("SELECT userid FROM guests " "WHERE comments LIKE '%\"migrated\": 1%'") guests = res.fetchall() return guests def get_comments_by_userid(self, userid): """ Get comments record. output should be like: {'k1': 'v1', 'k2': 'v2'}' """ userid = userid with get_guest_conn() as conn: res = conn.execute("SELECT comments FROM guests " "WHERE userid=?", (userid,)) result = res.fetchall() comments = {} if result[0][0]: comments = json.loads(result[0][0]) return comments def get_metadata_by_userid(self, userid): """get metadata record. output should be like: "a=1,b=2,c=3" """ userid = userid with get_guest_conn() as conn: res = conn.execute("SELECT * FROM guests " "WHERE userid=?", (userid,)) guest = res.fetchall() if len(guest) == 1: return guest[0][2] elif len(guest) == 0: LOG.debug("Guest with userid: %s not found from DB!" % userid) return '' else: msg = "Guest with userid: %s have multiple records!" % userid LOG.error(msg) raise exception.SDKInternalError(msg=msg, modID=self._module_id) def transfer_metadata_to_dict(self, meta): """transfer str to dict. output should be like: {'a':1, 'b':2, 'c':3} """ dic = {} arr = meta.strip(' ,').split(',') for i in arr: temp = i.split('=') key = temp[0].strip() value = temp[1].strip() dic[key] = value return dic def get_guest_by_id(self, guest_id): with get_guest_conn() as conn: res = conn.execute("SELECT * FROM guests " "WHERE id=?", (guest_id,)) guest = res.fetchall() # As id is the primary key, the filtered entry number should be 0 or 1 if len(guest) == 1: return guest[0] elif len(guest) == 0: LOG.debug("Guest with id: %s not found from DB!" % guest_id) return None # Code shouldn't come here, just in case return None def get_guest_by_userid(self, userid): userid = userid with get_guest_conn() as conn: res = conn.execute("SELECT * FROM guests " "WHERE userid=?", (userid,)) guest = res.fetchall() # As id is the primary key, the filtered entry number should be 0 or 1 if len(guest) == 1: return guest[0] elif len(guest) == 0: LOG.debug("Guest with userid: %s not found from DB!" % userid) return None # Code shouldn't come here, just in case return None zVMCloudConnector-1.4.1/zvmsdk/networkops.py0000664000175000017510000002752713442676317020653 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import shutil import tarfile from zvmsdk import config from zvmsdk import dist from zvmsdk import log from zvmsdk import smutclient _NetworkOPS = None CONF = config.CONF LOG = log.LOG def get_networkops(): global _NetworkOPS if _NetworkOPS is None: _NetworkOPS = NetworkOPS() return _NetworkOPS class NetworkOPS(object): """Configuration check and manage MAC address API oriented towards SDK driver """ def __init__(self): self._smutclient = smutclient.get_smutclient() self._dist_manager = dist.LinuxDistManager() def create_nic(self, userid, vdev=None, nic_id=None, mac_addr=None, active=False): return self._smutclient.create_nic(userid, vdev=vdev, nic_id=nic_id, mac_addr=mac_addr, active=active) def get_vswitch_list(self): return self._smutclient.get_vswitch_list() def couple_nic_to_vswitch(self, userid, nic_vdev, vswitch_name, active=False): self._smutclient.couple_nic_to_vswitch(userid, nic_vdev, vswitch_name, active=active) def uncouple_nic_from_vswitch(self, userid, nic_vdev, active=False): self._smutclient.uncouple_nic_from_vswitch(userid, nic_vdev, active=active) def add_vswitch(self, name, rdev=None, controller='*', connection='CONNECT', network_type='ETHERNET', router="NONROUTER", vid='UNAWARE', port_type='ACCESS', gvrp='GVRP', queue_mem=8, native_vid=1, persist=True): self._smutclient.add_vswitch(name, rdev=rdev, controller=controller, connection=connection, network_type=network_type, router=router, vid=vid, port_type=port_type, gvrp=gvrp, queue_mem=queue_mem, native_vid=native_vid, persist=persist) def grant_user_to_vswitch(self, vswitch_name, userid): self._smutclient.grant_user_to_vswitch(vswitch_name, userid) def revoke_user_from_vswitch(self, vswitch_name, userid): self._smutclient.revoke_user_from_vswitch(vswitch_name, userid) def set_vswitch_port_vlan_id(self, vswitch_name, userid, vlan_id): self._smutclient.set_vswitch_port_vlan_id(vswitch_name, userid, vlan_id) def set_vswitch(self, vswitch_name, **kwargs): self._smutclient.set_vswitch(vswitch_name, **kwargs) def delete_vswitch(self, vswitch_name, persist=True): self._smutclient.delete_vswitch(vswitch_name, persist) def delete_nic(self, userid, vdev, active=False): self._smutclient.delete_nic(userid, vdev, active=active) def network_configuration(self, userid, os_version, network_info, active=False): network_file_path = self._smutclient.get_guest_temp_path(userid) LOG.debug('Creating folder %s to contain network configuration files' % network_file_path) # check whether network interface has already been set for the guest # if not, means this the first time to set the network interface first = self._smutclient.is_first_network_config(userid) (network_doscript, active_cmds) = self._generate_network_doscript( userid, os_version, network_info, network_file_path, first, active=active) fileClass = "X" try: self._smutclient.punch_file(userid, network_doscript, fileClass) finally: LOG.debug('Removing the folder %s ', network_file_path) shutil.rmtree(network_file_path) # update guest db to mark the network is already set if first: self._smutclient.update_guestdb_with_net_set(userid) # using zvmguestconfigure tool to parse network_doscript if active: self._smutclient.execute_cmd(userid, active_cmds) # Prepare and create network doscript for instance def _generate_network_doscript(self, userid, os_version, network_info, network_file_path, first, active=False): path_contents = [] content_dir = {} files_map = [] # Create network configuration files LOG.debug('Creating network configuration files ' 'for guest %s in the folder %s' % (userid, network_file_path)) linuxdist = self._dist_manager.get_linux_dist(os_version)() files_and_cmds = linuxdist.create_network_configuration_files( network_file_path, network_info, first, active=active) (net_conf_files, net_conf_cmds, clean_cmd, net_enable_cmd) = files_and_cmds # Add network configure files to path_contents if len(net_conf_files) > 0: path_contents.extend(net_conf_files) # restart_cmds = '' # if active: # restart_cmds = linuxdist.restart_network() net_cmd_file = self._create_znetconfig(net_conf_cmds, linuxdist, net_enable_cmd, active=active) # Add znetconfig file to path_contents if len(net_cmd_file) > 0: path_contents.extend(net_cmd_file) for (path, contents) in path_contents: key = "%04i" % len(content_dir) files_map.append({'target_path': path, 'source_file': "%s" % key}) content_dir[key] = contents file_name = os.path.join(network_file_path, key) self._add_file(file_name, contents) self._create_invokeScript(network_file_path, clean_cmd, files_map) network_doscript = self._create_network_doscript(network_file_path) # get command about zvmguestconfigure active_cmds = '' if active: active_cmds = linuxdist.create_active_net_interf_cmd() return network_doscript, active_cmds def _add_file(self, file_name, data): with open(file_name, "w") as f: f.write(data) def _create_znetconfig(self, commands, linuxdist, append_cmd, active=False): LOG.debug('Creating znetconfig file') if active: znet_content = linuxdist.get_simple_znetconfig_contents() else: znet_content = linuxdist.get_znetconfig_contents() net_cmd_file = [] if znet_content: if len(commands) == 0: znetconfig = '\n'.join(('#!/bin/bash', znet_content)) else: znetconfig = '\n'.join(('#!/bin/bash', commands, 'sleep 2', znet_content)) if len(append_cmd) > 0: znetconfig += '\nsleep 2' znetconfig += '\n%s\n' % append_cmd znetconfig += '\nrm -rf /tmp/znetconfig.sh\n' # Create a temp file in instance to execute above commands net_cmd_file.append(('/tmp/znetconfig.sh', znetconfig)) # nosec return net_cmd_file def _create_invokeScript(self, network_file_path, commands, files_map): """invokeScript: Configure zLinux os network invokeScript is included in the network.doscript, it is used to put the network configuration file to the directory where it belongs and call znetconfig to configure the network """ LOG.debug('Creating invokeScript shell in the folder %s' % network_file_path) invokeScript = "invokeScript.sh" conf = "#!/bin/bash \n" command = commands for file in files_map: target_path = file['target_path'] source_file = file['source_file'] # potential risk: whether target_path exist command += 'mv ' + source_file + ' ' + target_path + '\n' command += 'sleep 2\n' command += '/bin/bash /tmp/znetconfig.sh\n' command += 'rm -rf invokeScript.sh\n' scriptfile = os.path.join(network_file_path, invokeScript) with open(scriptfile, "w") as f: f.write(conf) f.write(command) def _create_network_doscript(self, network_file_path): """doscript: contains a invokeScript.sh which will do the special work The network.doscript contains network configuration files and it will be used by zvmguestconfigure to configure zLinux os network when it starts up """ # Generate the tar package for punch LOG.debug('Creating network doscript in the folder %s' % network_file_path) network_doscript = os.path.join(network_file_path, 'network.doscript') tar = tarfile.open(network_doscript, "w") for file in os.listdir(network_file_path): file_name = os.path.join(network_file_path, file) tar.add(file_name, arcname=file) tar.close() return network_doscript def get_nic_info(self, userid=None, nic_id=None, vswitch=None): return self._smutclient.get_nic_info(userid=userid, nic_id=nic_id, vswitch=vswitch) def vswitch_query(self, vswitch_name): return self._smutclient.query_vswitch(vswitch_name) def delete_network_configuration(self, userid, os_version, vdev, active=False): network_file_path = self._smutclient.get_guest_temp_path(userid) linuxdist = self._dist_manager.get_linux_dist(os_version)() file = linuxdist.get_network_configuration_files(vdev) cmd = 'rm -f %s\n' % file cmd += linuxdist.delete_vdev_info(vdev) net_cmd_file = self._create_znetconfig(cmd, linuxdist, '', active=active) del_file = 'DEL%s.sh' % str(vdev).zfill(4) file_name = os.path.join(network_file_path, del_file) file_content = net_cmd_file[0][1] self._add_file(file_name, file_content) fileClass = "X" try: self._smutclient.punch_file(userid, file_name, fileClass) finally: LOG.debug('Removing the folder %s ', network_file_path) shutil.rmtree(network_file_path) if active: active_cmds = linuxdist.create_active_net_interf_cmd() self._smutclient.execute_cmd(userid, active_cmds) def dedicate_OSA(self, userid, OSA_device, vdev=None, active=False): return self._smutclient.dedicate_OSA(userid, OSA_device, vdev=vdev, active=active) zVMCloudConnector-1.4.1/zvmsdk/returncode.py0000664000175000017510000004251513442676317020604 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """ Each error corresponds to a dictionary contains: 'overallRC', 'modID', 'rc', 'rs'. -- 'overallRC' is used to indicate the overall return code, all the errors are classified into group with different overallRC value. -- 'modID' is used to indicate which module this error happens in. The available Module and their modID value to use is listed in the following: ModName ModID ------ -- SMUT 1 GUEST 10 NETWORK 20 VOLUME 30 IMAGE 40 MONITOR 50 SDKSERVER 100 SDKWSGI 120 SDKGENERAL 400 -- 'rc' is used together with rs to indicate the specific error. If 'rs' is enough to indicate the error, 'rc' would be set same to the overallRC. -- 'rs' is used to indicate the specific error. It is defined specificly by each module. Different error inside a module should use different 'rc'/'rs' combination. ------------------------------------------------------------------------------- ErrorCode General Classification ------------------------------------------------------------------------------- ErrorClass overallRC modID rc rs Description SMUT 1-99 1 xx xx Used by SMUT, refer to smutlayer/msgs.py Invalid input 100 SDKGEN 100 1 SDK API parameter number error 100 SDKGEN 100 2 SDK API input type error 100 SDKGEN 100 3 SDK API parameter format error ( value not in expected format or range) Socket Error 101 MODRC 101 x The SDK server or client socket error. Other Module Error 300 MODRC 300 x The module-specific error during SDK API handling that not belongs to other general module-shared error. Invalid API name 400 MODRC 400 x The SDK server received a invalid API name. REST API Req Err 400 MODRC 400 X The SDKWSGI detects an exception Object Not Exist 404 MODRC 404 1 The operated object does not exist, eg, the guest/vswitch/ image/volume. Conflict 409 MODRC 409 x The status of the to-be-updated object is conflict with the update request. Object Deleted 410 MODRC 410 1 The operated object has been deleted and not exist any more. This can be used for some module that support deleted=1 in DB. Internal error 500 MODRC 500 1 The SDK module got unexpected error, eg, typeerror, keyerror, etc. SDK server would take all the exceptions not belonging to SDKBaseexception as InternalError .Such error generally means bug report, SDK code should avoid using this return code. Service Unavailable 503 MODRC 503 1 The SDK REST reject deploy/capture requests because of the concurrent capture/deploy running exceeds the maximum number. Not Implementation 501 MODRC 501 1 The requested SDK function has not been implemented """ # ----------------------------------------------------------------------------- # Detail Module RC definition of each error # ----------------------------------------------------------------------------- ModRCs = { 'smut': 1, 'guest': 10, 'network': 20, 'volume': 30, 'image': 40, 'monitor': 50, 'file': 60, 'sdkserver': 100, 'sdkwsgi': 120, # The 'zvmsdk' is used as the default module if module is not specified # when raising exception 'zvmsdk': 400 } errors = { # Each entry defined here corresponds to a kind of error indicated by the # following list of info: # 1. the dict of 'overallRC', 'rc' # 2. the dict containing all the possible rs and its error message # 3. The general error description. This should be used for doc generation # Invalid input error 'input': [{'overallRC': 100, 'modID': ModRCs['zvmsdk'], 'rc': 100}, {1: ("Invalid API arg count, API: %(api)s, %(expected)d expected" " while %(provided)d provided."), 2: ("Invalid API arg type, API: %(api)s, expected types: " "'%(expected)s', input types: '%(inputtypes)s'"), 3: ("Invalid API arg format, error: %(msg)s"), 4: ("Missing required option: %(msg)s"), }, "Invalid API Input", ], # General Errors for each module, same overallRC = 300 # Guest Operation failed 'guest': [{'overallRC': 300, 'modID': ModRCs['guest'], 'rc': 300}, {1: "Database operation failed, error: %(msg)s", 2: "Failed to add mdisks when creating guest, error: %(msg)s", 3: ("Failed to deploy image to userid: '%(userid)s', " "unpackdiskimage failed with rc: %(unpack_rc)d, " "error: %(err)s"), 4: ("Failed to deploy image to userid: '%(userid)s', " "copy configure drive failed: %(err_info)s"), 5: ("Failed to capture userid %(userid)s to generate image, " "error: %(msg)s"), 6: ("Failed to resize cpus of guest: '%(userid)s', " "error: update cpu definition in user entry failed with " "smut error: '%(err)s'."), 7: ("Failed to live resize cpus of guest: '%(userid)s', " "error: define new cpu to active failed with smut error: " "'%(err)s'."), 8: ("Failed to live resize cpus of guest: '%(userid)s', " "error: rescan cpus to hot-plug new defined cpus failed: " "'%(err)s'."), 9: ("Failed to resize memory of guest: '%(userid)s', " "error: lock user entry failed with " "smut error: '%(err)s'."), 10: ("Failed to resize memory of guest: '%(userid)s', " "error: replace user entry failed with " "smut error: '%(err)s'."), 11: ("Failed to live resize memory of guest: '%(userid)s', " "error: define standby memory failed with " "smut error: '%(err)s'."), }, "Operation on Guest failed" ], # Network Operation failed 'network': [{'overallRC': 300, 'modID': ModRCs['network'], 'rc': 300}, {1: "Database operation failed, error: %(msg)s", 2: "ZVMSDK network error: %(msg)s", 3: ("Failed to couple nic %(nic)s to vswitch %(vswitch)s " "on the active guest system, error: %(couple_err)s, and " "failed to revoke user direct's changes, " "error: %(revoke_err)s "), 4: ("Failed to create nic %(nic)s for %(userid)s on the " "active guest system, error: %(create_err)s, and " "failed to revoke user direct's changes, " "error: %(revoke_err)s "), 5: ("Failed to actively change network setting for user " "%(userid)s, error: %(msg)s") }, "Operation on Network failed" ], # Image Operation failed 'image': [{'overallRC': 300, 'modID': ModRCs['image'], 'rc': 300}, {1: "Database operation failed, error: %(msg)s", 2: "No image schema found for %(schema)s", 3: "Image import error: Failed to calculate the md5sum of the" " image", 4: "Image import error: The md5sum after import is not same as" " source image, it is possible that the image has been " "broken during import", 5: "Image import error: Failed to get the root disk size units" " of the image via hexdump", 6: "Image import error: The header of image does not contain" " built-in disk size units", 7: "Image import error: The image's disk type is not valid." " Currently only FBA or CKD type image is supported", 8: "Image import error: Failed to get the physical size of" " image in bytes", 9: "Import image from http server failed with reason %(err)s", 10: "Image import error: Copying image file from remote" " filesystem failed with error %(err)s", 11: "The specified remote_host %(rh)s format invalid", 12: "Import image from local file system failed with error" " %(err)s", 13: "Image import error: image name %(img)s already exist in " "image database", 14: "Image import error: %(msg)s", 20: "The image record of %(img)s does not exist", 21: "Image Export error: Failed to copy image file to remote " "host with reason: %(msg)s", 22: "Export image to local file system failed: %(err)s", }, "Operation on Image failed" ], # Volume Operation failed 'volume': [{'overallRC': 300, 'modID': ModRCs['volume'], 'rc': 300}, {1: "Database operation failed, error: %(msg)s", 3: "Volume %(vol)s has already been attached on instance " "%(inst)s", 4: "Volume %(vol)s is not attached on instance %(inst)s", }, "Operation on Volume failed" ], # Monitor Operation failed 'monitor': [{'overallRC': 300, 'modID': ModRCs['monitor'], 'rc': 300}, {1: "Database operation failed, error: %(msg)s", }, "Operation on Monitor failed" ], # File Operation failed 'file': [{'overallRC': 300, 'modID': ModRCs['file'], 'rc': 300}, {1: "File import operation failed", 2: "File export operation failed"}, "Operation on file failed" ], # REST API Request error (Only used by sdkwsgi) # 'modID' would be set to ModRC['sdkwsgi'] 'RESTAPI': [{'overallRC': 400, 'modID': ModRCs['sdkwsgi'], 'rc': 400}, {1: "Invalid request", }, "REST API Request error" ], # Object not exist # Used when the operated object does not exist. # 'modID' would be set to each module rc when raise the exception # 'rs' is always 1 'notExist': [{'overallRC': 404, 'modID': None, 'rc': 404}, {1: "%(obj_desc)s does not exist."}, "The operated object does not exist" ], # Conflict Error (The to-be-updated object status conflict) 'conflict': [{'overallRC': 409, 'modID': None, 'rc': 409}, {1: "Guest '%(userid)s' is not in active status.", 2: ("Failed to live resize cpus of guest: '%(userid)s', " "error: current active cpu count: '%(active)i' is " "greater than requested count: '%(req)i'."), 3: ("Failed to resize cpus of guest: '%(userid)s', " "error: maximum number of cpus is not defined in user " "directory."), 4: ("Failed to resize cpus of guest: '%(userid)s', " "error: the requested number of cpus: '%(req)i' exceeds " "the maximum number of cpus allowed: '%(max)i'."), 5: ("Failed to set vswitch %(vsw)s, error: %(msg)s"), 6: ("Failed to create nic %(vdev)s for guest %(userid)s, " "error: %(msg)s"), 7: ("Failed to create nic %(vdev)s for guest %(userid)s, " "error: %(obj)s is locked"), 8: ("Failed to delete nic %(vdev)s for guest %(userid)s, " "error: %(msg)s"), 9: ("Failed to delete nic %(vdev)s for guest %(userid)s, " "error: %(obj)s is locked"), 10: ("Failed to couple nic %(vdev)s of guest %(userid)s " "with vswitch %(vsw)s, error: %(msg)s"), 11: ("Failed to couple nic %(vdev)s of guest %(userid)s " "with vswitch %(vsw)s, error: %(obj)s is locked"), 12: ("Failed to uncouple nic %(vdev)s of guest %(userid)s " "error: %(msg)s"), 13: ("Failed to uncouple nic %(vdev)s of guest %(userid)s " "error: %(obj)s is locked"), 14: ("Failed to dedicate OSA %(osa)s to guest %(userid)s " "error: %(msg)s"), 15: ("Failed to dedicate OSA %(osa)s to guest %(userid)s " "error: %(obj)s is locked"), 16: ("Failed to delete dedicated device from guest " "%(userid)s %(vdev)s, error: %(msg)s"), 17: ("Failed to delete dedicated device from guest " "%(userid)s %(vdev)s, error: %(obj)s is locked"), 18: ("Failed to live resize memory of guest: '%(userid)s', " "error: current active memory size: '%(active)i'm is " "greater than requested size: '%(req)i'm."), 19: ("Failed to resize memory of guest: '%(userid)s', " "error: user definition is not in expected format, " "cann't get the defined/max/reserved storage."), 20: ("Failed to resize memory of guest: '%(userid)s', " "error: the requested memory size: '%(req)im' exceeds " "the maximum memory size defined: '%(max)im'."), }, "The operated object status conflict" ], # Object deleted. # The operated object has been deleted and not exist any more. # This can be used for some module that support deleted=1 in DB. 'deleted': [{'overallRC': 410, 'modID': None, 'rc': 410}, {}, "The operated object is deleted" ], # Internal error # Module Internal error, rc is not defined here, it will be set when raising # exception. when module id is not specified, the 'zvmsdk' module rc will be # used. 'internal': [{'overallRC': 500, 'modID': None, 'rc': 500}, {1: "Unexpected internal error in ZVM SDK, error: %(msg)s"}, "ZVM SDK Internal Error" ], # Service Unavailable # The SDK REST reject deploy/capture requests because of the concurrent # capture/deploy running exceeds the maximum number. 'serviceUnavail': [{'overallRC': 503, 'modID': ModRCs['sdkwsgi'], 'rc': 503}, {1: "Max concurrent deploy/capture requests received, " "request is rejected. %(req)s", }, "z/VM Cloud Connector service is unavailable" ], # Service not support # The requested function has not been implemented in current release, # the 'modID' would be set to each module rc when raise the exception # 'rs' is always 1 'serviceNotSupport': [{'overallRC': 501, 'modID': None, 'rc': 501}, {1: "The requested function: %(func)s has not been " "implemented in current release", }, "z/VM Cloud Connector function not implemented" ], } # smut internal error # This const defines the list of smut errors that should be converted to # internal error in SDK layer. # Each element in the list is a tuple consisting the 'overallRC', 'rc', # list of 'rs' # when the value is 'None', it means always match. SMUT_INTERNAL_ERROR = [(4, 4, range(1, 18)), (2, 2, [99, ]), (25, None, None), (99, 99, [416, 417]) ] zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/0000775000175000017510000000000013442723341017512 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/__init__.py0000664000175000017510000000000013371225174021613 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/schemas/0000775000175000017510000000000013442723341021135 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/schemas/__init__.py0000664000175000017510000000000013371225174023236 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/schemas/volume.py0000664000175000017510000000315513371225174023024 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from zvmsdk.sdkwsgi.validation import parameter_types attach = { 'type': 'object', 'properties': { 'info': { 'type': 'object', 'properties': { 'connection': parameter_types.connection_info, }, 'required': ['connection'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['info'], 'additionalProperties': False, } detach = { 'type': 'object', 'properties': { 'info': { 'type': 'object', 'properties': { 'connection': parameter_types.connection_info, }, 'required': ['connection'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['info'], 'additionalProperties': False, } get_volume_connector = { 'type': 'object', 'properties': { 'userid': parameter_types.userid_list, }, 'additionalProperties': False, } zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/schemas/image.py0000664000175000017510000000413013371225174022571 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from zvmsdk.sdkwsgi.validation import parameter_types create = { 'type': 'object', 'properties': { 'image': { 'type': 'object', 'properties': { 'image_name': parameter_types.name, 'url': parameter_types.url, 'image_meta': parameter_types.image_meta, 'remote_host': parameter_types.remotehost }, 'required': ['image_name', 'url', 'image_meta'], 'additionalProperties': False }, 'additionalProperties': False }, 'required': ['image'], 'additionalProperties': False } export = { 'type': 'object', 'properties': { 'location': { 'type': 'object', 'properties': { 'dest_url': parameter_types.url, 'remote_host': parameter_types.remotehost }, 'required': ['dest_url'], 'additionalProperties': False }, 'additionalProperties': False }, 'required': ['location'], 'additionalProperties': False } query = { 'type': 'object', 'properties': { 'imagename': parameter_types.image_list }, 'additionalProperties': True } diskname = { 'type': 'object', 'properties': { 'disk': parameter_types.disk_pool }, 'additionalProperties': False } diskpool = { 'type': 'object', 'properties': { 'poolname': parameter_types.disk_pool_list }, 'additionalProperties': False } zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/schemas/guest.py0000664000175000017510000001637313375735014022655 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from zvmsdk.sdkwsgi.validation import parameter_types create = { 'type': 'object', 'properties': { 'guest': { 'type': 'object', 'properties': { 'userid': parameter_types.userid, 'vcpus': parameter_types.positive_integer, 'memory': parameter_types.positive_integer, # profile is similar to userid 'user_profile': parameter_types.userid, 'disk_list': parameter_types.disk_list, 'max_cpu': parameter_types.max_cpu, 'max_mem': parameter_types.max_mem, }, 'required': ['userid', 'vcpus', 'memory'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['guest'], 'additionalProperties': False, } live_migrate_vm = { 'type': 'object', 'properties': { 'dest_zcc_userid': parameter_types.userid, 'destination': parameter_types.userid, 'parms': parameter_types.live_migrate_parms, 'operation': parameter_types.name, }, 'required': ['destination', 'operation'], 'additionalProperties': False, } create_nic = { 'type': 'object', 'properties': { 'nic': { 'type': 'object', 'properties': { 'vdev': parameter_types.vdev, 'nic_id': parameter_types.nic_id, 'mac_addr': parameter_types.mac_address, 'active': parameter_types.boolean, }, 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['nic'], 'additionalProperties': False, } create_network_interface = { 'type': 'object', 'properties': { 'interface': { 'type': 'object', 'properties': { 'os_version': parameter_types.os_version, 'guest_networks': parameter_types.network_list, 'active': parameter_types.boolean, }, 'required': ['os_version', 'guest_networks'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['interface'], 'additionalProperties': False, } delete_network_interface = { 'type': 'object', 'properties': { 'interface': { 'type': 'object', 'properties': { 'os_version': parameter_types.os_version, 'vdev': parameter_types.vdev, 'active': parameter_types.boolean, }, 'required': ['os_version', 'vdev'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['interface'], 'additionalProperties': False, } config_minidisks = { 'type': 'object', 'properties': { 'disk_info': { 'type': 'object', 'properties': { 'disk_list': parameter_types.disk_conf, }, 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['disk_info'], 'additionalProperties': False, } create_disks = { 'type': 'object', 'properties': { 'disk_info': { 'type': 'object', 'properties': { 'disk_list': parameter_types.disk_list, }, 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['disk_info'], 'additionalProperties': False, } delete_disks = { 'type': 'object', 'properties': { 'vdev_info': { 'type': 'object', 'properties': { 'vdev_list': parameter_types.vdev_list, }, 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['vdev_info'], 'additionalProperties': False, } nic_couple_uncouple = { 'type': 'object', 'properties': { 'info': { 'type': 'object', 'properties': { 'couple': parameter_types.boolean, 'active': parameter_types.boolean, 'vswitch': parameter_types.vswitch_name, }, # FIXME: vswitch should be required when it's couple 'required': ['couple'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['info'], 'additionalProperties': False, } deploy = { 'type': 'object', 'properties': { 'image': parameter_types.name, 'transportfiles': {'type': ['string']}, 'remotehost': parameter_types.remotehost, 'vdev': parameter_types.vdev, 'hostname': parameter_types.hostname, }, 'required': ['image'], 'additionalProperties': False, } capture = { 'type': 'object', 'properties': { 'image': parameter_types.name, 'capture_type': parameter_types.capture_type, 'compress_level': parameter_types.compress_level, }, 'required': ['image'], 'additionalProperties': False, } resize_cpus = { 'type': 'object', 'properties': { 'cpu_cnt': parameter_types.max_cpu, }, 'required': ['cpu_cnt'], 'additionalProperties': False, } resize_mem = { 'type': 'object', 'properties': { 'size': parameter_types.max_mem, }, 'required': ['size'], 'additionalProperties': False, } userid_list_query = { 'type': 'object', 'properties': { 'userid': parameter_types.userid_list, }, 'additionalProperties': False } register_vm = { 'type': 'object', 'properties': { 'meta': {'type': ['string']}, 'net_set': {'type': ['string']}, }, 'required': ['meta', 'net_set'], 'additionalProperties': False } userid_list_array_query = { 'type': 'object', 'properties': { 'userid': parameter_types.userid_list_array, }, 'additionalProperties': False } nic_DB_info = { 'type': 'object', 'properties': { 'userid': parameter_types.userid, 'nic_id': parameter_types.nic_id, 'vswitch': parameter_types.vswitch_name, }, 'additionalProperties': False, } stop = { 'type': 'object', 'properties': { 'userid': parameter_types.userid, 'timeout': parameter_types.positive_integer, 'poll_interval': parameter_types.positive_integer, }, 'additionalProperties': False, } softstop = { 'type': 'object', 'properties': { 'userid': parameter_types.userid, 'timeout': parameter_types.non_negative_integer, 'poll_interval': parameter_types.non_negative_integer, }, 'additionalProperties': False, } zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/schemas/vswitch.py0000664000175000017510000000444013371225174023202 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from zvmsdk.sdkwsgi.validation import parameter_types create = { 'type': 'object', 'properties': { 'vswitch': { 'type': 'object', 'properties': { 'name': parameter_types.vswitch_name, 'rdev': parameter_types.rdev_list, # FIXME: controller has its own conventions 'controller': parameter_types.controller, 'persist': parameter_types.boolean, 'connection': parameter_types.connection_type, 'queue_mem': { 'type': ['integer'], 'minimum': 1, 'maximum': 8, }, 'router': parameter_types.router_type, 'network_type': parameter_types.network_type, 'vid': parameter_types.vid_type, 'port_type': parameter_types.port_type, 'gvrp': parameter_types.gvrp_type, 'native_vid': parameter_types.native_vid_type, }, 'required': ['name'], 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['vswitch'], 'additionalProperties': False, } update = { 'type': 'object', 'properties': { 'vswitch': { 'type': 'object', 'properties': { 'grant_userid': parameter_types.userid, 'user_vlan_id': parameter_types.user_vlan_id, 'revoke_userid': parameter_types.userid, }, 'additionalProperties': False, }, 'additionalProperties': False, }, 'required': ['vswitch'], 'additionalProperties': False, } zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/handler.py0000664000175000017510000001435713442676317021525 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import routes import webob from zvmsdk import exception from zvmsdk import log from zvmsdk.sdkwsgi import util from zvmsdk.sdkwsgi.handlers import file from zvmsdk.sdkwsgi.handlers import guest from zvmsdk.sdkwsgi.handlers import host from zvmsdk.sdkwsgi.handlers import image from zvmsdk.sdkwsgi.handlers import tokens from zvmsdk.sdkwsgi.handlers import version from zvmsdk.sdkwsgi.handlers import volume from zvmsdk.sdkwsgi.handlers import vswitch LOG = log.LOG # This is the route of zvm sdk REST API, in order to add or modify # you need add code in handlers/ folder to handle the request ROUTE_LIST = ( ('/', { 'GET': version.version, }), ('/guests', { 'POST': guest.guest_create, 'GET': guest.guest_list, }), ('/guests/stats', { 'GET': guest.guest_get_stats }), ('/guests/interfacestats', { 'GET': guest.guest_get_interface_stats }), ('/guests/nics', { 'GET': guest.guests_get_nic_info }), ('/guests/volumes', { 'POST': volume.volume_attach, 'DELETE': volume.volume_detach, }), ('/volumes/conn/{userid}', { 'GET': volume.get_volume_connector, }), ('/guests/{userid}', { 'DELETE': guest.guest_delete, 'GET': guest.guest_get, }), ('/guests/{userid}/action', { 'POST': guest.guest_action, }), ('/guests/{userid}/info', { 'GET': guest.guest_get_info, }), ('/guests/{userid}/nic', { 'POST': guest.guest_create_nic, }), ('/guests/{userid}/nic/{vdev}', { 'DELETE': guest.guest_delete_nic, 'PUT': guest.guest_couple_uncouple_nic, }), ('/guests/{userid}/interface', { 'POST': guest.guest_create_network_interface, 'DELETE': guest.guest_delete_network_interface, }), ('/guests/{userid}/power_state', { 'GET': guest.guest_get_power_state, }), ('/guests/{userid}/disks', { 'POST': guest.guest_create_disks, 'DELETE': guest.guest_delete_disks, 'PUT': guest.guest_config_disks, }), ('/host', { 'GET': host.host_get_info, }), ('/host/diskpool', { 'GET': host.host_get_disk_info, }), ('/images', { 'POST': image.image_create, 'GET': image.image_query }), ('/images/{name}', { 'DELETE': image.image_delete, 'PUT': image.image_export, }), ('/images/{name}/root_disk_size', { 'GET': image.image_get_root_disk_size, }), ('/files', { 'PUT': file.file_import, 'POST': file.file_export, }), ('/token', { 'POST': tokens.create, }), ('/vswitches', { 'GET': vswitch.vswitch_list, 'POST': vswitch.vswitch_create, }), ('/vswitches/{name}', { 'GET': vswitch.vswitch_query, 'DELETE': vswitch.vswitch_delete, 'PUT': vswitch.vswitch_update, }), ) def dispatch(environ, start_response, mapper): """Find a matching route for the current request. :raises: 404(not found) if no match request 405(method not allowed) if route exist but method not provided. """ result = mapper.match(environ=environ) if result is None: info = environ.get('PATH_INFO', '') LOG.debug('The route for %s can not be found', info) raise webob.exc.HTTPNotFound( json_formatter=util.json_error_formatter) handler = result.pop('action') environ['wsgiorg.routing_args'] = ((), result) return handler(environ, start_response) def handle_not_allowed(environ, start_response): """Return a 405 response when method is not allowed. If _methods are in routing_args, send an allow header listing the methods that are possible on the provided URL. """ _methods = util.wsgi_path_item(environ, '_methods') headers = {} if _methods: headers['allow'] = str(_methods) raise webob.exc.HTTPMethodNotAllowed( ('The method specified is not allowed for this resource.'), headers=headers, json_formatter=util.json_error_formatter) def make_map(declarations): """Process route declarations to create a Route Mapper.""" mapper = routes.Mapper() for route, methods in ROUTE_LIST: allowed_methods = [] for method, func in methods.items(): mapper.connect(route, action=func, conditions=dict(method=[method])) allowed_methods.append(method) allowed_methods = ', '.join(allowed_methods) mapper.connect(route, action=handle_not_allowed, _methods=allowed_methods) return mapper class SdkHandler(object): """Serve zvm sdk request Dispatch to handlers defined in ROUTE_LIST. """ def __init__(self, **local_config): self._map = make_map(ROUTE_LIST) def __call__(self, environ, start_response): clen = environ.get('CONTENT_LENGTH') try: if clen and (int(clen) > 0) and not environ.get('CONTENT_TYPE'): msg = 'content-type header required when content-length > 0' LOG.debug(msg) raise webob.exc.HTTPBadRequest(msg, json_formatter=util.json_error_formatter) except ValueError as exc: msg = 'content-length header must be an integer' LOG.debug(msg) raise webob.exc.HTTPBadRequest(msg, json_formatter=util.json_error_formatter) try: return dispatch(environ, start_response, self._map) except exception.NotFound as exc: raise webob.exc.HTTPNotFound( exc, json_formatter=util.json_error_formatter) except Exception as exc: raise zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/deploy.py0000664000175000017510000001127413371225174021367 0ustar ruirui00000000000000# 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. """Deployment handling for sdk API.""" import json import six import sys import traceback import webob from zvmsdk import log from zvmsdk.sdkwsgi import handler from zvmsdk.sdkwsgi import requestlog from zvmsdk.sdkwsgi import util LOG = log.LOG NAME = "zvm-cloud-connector" def _find_fault(clazz, encountered=None): if not encountered: encountered = [] for subclass in clazz.__subclasses__(): if subclass not in encountered: encountered.append(subclass) for subsubclass in _find_fault(subclass, encountered): yield subsubclass yield subclass class Fault(webob.exc.HTTPException): def __init__(self, exception): self.wrapped_exc = exception for key, value in list(self.wrapped_exc.headers.items()): self.wrapped_exc.headers[key] = str(value) self.status_int = exception.status_int @webob.dec.wsgify() def __call__(self, req): code = self.wrapped_exc.status_int explanation = self.wrapped_exc.explanation LOG.debug("Returning %(code)s to user: %(explanation)s", {'code': code, 'explanation': explanation}) fault_data = { 'overallRC': 400, 'rc': 400, 'rs': code, 'modID': util.SDKWSGI_MODID, 'output': '', 'errmsg': explanation} if code == 413 or code == 429: retry = self.wrapped_exc.headers.get('Retry-After', None) if retry: fault_data['retryAfter'] = retry self.wrapped_exc.content_type = 'application/json' self.wrapped_exc.charset = 'UTF-8' self.wrapped_exc.text = six.text_type(json.dumps(fault_data)) return self.wrapped_exc def __str__(self): return self.wrapped_exc.__str__() class FaultWrapper(object): """Calls down the middleware stack, making exceptions into faults.""" _status_to_type = {} @staticmethod def status_to_type(status): if not FaultWrapper._status_to_type: for clazz in _find_fault(webob.exc.HTTPError): FaultWrapper._status_to_type[clazz.code] = clazz return FaultWrapper._status_to_type.get( status, webob.exc.HTTPInternalServerError)() def __init__(self, application): self.application = application def _error(self, inner, req): exc_info = traceback.extract_tb(sys.exc_info()[2])[-1] LOG.info('Got unhandled exception: %s', exc_info) safe = getattr(inner, 'safe', False) headers = getattr(inner, 'headers', None) status = getattr(inner, 'code', 500) if status is None: status = 500 outer = self.status_to_type(status) if headers: outer.headers = headers if safe: outer.explanation = '%s: %s' % (inner.__class__.__name__, inner.message) return Fault(outer) @webob.dec.wsgify() def __call__(self, req): try: return req.get_response(self.application) except Exception as ex: return self._error(ex, req) class HeaderControl(object): def __init__(self, application): self.application = application @webob.dec.wsgify def __call__(self, req): response = req.get_response(self.application) response.headers.add('cache-control', 'no-cache') return response def deploy(project_name): """Assemble the middleware pipeline""" request_log = requestlog.RequestLog header_addon = HeaderControl fault_wrapper = FaultWrapper application = handler.SdkHandler() # currently we have 3 middleware for middleware in (header_addon, fault_wrapper, request_log, ): if middleware: application = middleware(application) return application def loadapp(project_name=NAME): application = deploy(project_name) return application def init_application(): # build and return WSGI app return loadapp() zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/zvmsdk-wsgi0000664000175000017510000000373213371225174021731 0ustar ruirui00000000000000#!/usr/bin/python # PBR Generated from u'wsgi_scripts' import threading from zvmsdk import config if __name__ == "__main__": import argparse import socket import sys import wsgiref.simple_server as wss config.load_config() from zvmsdk import log log.setup_log() from zvmsdk.sdkwsgi.deploy import init_application my_ip = socket.gethostbyname(socket.gethostname()) parser = argparse.ArgumentParser( description=init_application.__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter, usage='%(prog)s [-h] [--port PORT] -- [passed options]') parser.add_argument('--port', '-p', type=int, default=8888, help='TCP port to listen on') parser.add_argument('args', nargs=argparse.REMAINDER, metavar='-- [passed options]', help="'--' is the separator of the arguments used " "to start the WSGI server and the arguments passed " "to the WSGI application.") args = parser.parse_args() if args.args: if args.args[0] == '--': args.args.pop(0) else: parser.error("unrecognized arguments: %s" % ' '.join(args.args)) sys.argv[1:] = args.args server = wss.make_server('', args.port, init_application()) print("*" * 80) print("STARTING test server zvmsdk.sdkwsgi.wsgi.init_application") url = "http://%s:%d/" % (my_ip, server.server_port) print("Available at %s" % url) print("DANGER! For testing only, do not use in production") print("*" * 80) sys.stdout.flush() server.serve_forever() else: application = None app_lock = threading.Lock() with app_lock: if application is None: config.load_config() from zvmsdk import log log.setup_log() from zvmsdk.sdkwsgi.deploy import init_application application = init_application() zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/util.py0000664000175000017510000001711113442676317021054 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import six import webob from webob.dec import wsgify from zvmsdk import log LOG = log.LOG SDKWSGI_MODID = 120 def extract_json(body): try: LOG.debug('Decoding body: %s', body) # This function actually is received from upper layer through # socket, so it's bytes in py3 if isinstance(body, bytes): body = bytes.decode(body) data = json.loads(body) except ValueError as exc: msg = ('Malformed JSON: %(error)s') % {'error': exc} LOG.debug(msg) raise webob.exc.HTTPBadRequest(msg, json_formatter=json_error_formatter) return data def json_error_formatter(body, status, title, environ): """A json_formatter for webob exceptions.""" body = webob.exc.strip_tags(body) status_code = int(status.split(None, 1)[0]) error_dict = { 'status': status_code, 'title': title, 'detail': body } return {'errors': [error_dict]} def wsgi_path_item(environ, name): """Extract the value of a named field in a URL. Return None if the name is not present or there are no path items. """ try: return environ['wsgiorg.routing_args'][1][name] except (KeyError, IndexError): return None TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') def bool_from_string(subject, strict=False, default=False): if isinstance(subject, bool): return subject if not isinstance(subject, six.string_types): subject = six.text_type(subject) lowered = subject.strip().lower() if lowered in TRUE_STRINGS: return True elif lowered in FALSE_STRINGS: return False elif strict: acceptable = ', '.join( "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) msg = ("Unrecognized value '%(val)s', acceptable values are:" " %(acceptable)s") % {'val': subject, 'acceptable': acceptable} raise ValueError(msg) else: return default def get_request_uri(environ): name = environ.get('SCRIPT_NAME', '') info = environ.get('PATH_INFO', '') req_uri = name + info if environ.get('QUERY_STRING'): req_uri += '?' + environ['QUERY_STRING'] return req_uri def get_http_code_from_sdk_return(msg, additional_handler=None, default=200): LOG.debug("Get msg to handle: %s", msg) if 'overallRC' in msg: ret = msg['overallRC'] if ret != 0: # same definition to sdk layer if ret in [400, 404, 409, 501, 503]: return ret # 100 mean validation error in sdk layer and # lead to a 400 badrequest if ret in [100]: return 400 # Add a special handle for smut return if additional_handler: ret = additional_handler(msg) if ret: return ret # ok, we reach here because can't handle it LOG.info("The msg <%s> lead to return internal error", msg) return 500 else: # return default code return default def handle_not_found(msg): if 'overallRC' in msg and 'rc' in msg and 'rs' in msg: # overall rc: 8, rc: 212, rs: 40 means vswitch not exist if (msg['overallRC'] == 8 and msg['rc'] == 212 and msg['rs'] == 40): LOG.debug('vswitch does not exist, change ret to 404') return 404 # overall rc: 4, rc: 5, rs: 402 means vswitch not exist if (msg['overallRC'] == 4 and msg['rc'] == 5 and msg['rs'] == 402): LOG.debug('disk pool not exist, change ret to 404') return 404 # overall rc: 300, rc: 300, rs: 20 means image not exist if (msg['overallRC'] == 300 and msg['rc'] == 300 and msg['rs'] == 20): LOG.debug('image not exist, change ret to 404') return 404 # overall rc: 8, rc: 400, rs: 4 means guest not exist if (msg['overallRC'] == 8 and msg['rc'] == 400 and msg['rs'] == 4): LOG.debug('guest not exist, change ret to 404') return 404 # overall rc: 8, rc: 200, rs: 4 means guest not exist if (msg['overallRC'] == 8 and msg['rc'] == 200 and msg['rs'] == 4): LOG.debug('guest not exist, change ret to 404') return 404 # overall rc: 300, rc:300, rs: 3, error message contains # "not linked; not in CP directory" means target vdev not exist if (msg['overallRC'] == 300 and msg['rc'] == 300 and msg['rs'] == 3 and 'not linked; not in CP directory' in msg['errmsg']): LOG.debug('deploy target vdev not exist,' ' change ret to 404') return 404 return 0 def handle_already_exists(msg): if 'overallRC' in msg and 'rc' in msg and 'rs' in msg: # overall rc: 8, rc: 212, rs: 36 means vswitch already exist if (msg['overallRC'] == 8 and msg['rc'] == 212 and msg['rs'] == 36): LOG.debug('vswitch already exist, change ret to 409') return 409 # overall rc: 300, rc: 300, rc: 13 means image already exist if (msg['overallRC'] == 300 and msg['rc'] == 300 and msg['rs'] == 13): LOG.debug('image already exist, change ret to 409') return 409 # overall rc: 8, rc: 400, rs: 8 means guest already exist if (msg['overallRC'] == 8 and msg['rc'] == 400 and msg['rs'] == 8): LOG.debug('guest already exist, change ret to 409') return 409 # not handle it well, go to default return 0 def handle_conflict_state(msg): if 'overallRC' in msg and 'rc' in msg and 'rs' in msg: # overall rc: 8, rc: 212, rs: 36 means vswitch already exist if (msg['overallRC'] == 300 and msg['rc'] == 300 and msg['rs'] == 5): LOG.debug('guest power off state, change ret to 409') return 409 return 0 def handle_not_found_and_conflict(msg): err = handle_not_found(msg) if err == 0: return handle_conflict_state(msg) return err class SdkWsgify(wsgify): def call_func(self, req, *args, **kwargs): """Add json_error_formatter to any webob HTTPExceptions.""" try: return super(SdkWsgify, self).call_func(req, *args, **kwargs) except webob.exc.HTTPException as exc: msg = ('encounter %(error)s error') % {'error': exc} LOG.debug(msg) exc.json_formatter = json_error_formatter code = exc.status_int explanation = six.text_type(exc) fault_data = { 'overallRC': 400, 'rc': 400, 'rs': code, 'modID': SDKWSGI_MODID, 'output': '', 'errmsg': explanation} exc.text = six.text_type(json.dumps(fault_data)) raise exc zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/validation/0000775000175000017510000000000013442723341021644 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/validation/__init__.py0000664000175000017510000001242513371225174023763 0ustar ruirui00000000000000# Copyright 2017 IBM Corp. # Copyright 2013 NEC Corporation. # All rights reserved. # # 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 functools import re import jsonschema from jsonschema import exceptions as jsonschema_exc import six from zvmsdk import exception def _schema_validation_helper(schema, target, args, kwargs, is_body=True): schema_validator = _SchemaValidator( schema, is_body=is_body) schema_validator.validate(target) def schema(request_body_schema): def add_validator(func): @functools.wraps(func) def wrapper(*args, **kwargs): _schema_validation_helper(request_body_schema, kwargs['body'], args, kwargs) return func(*args, **kwargs) return wrapper return add_validator class FormatChecker(jsonschema.FormatChecker): def check(self, instance, format): if format not in self.checkers: return func, raises = self.checkers[format] result, cause = None, None try: result = func(instance) except raises as e: cause = e if not result: msg = "%r is not a %r" % (instance, format) raise jsonschema_exc.FormatError(msg, cause=cause) class _SchemaValidator(object): validator = None validator_org = jsonschema.Draft4Validator def __init__(self, schema, relax_additional_properties=False, is_body=True): self.is_body = is_body validators = { 'dummy': self._dummy } validator_cls = jsonschema.validators.extend(self.validator_org, validators) format_checker = FormatChecker() self.validator = validator_cls(schema, format_checker=format_checker) def _dummy(self, validator, minimum, instance, schema): pass def validate(self, *args, **kwargs): try: self.validator.validate(*args, **kwargs) except jsonschema.ValidationError as ex: if isinstance(ex.cause, exception.InvalidName): detail = ex.cause.format_message() elif len(ex.path) > 0: if self.is_body: detail = ("Invalid input for field/attribute %(path)s. " "Value: %(value)s. %(message)s") else: detail = ("Invalid input for query parameters %(path)s. " "Value: %(value)s. %(message)s") detail = detail % { 'path': ex.path.pop(), 'value': ex.instance, 'message': ex.message } else: detail = ex.message raise exception.ValidationError(detail=detail) except TypeError as ex: detail = six.text_type(ex) raise exception.ValidationError(detail=detail) def _remove_unexpected_query_parameters(schema, req): """Remove unexpected properties from the req.GET.""" additional_properties = schema.get('addtionalProperties', True) if additional_properties: pattern_regexes = [] patterns = schema.get('patternProperties', None) if patterns: for regex in patterns: pattern_regexes.append(re.compile(regex)) for param in set(req.GET.keys()): if param not in schema['properties'].keys(): if not (list(regex for regex in pattern_regexes if regex.match(param))): del req.GET[param] def query_schema(query_params_schema, min_version=None, max_version=None): """Register a schema to validate request query parameters.""" def add_validator(func): @functools.wraps(func) def wrapper(*args, **kwargs): if 'req' in kwargs: req = kwargs['req'] else: req = args[1] if req.environ['wsgiorg.routing_args'][1]: if _schema_validation_helper(query_params_schema, req.environ['wsgiorg.routing_args'][1], args, kwargs, is_body=False): _remove_unexpected_query_parameters(query_params_schema, req) else: if _schema_validation_helper(query_params_schema, req.GET.dict_of_lists(), args, kwargs, is_body=False): _remove_unexpected_query_parameters(query_params_schema, req) return func(*args, **kwargs) return wrapper return add_validator zVMCloudConnector-1.4.1/zvmsdk/sdkwsgi/validation/parameter_types.py0000664000175000017510000002514413375735014025435 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # Copyright 2013 NEC Corporation. # All rights reserved. # # 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 re import unicodedata import six def single_param(schema): ret = multi_params(schema) ret['maxItems'] = 1 return ret def multi_params(schema): return {'type': 'array', 'items': schema} class ValidationRegex(object): def __init__(self, regex, reason): self.regex = regex self.reason = reason def _is_printable(char): category = unicodedata.category(char) return (not category.startswith("C") and (not category.startswith("Z") or category == "Zs")) def _get_all_chars(): for i in range(0xFFFF): yield six.unichr(i) def _build_regex_range(ws=True, invert=False, exclude=None): if exclude is None: exclude = [] regex = "" in_range = False last = None last_added = None def valid_char(char): if char in exclude: result = False elif ws: result = _is_printable(char) else: # Zs is the unicode class for space characters, of which # there are about 10 in this range. result = (_is_printable(char) and unicodedata.category(char) != "Zs") if invert is True: return not result return result # iterate through the entire character range. in_ for c in _get_all_chars(): if valid_char(c): if not in_range: regex += re.escape(c) last_added = c in_range = True else: if in_range and last != last_added: regex += "-" + re.escape(last) in_range = False last = c else: if in_range: regex += "-" + re.escape(c) return regex valid_name_regex_base = '^(?![%s])[%s]*(? expiration) LOG.debug( "timeout is %(timeout)s, expiration is %(expiration)s, \ time_start is %(time_start)s" % {"timeout": timeout, "expiration": expiration, "time_start": time_start}) try: f(*args, **kwargs) except exceptions: retry = not expired if retry: LOG.debug("Will re-try %(fname)s in %(itv)d seconds" % {'fname': f.__name__, 'itv': sleep}) time.sleep(sleep) sleep = min(sleep + inc_sleep, max_sleep) else: LOG.debug("Looping call %s timeout" % f.__name__) continue retry = False def convert_to_mb(s): """Convert memory size from GB to MB.""" s = s.upper() try: if s.endswith('G'): return float(s[:-1].strip()) * 1024 elif s.endswith('T'): return float(s[:-1].strip()) * 1024 * 1024 else: return float(s[:-1].strip()) except (IndexError, ValueError, KeyError, TypeError): errmsg = ("Invalid memory format: %s") % s raise exception.SDKInternalError(msg=errmsg) class PathUtils(object): def clean_temp_folder(self, tmp_folder): if os.path.isdir(tmp_folder): LOG.debug('Removing existing folder %s ', tmp_folder) shutil.rmtree(tmp_folder) def _get_guest_path(self): return os.path.join(constants.SDK_DATA_PATH, 'guests') def mkdir_if_not_exist(self, folder): if not os.path.exists(folder): LOG.debug("Creating the guest path %s", folder) os.makedirs(folder) # This is for persistent info for guests # by default it's /var/lib/zvmsdk/guests/xxxx def remove_guest_path(self, userid): guest_folder = os.path.join(self._get_guest_path(), userid) try: shutil.rmtree(guest_folder) except Exception: # Ignore any exception for delete temp folder pass def get_guest_temp_path(self, userid): tmp_inst_dir = tempfile.mkdtemp(prefix=userid, dir='/tmp') return tmp_inst_dir def get_guest_path(self, userid): guest_folder = os.path.join(self._get_guest_path(), userid) self.mkdir_if_not_exist(guest_folder) return guest_folder def get_console_log_path(self, userid): return os.path.join(self.get_guest_path(userid), "console.log") def create_import_image_repository(self, image_osdistro, type, image_name): zvmsdk_image_import_repo = os.path.join( CONF.image.sdk_image_repository, type, image_osdistro, image_name) if not os.path.exists(zvmsdk_image_import_repo): LOG.debug('Creating image repository %s for image import', zvmsdk_image_import_repo) os.makedirs(zvmsdk_image_import_repo) return zvmsdk_image_import_repo def create_file_repository(self, file_type): zvmsdk_file_repo = os.path.join(CONF.file.file_repository, file_type) if not os.path.exists(zvmsdk_file_repo): LOG.debug('Creating file repository %s for file transfer', zvmsdk_file_repo) os.makedirs(zvmsdk_file_repo) return zvmsdk_file_repo def to_utf8(text): if isinstance(text, bytes): return text elif isinstance(text, six.text_type): return text.encode() else: raise TypeError("bytes or Unicode expected, got %s" % type(text).__name__) def valid_userid(userid): if not isinstance(userid, six.string_types): return False if ((userid == '') or (userid.find(' ') != -1)): return False if len(userid) > 8: return False return True def valid_mac_addr(addr): ''' Validates a mac address''' if not isinstance(addr, six.string_types): return False valid = re.compile(r''' (^([0-9A-F]{2}[:]){5}([0-9A-F]{2})$) ''', re.VERBOSE | re.IGNORECASE) return valid.match(addr) is not None def valid_cidr(cidr): if not isinstance(cidr, six.string_types): return False try: netaddr.IPNetwork(cidr) except netaddr.AddrFormatError: return False if '/' not in cidr: return False if re.search('\s', cidr): return False return True def last_bytes(file_like_object, num): try: file_like_object.seek(-num, os.SEEK_END) except IOError as e: # seek() fails with EINVAL when trying to go before the start of the # file. It means that num is larger than the file size, so just # go to the start. if e.errno == errno.EINVAL: file_like_object.seek(0, os.SEEK_SET) else: raise remaining = file_like_object.tell() return (file_like_object.read(), remaining) def check_input_types(*types, **validkeys): """This is a function decorator to check all input parameters given to decorated function are in expected types. The checks can be skipped by specify skip_input_checks=True in decorated function. :param tuple types: expected types of input parameters to the decorated function :param validkeys: valid keywords(str) in a list. e.g. validkeys=['key1', 'key2'] """ def decorator(function): @functools.wraps(function) def wrap_func(*args, **kwargs): if args[0]._skip_input_check: # skip input check return function(*args, **kwargs) # drop class object self inputs = args[1:] if (len(inputs) > len(types)): msg = ("Too many parameters provided: %(specified)d specified," "%(expected)d expected." % {'specified': len(inputs), 'expected': len(types)}) LOG.info(msg) raise exception.SDKInvalidInputNumber(function.__name__, len(types), len(inputs)) argtypes = tuple(map(type, inputs)) match_types = types[0:len(argtypes)] invalid_type = False invalid_userid_idx = -1 for idx in range(len(argtypes)): _mtypes = match_types[idx] if not isinstance(_mtypes, tuple): _mtypes = (_mtypes,) argtype = argtypes[idx] if constants._TUSERID in _mtypes: userid_type = True for _tmtype in _mtypes: if ((argtype == _tmtype) and (_tmtype != constants._TUSERID)): userid_type = False if (userid_type and (not valid_userid(inputs[idx]))): invalid_userid_idx = idx break elif argtype not in _mtypes: invalid_type = True break if invalid_userid_idx != -1: msg = ("Invalid string value found at the #%d parameter, " "length should be less or equal to 8 and should not be " "null or contain spaces." % (invalid_userid_idx + 1)) LOG.info(msg) raise exception.SDKInvalidInputFormat(msg=msg) if invalid_type: msg = ("Invalid input types: %(argtypes)s; " "Expected types: %(types)s" % {'argtypes': str(argtypes), 'types': str(types)}) LOG.info(msg) raise exception.SDKInvalidInputTypes(function.__name__, str(types), str(argtypes)) valid_keys = validkeys.get('valid_keys') if valid_keys: for k in kwargs.keys(): if k not in valid_keys: msg = ("Invalid keyword: %(key)s; " "Expected keywords are: %(keys)s" % {'key': k, 'keys': str(valid_keys)}) LOG.info(msg) raise exception.SDKInvalidInputFormat(msg=msg) return function(*args, **kwargs) return wrap_func return decorator def import_class(import_str): """Returns a class from a string including module and class.""" mod_str, _sep, class_str = import_str.rpartition('.') __import__(mod_str) try: return getattr(sys.modules[mod_str], class_str) except AttributeError: raise ImportError('Class %s cannot be found (%s)' % (class_str, traceback.format_exception(*sys.exc_info()))) def import_object(import_str, *args, **kwargs): """Import a class and return an instance of it.""" return import_class(import_str)(*args, **kwargs) @contextlib.contextmanager def expect_invalid_resp_data(data=''): """Catch exceptions when using zvm client response data.""" try: yield except (ValueError, TypeError, IndexError, AttributeError, KeyError) as err: msg = ('Invalid smut response data: %s. Error: %s' % (data, six.text_type(err))) LOG.error(msg) raise exception.SDKInternalError(msg=msg) def wrap_invalid_resp_data_error(function): """Catch exceptions when using zvm client response data.""" @functools.wraps(function) def decorated_function(*arg, **kwargs): try: return function(*arg, **kwargs) except (ValueError, TypeError, IndexError, AttributeError, KeyError) as err: msg = ('Invalid smut response data. Error: %s' % six.text_type(err)) LOG.error(msg) raise exception.SDKInternalError(msg=msg) return decorated_function @contextlib.contextmanager def expect_and_reraise_internal_error(modID='SDK'): """Catch all kinds of zvm client request failure and reraise. modID: the moduleID that the internal error happens in. """ try: yield except exception.SDKInternalError as err: msg = err.format_message() raise exception.SDKInternalError(msg, modID=modID) @contextlib.contextmanager def log_and_reraise_sdkbase_error(action): """Catch SDK base exception and print error log before reraise exception. msg: the error message to be logged. """ try: yield except exception.SDKBaseException: msg = "Failed to " + action + "." LOG.error(msg) raise @contextlib.contextmanager def log_and_reraise_smut_request_failed(action=None): """Catch SDK base exception and print error log before reraise exception. msg: the error message to be logged. """ try: yield except exception.SDKSMUTRequestFailed as err: msg = '' if action is not None: msg = "Failed to %s. " % action msg += "SMUT error: %s" % err.format_message() LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) @contextlib.contextmanager def ignore_errors(): """Only execute the clauses and ignore the results""" try: yield except Exception as err: msg = 'ignore an error:%s' % err.format_message() LOG.debug(msg) pass def get_smut_userid(): """Get the userid of smut server""" cmd = ["sudo", "/sbin/vmcp", "query userid"] try: userid = subprocess.check_output(cmd, close_fds=True, stderr=subprocess.STDOUT) userid = bytes.decode(userid) userid = userid.split()[0] return userid except Exception as err: msg = ("Could not find the userid of the smut server: %s") % err raise exception.SDKInternalError(msg=msg) def get_namelist(): """Generate namelist. Either through set CONF.zvm.namelist, or by generate based on smut userid. """ if CONF.zvm.namelist is not None: # namelist length limit should be 64, but there's bug limit to 8 # will change the limit to 8 once the bug fixed if len(CONF.zvm.namelist) <= 8: return CONF.zvm.namelist # return ''.join(('NL', get_smut_userid().rjust(6, '0')[-6:])) # py3 compatible changes userid = get_smut_userid() return 'NL' + userid.rjust(6, '0')[-6:] def generate_iucv_authfile(fn, client): """Generate the iucv_authorized_userid file""" lines = ['#!/bin/bash\n', 'echo -n %s > /etc/iucv_authorized_userid\n' % client] with open(fn, 'w') as f: f.writelines(lines) @wrap_invalid_resp_data_error def translate_response_to_dict(rawdata, dirt): """Translate SMUT response to a python dictionary. SMUT response example: keyword1: value1\n keyword2: value2\n ... keywordn: valuen\n Will return a python dictionary: {keyword1: value1, keyword2: value2, ... keywordn: valuen,} """ data_list = rawdata.split("\n") data = {} for ls in data_list: for k in list(dirt.keys()): if ls.__contains__(dirt[k]): data[k] = ls[(ls.find(dirt[k]) + len(dirt[k])):].strip() break if data == {}: msg = ("Invalid smut response data. Error: No value matched with " "keywords. Raw Data: %(raw)s; Keywords: %(kws)s" % {'raw': rawdata, 'kws': str(dirt)}) raise exception.SDKInternalError(msg=msg) return data def make_dummy_image(image_path, d_type='CKD'): if d_type not in ('CKD', 'FBA'): d_type = 'CKD' d_unit = 'CYL' if d_type == 'FBA': d_unit = 'BLK' header = ("z/VM %(type)s Disk Image: 0 %(unit)s" % {'type': d_type, 'unit': d_unit}) header = (' '.join((header, 'HLen: 0055', 'GZIP: 0'))) with open(image_path, 'wb') as f: f.write(header.encode()) @contextlib.contextmanager def acquire_lock(lock): """ lock wrapper """ lock.acquire() try: yield finally: lock.release() def check_userid_exist(userid): cmd = 'sudo vmcp q %s' % userid rc, output = execute(cmd) if re.search('(^HCP\w\w\w003E)', output): # userid not exist return False return True def check_userid_on_others(userid): try: check_userid_exist(userid) cmd = 'sudo vmcp q %s' % userid rc, output = execute(cmd) if re.search(' - SSI', output): return True return False except Exception as err: msg = ("Could not find the userid: %s") % err raise exception.SDKInternalError(msg=msg) zVMCloudConnector-1.4.1/zvmsdk/vmops.py0000775000175000017510000004201313442676317017572 0ustar ruirui00000000000000# 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. import os import six from zvmsdk import config from zvmsdk import dist from zvmsdk import exception from zvmsdk import log from zvmsdk import smutclient from zvmsdk import database from zvmsdk import utils as zvmutils _VMOPS = None CONF = config.CONF LOG = log.LOG def get_vmops(): global _VMOPS if _VMOPS is None: _VMOPS = VMOps() return _VMOPS class VMOps(object): def __init__(self): self._smutclient = smutclient.get_smutclient() self._dist_manager = dist.LinuxDistManager() self._pathutils = zvmutils.PathUtils() self._namelist = zvmutils.get_namelist() self._GuestDbOperator = database.GuestDbOperator() self._ImageDbOperator = database.ImageDbOperator() def get_power_state(self, userid): """Get power status of a z/VM instance.""" return self._smutclient.get_power_state(userid) def _get_cpu_num_from_user_dict(self, dict_info): cpu_num = 0 for inf in dict_info: if 'CPU ' in inf: cpu_num += 1 return cpu_num def _get_max_memory_from_user_dict(self, dict_info): with zvmutils.expect_invalid_resp_data(): mem = dict_info[0].split(' ')[4] return zvmutils.convert_to_mb(mem) * 1024 def get_info(self, userid): power_stat = self.get_power_state(userid) perf_info = self._smutclient.get_image_performance_info(userid) if perf_info: try: max_mem_kb = int(perf_info['max_memory'].split()[0]) mem_kb = int(perf_info['used_memory'].split()[0]) num_cpu = int(perf_info['guest_cpus']) cpu_time_us = int(perf_info['used_cpu_time'].split()[0]) except (ValueError, TypeError, IndexError, AttributeError, KeyError) as err: LOG.error('Parse performance_info encounter error: %s', str(perf_info)) raise exception.SDKInternalError(msg=str(err), modID='guest') return {'power_state': power_stat, 'max_mem_kb': max_mem_kb, 'mem_kb': mem_kb, 'num_cpu': num_cpu, 'cpu_time_us': cpu_time_us} else: # virtual machine in shutdown state or not exists dict_info = self._smutclient.get_user_direct(userid) return { 'power_state': power_stat, 'max_mem_kb': self._get_max_memory_from_user_dict(dict_info), 'mem_kb': 0, 'num_cpu': self._get_cpu_num_from_user_dict(dict_info), 'cpu_time_us': 0} def instance_metadata(self, instance, content, extra_md): pass def add_instance_metadata(self): pass def is_reachable(self, userid): """Reachable through IUCV communication channel.""" return self._smutclient.get_guest_connection_status(userid) def guest_start(self, userid): """"Power on z/VM instance.""" LOG.info("Begin to power on vm %s", userid) self._smutclient.guest_start(userid) LOG.info("Complete power on vm %s", userid) def guest_stop(self, userid, **kwargs): LOG.info("Begin to power off vm %s", userid) self._smutclient.guest_stop(userid, **kwargs) LOG.info("Complete power off vm %s", userid) def guest_softstop(self, userid, **kwargs): LOG.info("Begin to soft power off vm %s", userid) self._smutclient.guest_softstop(userid, **kwargs) LOG.info("Complete soft power off vm %s", userid) def guest_pause(self, userid): LOG.info("Begin to pause vm %s", userid) self._smutclient.guest_pause(userid) LOG.info("Complete pause vm %s", userid) def guest_unpause(self, userid): LOG.info("Begin to unpause vm %s", userid) self._smutclient.guest_unpause(userid) LOG.info("Complete unpause vm %s", userid) def guest_reboot(self, userid): """Reboot a guest vm.""" LOG.info("Begin to reboot vm %s", userid) self._smutclient.guest_reboot(userid) LOG.info("Complete reboot vm %s", userid) def guest_reset(self, userid): """Reset z/VM instance.""" LOG.info("Begin to reset vm %s", userid) self._smutclient.guest_reset(userid) LOG.info("Complete reset vm %s", userid) def live_migrate_vm(self, userid, destination, parms, action): """Move an eligible, running z/VM(R) virtual machine transparently from one z/VM system to another within an SSI cluster.""" # Check guest state is 'on' state = self.get_power_state(userid) if state != 'on': LOG.error("Failed to live migrate guest %s, error: " "guest is inactive, cann't perform live migrate." % userid) raise exception.SDKConflictError(modID='guest', rs=1, userid=userid) # Do live migrate if action.lower() == 'move': LOG.info("Moving the specific vm %s", userid) self._smutclient.live_migrate_move(userid, destination, parms) LOG.info("Complete move vm %s", userid) if action.lower() == 'test': LOG.info("Testing the eligiblity of specific vm %s", userid) self._smutclient.live_migrate_test(userid, destination) def create_vm(self, userid, cpu, memory, disk_list, user_profile, max_cpu, max_mem): """Create z/VM userid into user directory for a z/VM instance.""" LOG.info("Creating the user directory for vm %s", userid) info = self._smutclient.create_vm(userid, cpu, memory, disk_list, user_profile, max_cpu, max_mem) # add userid into smapi namelist self._smutclient.namelist_add(self._namelist, userid) return info def create_disks(self, userid, disk_list): LOG.info("Beging to create disks for vm: %(userid)s, list: %(list)s", {'userid': userid, 'list': disk_list}) user_direct = self._smutclient.get_user_direct(userid) exist_disks = [] for ent in user_direct: if ent.strip().startswith('MDISK'): md_vdev = ent.split()[1].strip() exist_disks.append(md_vdev) if exist_disks: start_vdev = hex(int(max(exist_disks), 16) + 1)[2:].rjust(4, '0') else: start_vdev = None info = self._smutclient.add_mdisks(userid, disk_list, start_vdev) LOG.info("Complete create disks for vm: %s", userid) return info def delete_disks(self, userid, vdev_list): LOG.info("Begin to delete disk on vm: %(userid), vdev list: %(list)s", {'userid': userid, 'list': vdev_list}) # not support delete disks when guest is active if self._smutclient.get_power_state(userid) == 'on': func = 'delete disks when guest is active' raise exception.SDKFunctionNotImplementError(func) self._smutclient.remove_mdisks(userid, vdev_list) LOG.info("Complete delete disks for vm: %s", userid) def guest_config_minidisks(self, userid, disk_info): LOG.info("Begin to configure disks on vm: %(userid), info: %(info)s", {'userid': userid, 'info': disk_info}) if disk_info != []: self._smutclient.process_additional_minidisks(userid, disk_info) LOG.info("Complete configure disks for vm: %s", userid) else: LOG.info("No disk to handle on %s." % userid) def is_powered_off(self, instance_name): """Return True if the instance is powered off.""" return self._smutclient.get_power_state(instance_name) == 'off' def delete_vm(self, userid): """Delete z/VM userid for the instance.""" LOG.info("Begin to delete vm %s", userid) self._smutclient.delete_vm(userid) # remove userid from smapi namelist self._smutclient.namelist_remove(self._namelist, userid) LOG.info("Complete delete vm %s", userid) def execute_cmd(self, userid, cmdStr): """Execute commands on the guest vm.""" LOG.debug("executing cmd: %s", cmdStr) return self._smutclient.execute_cmd(userid, cmdStr) def set_hostname(self, userid, hostname, os_version): """Punch a script that used to set the hostname of the guest. :param str guest: the user id of the guest :param str hostname: the hostname of the guest :param str os_version: version of guest operation system """ tmp_path = self._pathutils.get_guest_temp_path(userid) if not os.path.exists(tmp_path): os.makedirs(tmp_path) tmp_file = tmp_path + '/hostname.sh' lnxdist = self._dist_manager.get_linux_dist(os_version)() lines = lnxdist.generate_set_hostname_script(hostname) with open(tmp_file, 'w') as f: f.writelines(lines) requestData = "ChangeVM " + userid + " punchfile " + \ tmp_file + " --class x" LOG.debug("Punch script to guest %s to set hostname" % userid) try: self._smutclient._request(requestData) except exception.SDKSMUTRequestFailed as err: msg = ("Failed to punch set_hostname script to userid '%s'. SMUT " "error: %s" % (userid, err.format_message())) LOG.error(msg) raise exception.SDKSMUTRequestFailed(err.results, msg) finally: self._pathutils.clean_temp_folder(tmp_path) def guest_deploy(self, userid, image_name, transportfiles=None, remotehost=None, vdev=None, hostname=None): LOG.info("Begin to deploy image on vm %s", userid) self._smutclient.guest_deploy(userid, image_name, transportfiles, remotehost, vdev) # punch scripts to set hostname if (transportfiles is None) and hostname: image_info = self._ImageDbOperator.image_query_record(image_name) os_version = image_info[0]['imageosdistro'] self.set_hostname(userid, hostname, os_version) def guest_capture(self, userid, image_name, capture_type='rootonly', compress_level=6): LOG.info("Begin to capture vm %(userid), image name is %(name)s", {'userid': userid, 'name': image_name}) self._smutclient.guest_capture(userid, image_name, capture_type=capture_type, compress_level=compress_level) LOG.info("Complete capture image on vm %s", userid) def guest_list(self): return self._smutclient.get_vm_list() def get_definition_info(self, userid, **kwargs): check_command = ["nic_coupled"] direct_info = self._smutclient.get_user_direct(userid) info = {} info['user_direct'] = direct_info for k, v in kwargs.items(): if k in check_command: if (k == 'nic_coupled'): info['nic_coupled'] = False nstr = "NICDEF %s TYPE QDIO LAN SYSTEM" % v for inf in direct_info: if nstr in inf: info['nic_coupled'] = True break else: raise exception.SDKInvalidInputFormat( msg=("invalid check option for user direct: %s") % k) return info def get_console_output(self, userid): def append_to_log(log_data, log_path): LOG.debug('log_data: %(log_data)r, log_path: %(log_path)r', {'log_data': log_data, 'log_path': log_path}) with open(log_path, 'a+') as fp: fp.write(log_data) return log_path LOG.info("Begin to capture console log on vm %s", userid) log_size = CONF.guest.console_log_size * 1024 console_log = self._smutclient.get_user_console_output(userid) log_path = self._pathutils.get_console_log_path(userid) # TODO: need consider shrink log file size append_to_log(console_log, log_path) log_fp = file(log_path, 'rb') try: log_data, remaining = zvmutils.last_bytes(log_fp, log_size) except Exception as err: msg = ("Failed to truncate console log, error: %s" % six.text_type(err)) LOG.error(msg) raise exception.SDKInternalError(msg) if remaining > 0: LOG.info('Truncated console log returned, %d bytes ignored' % remaining) LOG.info("Complete get console output on vm %s", userid) return log_data def check_guests_exist_in_db(self, userids, raise_exc=True): if not isinstance(userids, list): # convert userid string to list userids = [userids] all_userids = self.guest_list() userids_not_in_db = list(set(userids) - set(all_userids)) if userids_not_in_db: if raise_exc: # log and raise exception userids_not_in_db = ' '.join(userids_not_in_db) LOG.error("Guest '%s' does not exist in guests database" % userids_not_in_db) raise exception.SDKObjectNotExistError( obj_desc=("Guest '%s'" % userids_not_in_db), modID='guest') else: return False else: userids_migrated = self._GuestDbOperator.get_migrated_guest_list() userids_in_migrated = list(set(userids) & set(userids_migrated)) # case1 userid has been migrated. if userids_in_migrated: if raise_exc: migrated_userids = ' '.join(userids_in_migrated) LOG.error("Guest(s) '%s' has been migrated." % migrated_userids) raise exception.SDKObjectNotExistError( obj_desc=("Guest(s) '%s'" % migrated_userids), modID='guest') else: return False flag = True for uid in userids: # case2 userid has been shudown and started on other host. if zvmutils.check_userid_on_others(uid): flag = False comment = self._GuestDbOperator.get_comments_by_userid(uid) comment['migrated'] = 1 action = "update guest '%s' in database" % uid with zvmutils.log_and_reraise_sdkbase_error(action): self._GuestDbOperator.update_guest_by_userid( uid, comments=comment) return flag def live_resize_cpus(self, userid, count): # Check power state is 'on' state = self.get_power_state(userid) if state != 'on': LOG.error("Failed to live resize cpus of guest %s, error: " "guest is inactive, cann't perform live resize." % userid) raise exception.SDKConflictError(modID='guest', rs=1, userid=userid) # Do live resize self._smutclient.live_resize_cpus(userid, count) LOG.info("Complete live resize cpu on vm %s", userid) def resize_cpus(self, userid, count): LOG.info("Begin to resize cpu on vm %s", userid) # Do resize self._smutclient.resize_cpus(userid, count) LOG.info("Complete resize cpu on vm %s", userid) def live_resize_memory(self, userid, memory): # Check power state is 'on' state = self.get_power_state(userid) if state != 'on': LOG.error("Failed to live resize memory of guest %s, error: " "guest is inactive, cann't perform live resize." % userid) raise exception.SDKConflictError(modID='guest', rs=1, userid=userid) # Do live resize self._smutclient.live_resize_memory(userid, memory) LOG.info("Complete live resize memory on vm %s", userid) def resize_memory(self, userid, memory): LOG.info("Begin to resize memory on vm %s", userid) # Do resize self._smutclient.resize_memory(userid, memory) LOG.info("Complete resize memory on vm %s", userid) zVMCloudConnector-1.4.1/zvmsdk/imageops.py0000664000175000017510000000350113442676317020226 0ustar ruirui00000000000000# 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. from zvmsdk import config from zvmsdk import log from zvmsdk import smutclient from zvmsdk import utils as zvmutils LOG = log.LOG CONF = config.CONF _IMAGEOPS = None def get_imageops(): global _IMAGEOPS if _IMAGEOPS is None: _IMAGEOPS = ImageOps() return _IMAGEOPS class ImageOps(object): def __init__(self): self._smutclient = smutclient.get_smutclient() self._pathutils = zvmutils.PathUtils() def image_get_root_disk_size(self, image_name): return self._smutclient.image_get_root_disk_size(image_name) def image_import(self, image_name, url, image_meta, remote_host=None): return self._smutclient.image_import(image_name, url, image_meta, remote_host) def image_query(self, imagename=None): return self._smutclient.image_query(imagename) def image_delete(self, image_name): return self._smutclient.image_delete(image_name) def image_export(self, image_name, dest_url, remote_host=None): return self._smutclient.image_export(image_name, dest_url, remote_host) zVMCloudConnector-1.4.1/zvmsdk/tests/0000775000175000017510000000000013442723341017201 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/__init__.py0000664000175000017510000000000013371225174021302 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/0000775000175000017510000000000013442723341020160 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/__init__.py0000664000175000017510000000000013371225174022261 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_smutclient.py0000664000175000017510000041020713442676324023774 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os import mock import tempfile from smutLayer import smut from zvmsdk import config from zvmsdk import database from zvmsdk import exception from zvmsdk import smutclient from zvmsdk import utils as zvmutils from zvmsdk.tests.unit import base CONF = config.CONF class SDKSMUTClientTestCases(base.SDKTestCase): """Test cases for smut zvm client.""" def setUp(self): self._smutclient = smutclient.SMUTClient() def _generate_results(self, overallrc=0, rc=0, rs=0, errno=0, strerror='', logentries=[], response=[]): return {'rc': rc, 'errno': errno, 'strError': strerror, 'overallRC': overallrc, 'logEntries': logentries, 'rs': rs, 'response': response} @mock.patch.object(smut.SMUT, 'request') def test_private_request_success(self, request): requestData = "fake request" request.return_value = {'overallRC': 0} self._smutclient._request(requestData) request.assert_called_once_with(requestData) @mock.patch.object(smut.SMUT, 'request') def test_private_request_failed(self, request): requestData = "fake request" request.return_value = {'overallRC': 1, 'logEntries': []} self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient._request, requestData) @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_start(self, request): fake_userid = 'FakeID' requestData = "PowerVM FakeID on" request.return_value = {'overallRC': 0} self._smutclient.guest_start(fake_userid) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_stop(self, request): fake_userid = 'FakeID' requestData = "PowerVM FakeID off" request.return_value = {'overallRC': 0} self._smutclient.guest_stop(fake_userid) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_stop_with_timeout(self, request): fake_userid = 'FakeID' requestData = "PowerVM FakeID off --maxwait 300" request.return_value = {'overallRC': 0} self._smutclient.guest_stop(fake_userid, timeout=300) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_stop_with_poll_interval(self, request): fake_userid = 'FakeID' rd = "PowerVM FakeID off --maxwait 300 --poll 10" request.return_value = {'overallRC': 0} self._smutclient.guest_stop(fake_userid, timeout=300, poll_interval=10) request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_softstop(self, request): fake_userid = 'FakeID' requestData = "PowerVM FakeID softoff --maxwait 300 --poll 10" request.return_value = {'overallRC': 0} self._smutclient.guest_softstop(fake_userid, timeout=300, poll_interval=10) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, 'get_power_state') @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_pause(self, request, power_state): power_state.return_value = 'on' fake_userid = 'FakeID' requestData = "PowerVM FakeID pause" request.return_value = {'overallRC': 0} self._smutclient.guest_pause(fake_userid) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, 'get_power_state') @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_unpause(self, request, power_state): power_state.return_value = 'on' fake_userid = 'FakeID' requestData = "PowerVM FakeID unpause" request.return_value = {'overallRC': 0} self._smutclient.guest_unpause(fake_userid) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_power_state(self, request): fake_userid = 'FakeID' requestData = "PowerVM FakeID status" request.return_value = {'overallRC': 0, 'response': [fake_userid + ': on']} status = self._smutclient.get_power_state(fake_userid) request.assert_called_once_with(requestData) self.assertEqual('on', status) @mock.patch.object(smutclient.SMUTClient, 'add_mdisks') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(database.GuestDbOperator, 'add_guest') def test_create_vm(self, add_guest, request, add_mdisks): user_id = 'fakeuser' cpu = 2 memory = 1024 disk_list = [{'size': '1g', 'is_boot_disk': True, 'disk_pool': 'ECKD:eckdpool1', 'format': 'ext3'}] profile = 'osdflt' max_cpu = 10 max_mem = '4G' base.set_conf('zvm', 'default_admin_userid', 'lbyuser1 lbyuser2') base.set_conf('zvm', 'user_root_vdev', '0100') rd = ('makevm fakeuser directory LBYONLY 1024m G --cpus 2 ' '--profile osdflt --maxCPU 10 --maxMemSize 4G --setReservedMem ' '--logonby "lbyuser1 lbyuser2" --ipl 0100') self._smutclient.create_vm(user_id, cpu, memory, disk_list, profile, max_cpu, max_mem) request.assert_called_with(rd) add_mdisks.assert_called_with(user_id, disk_list) add_guest.assert_called_with(user_id) @mock.patch.object(smutclient.SMUTClient, '_request') def test_add_mdisk(self, request): userid = 'fakeuser' disk = {'size': '1g', 'disk_pool': 'ECKD:eckdpool1', 'format': 'ext3'} vdev = '0101' rd = ('changevm fakeuser add3390 eckdpool1 0101 1g --mode MR ' '--filesystem ext3') self._smutclient._add_mdisk(userid, disk, vdev), request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') def test_remove_mdisk(self, request): userid = 'fakeuser' vdev = '0102' rd = 'changevm fakeuser removedisk 0102' self._smutclient._remove_mdisk(userid, vdev), request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_authorize_iucv_client(self, request): fake_userid = 'FakeID' client_userid = 'ClientID' requestData = "ChangeVM FakeID punchfile /tmp/FakeID/iucvauth.sh" + \ " --class x" request.return_value = {'overallRC': 0} self._smutclient.guest_authorize_iucv_client(fake_userid, client_userid) request.assert_called_once_with(requestData) self.assertIs(os.path.exists('/tmp/FakeID'), False) @mock.patch.object(database.GuestDbOperator, 'update_guest_by_userid') @mock.patch.object(database.ImageDbOperator, 'image_query_record') @mock.patch.object(smutclient.SMUTClient, 'guest_authorize_iucv_client') @mock.patch.object(zvmutils.PathUtils, 'clean_temp_folder') @mock.patch.object(tempfile, 'mkdtemp') @mock.patch.object(zvmutils, 'execute') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_image_path_by_name') def test_guest_deploy(self, get_image_path, request, execute, mkdtemp, cleantemp, guestauth, image_query, guest_update): base.set_conf("zvm", "user_root_vdev", "0100") execute.side_effect = [(0, ""), (0, "")] mkdtemp.return_value = '/tmp/tmpdir' image_query.return_value = [{'imageosdistro': 'fakeos'}] userid = 'fakeuser' image_name = 'fakeimg' get_image_path.return_value = \ '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg' transportfiles = '/faketran' self._smutclient.guest_deploy(userid, image_name, transportfiles) get_image_path.assert_called_once_with(image_name) unpack_cmd = ['sudo', '/opt/zthin/bin/unpackdiskimage', 'fakeuser', '0100', '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg/0100'] cp_cmd = ["/usr/bin/cp", '/faketran', '/tmp/tmpdir/faketran'] execute.assert_has_calls([mock.call(unpack_cmd), mock.call(cp_cmd)]) purge_rd = "changevm fakeuser purgerdr" punch_rd = ("changevm fakeuser punchfile " "/tmp/tmpdir/faketran --class X") request.assert_has_calls([mock.call(purge_rd), mock.call(punch_rd)]) mkdtemp.assert_called_with() cleantemp.assert_called_with('/tmp/tmpdir') guestauth.assert_called_once_with(userid) guest_update.assert_called_once_with(userid, meta='os_version=fakeos') @mock.patch.object(zvmutils, 'execute') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_image_path_by_name') def test_guest_deploy_unpackdiskimage_failed(self, get_image_path, request, execute): base.set_conf("zvm", "user_root_vdev", "0100") userid = 'fakeuser' image_name = 'fakeimg' transportfiles = '/faketran' get_image_path.return_value = \ '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg' unpack_error = ('unpackdiskimage fakeuser start time: ' '2017-08-16-01:29:59.453\nSOURCE USER ID: "fakeuser"\n' 'DISK CHANNEL: "0100"\n' 'IMAGE FILE: "/var/lib/zvmsdk/images/fakeimg"\n\n' 'Image file compression level: 6\n' 'Deploying image to fakeuser\'s disk at channel 100.\n' 'ERROR: Unable to link fakeuser 0100 disk. ' 'HCPLNM053E FAKEUSER not in CP directory\n' 'HCPDTV040E Device 260C does not exist\n' 'ERROR: Failed to connect disk: fakeuser:0100\n\n' 'IMAGE DEPLOYMENT FAILED.\n' 'A detailed trace can be found at: /var/log/zthin/' 'unpackdiskimage_trace_2017-08-16-01:29:59.453.txt\n' 'unpackdiskimage end time: 2017-08-16-01:29:59.605\n') execute.return_value = (3, unpack_error) self.assertRaises(exception.SDKGuestOperationError, self._smutclient.guest_deploy, userid, image_name, transportfiles) get_image_path.assert_called_once_with(image_name) unpack_cmd = ['sudo', '/opt/zthin/bin/unpackdiskimage', 'fakeuser', '0100', '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg/0100'] execute.assert_called_once_with(unpack_cmd) @mock.patch.object(zvmutils.PathUtils, 'clean_temp_folder') @mock.patch.object(tempfile, 'mkdtemp') @mock.patch.object(zvmutils, 'execute') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_image_path_by_name') def test_guest_deploy_cp_transport_failed(self, get_image_path, request, execute, mkdtemp, cleantemp): base.set_conf("zvm", "user_root_vdev", "0100") cp_error = ("/usr/bin/cp: cannot stat '/faketran': " "No such file or directory\n") execute.side_effect = [(0, ""), (1, cp_error)] mkdtemp.return_value = '/tmp/tmpdir' userid = 'fakeuser' image_name = 'fakeimg' transportfiles = '/faketran' get_image_path.return_value = \ '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg' self.assertRaises(exception.SDKGuestOperationError, self._smutclient.guest_deploy, userid, image_name, transportfiles) get_image_path.assert_called_once_with(image_name) unpack_cmd = ['sudo', '/opt/zthin/bin/unpackdiskimage', 'fakeuser', '0100', '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg/0100'] cp_cmd = ["/usr/bin/cp", '/faketran', '/tmp/tmpdir/faketran'] execute.assert_has_calls([mock.call(unpack_cmd), mock.call(cp_cmd)]) purge_rd = "changevm fakeuser purgerdr" request.assert_called_once_with(purge_rd) mkdtemp.assert_called_with() cleantemp.assert_called_with('/tmp/tmpdir') @mock.patch.object(zvmutils.PathUtils, 'clean_temp_folder') @mock.patch.object(tempfile, 'mkdtemp') @mock.patch.object(zvmutils, 'execute') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_image_path_by_name') def test_guest_deploy_smut_request_failed(self, get_image_path, request, execute, mkdtemp, cleantemp): base.set_conf("zvm", "user_root_vdev", "0100") get_image_path.return_value = \ '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg' fake_smut_results = {'rs': 8, 'errno': 0, 'strError': 'Failed', 'overallRC': 3, 'rc': 400, 'logEntries': '', 'response': ['(Error) output and error info']} execute.side_effect = [(0, ""), (0, "")] request.side_effect = [None, exception.SDKSMUTRequestFailed( fake_smut_results, 'fake error')] mkdtemp.return_value = '/tmp/tmpdir' userid = 'fakeuser' image_name = 'fakeimg' transportfiles = '/faketran' remote_host = "user@1.1.1.1" self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient.guest_deploy, userid, image_name, transportfiles, remote_host) get_image_path.assert_called_once_with(image_name) unpack_cmd = ['sudo', '/opt/zthin/bin/unpackdiskimage', 'fakeuser', '0100', '/var/lib/zvmsdk/images/netboot/rhel7/fakeimg/0100'] scp_cmd = ["/usr/bin/scp", "-B", '-P', '22', '-o StrictHostKeyChecking=no', 'user@1.1.1.1:/faketran', '/tmp/tmpdir/faketran'] execute.assert_has_calls([mock.call(unpack_cmd), mock.call(scp_cmd)]) purge_rd = "changevm fakeuser purgerdr" punch_rd = ("changevm fakeuser punchfile " "/tmp/tmpdir/faketran --class X") request.assert_has_calls([mock.call(purge_rd), mock.call(punch_rd)]) mkdtemp.assert_called_with() cleantemp.assert_called_with('/tmp/tmpdir') @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_grant_user_to_vswitch(self, request, userid): userid.return_value = 'FakeHostID' vswitch_name = 'FakeVs' userid = 'FakeID' requestData = ' '.join(( 'SMAPI FakeHostID API Virtual_Network_Vswitch_Set_Extended', "--operands", "-k switch_name=FakeVs", "-k grant_userid=FakeID", "-k persist=YES")) self._smutclient.grant_user_to_vswitch(vswitch_name, userid) request.assert_called_once_with(requestData) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_revoke_user_from_vswitch(self, request, userid): userid.return_value = 'FakeHostID' vswitch_name = 'FakeVs' userid = 'FakeID' requestData = ' '.join(( 'SMAPI FakeHostID API Virtual_Network_Vswitch_Set_Extended', "--operands", "-k switch_name=FakeVs", "-k revoke_userid=FakeID", "-k persist=YES")) self._smutclient.revoke_user_from_vswitch(vswitch_name, userid) request.assert_called_once_with(requestData) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_image_performance_query_single(self, smut_req, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': [ 'Virtual server ID: FAKEVM', 'Record version: "1"', 'Guest flags: "0"', 'Used CPU time: "646609178 uS"', 'Elapsed time: "596837441984 uS"', 'Minimum memory: "0 KB"', 'Max memory: "2097152 KB"', 'Shared memory: "302180 KB"', 'Used memory: "302180 KB"', 'Active CPUs in CEC: "44"', 'Logical CPUs in VM: "6"', 'Guest CPUs: "2"', 'Minimum CPU count: "2"', 'Max CPU limit: "10000"', 'Processor share: "100"', 'Samples CPU in use: "371"', ',Samples CPU delay: "116"', 'Samples page wait: "0"', 'Samples idle: "596331"', 'Samples other: "12"', 'Samples total: "596830"', 'Guest name: "FAKEVM "', ''] } pi_info = self._smutclient.image_performance_query('fakevm') self.assertEqual(pi_info['FAKEVM']['used_memory'], "302180 KB") self.assertEqual(pi_info['FAKEVM']['used_cpu_time'], "646609178 uS") self.assertEqual(pi_info['FAKEVM']['elapsed_cpu_time'], "596837441984 uS") self.assertEqual(pi_info['FAKEVM']['min_cpu_count'], "2") self.assertEqual(pi_info['FAKEVM']['max_cpu_limit'], "10000") self.assertEqual(pi_info['FAKEVM']['samples_cpu_in_use'], "371") self.assertEqual(pi_info['FAKEVM']['samples_cpu_delay'], "116") self.assertEqual(pi_info['FAKEVM']['guest_cpus'], "2") self.assertEqual(pi_info['FAKEVM']['userid'], "FAKEVM") self.assertEqual(pi_info['FAKEVM']['max_memory'], "2097152 KB") self.assertEqual(pi_info['FAKEVM']['min_memory'], "0 KB") self.assertEqual(pi_info['FAKEVM']['shared_memory'], "302180 KB") @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_image_performance_query_single_off(self, smut_req, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': [] } pi_info = self._smutclient.image_performance_query('fakevm') self.assertDictEqual(pi_info, {}) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_image_performance_query_multiple(self, smut_req, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" response_list = ['Virtual server ID: fakevm', 'Record version: "1"', 'Guest flags: "0"', 'Used CPU time: "652337849 uS"', 'Elapsed time: "602181110336 uS"', 'Minimum memory: "0 KB"', 'Max memory: "2097152 KB"', 'Shared memory: "302336 KB"', 'Used memory: "302336 KB"', 'Active CPUs in CEC: "44"', 'Logical CPUs in VM: "6"', 'Guest CPUs: "2"', 'Minimum CPU count: "2"', 'Max CPU limit: "10000"', 'Processor share: "100"', 'Samples CPU in use: "375"', ',Samples CPU delay: "116"', 'Samples page wait: "0"', 'Samples idle: "601671"', 'Samples other: "12"', 'Samples total: "602174"', 'Guest name: "FAKEVM "', '', 'Virtual server ID: fakevm2', 'Record version: "1"', 'Guest flags: "0"', 'Used CPU time: "3995650268844 uS"', 'Elapsed time: "3377790094595 uS"', 'Minimum memory: "0 KB"', 'Max memory: "8388608 KB"', 'Shared memory: "8383048 KB"', 'Used memory: "8383048 KB"', 'Active CPUs in CEC: "44"', 'Logical CPUs in VM: "6"', 'Guest CPUs: "4"', 'Minimum CPU count: "4"', 'Max CPU limit: "10000"', 'Processor share: "100"', 'Samples CPU in use: "1966323"', ',Samples CPU delay: "111704"', 'Samples page wait: "0"', 'Samples idle: "4001258"', 'Samples other: "8855"', 'Samples total: "6088140"', 'Guest name: "FAKEVM2 "', ''] smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': response_list } pi_info = self._smutclient.image_performance_query(['fakevm', 'fakevm2']) self.assertEqual(pi_info['FAKEVM']['used_memory'], "302336 KB") self.assertEqual(pi_info['FAKEVM']['used_cpu_time'], "652337849 uS") self.assertEqual(pi_info['FAKEVM']['elapsed_cpu_time'], "602181110336 uS") self.assertEqual(pi_info['FAKEVM']['min_cpu_count'], "2") self.assertEqual(pi_info['FAKEVM']['max_cpu_limit'], "10000") self.assertEqual(pi_info['FAKEVM']['samples_cpu_in_use'], "375") self.assertEqual(pi_info['FAKEVM']['samples_cpu_delay'], "116") self.assertEqual(pi_info['FAKEVM']['guest_cpus'], "2") self.assertEqual(pi_info['FAKEVM']['userid'], "FAKEVM") self.assertEqual(pi_info['FAKEVM']['max_memory'], "2097152 KB") self.assertEqual(pi_info['FAKEVM']['min_memory'], "0 KB") self.assertEqual(pi_info['FAKEVM']['shared_memory'], "302336 KB") self.assertEqual(pi_info['FAKEVM2']['used_memory'], "8383048 KB") self.assertEqual(pi_info['FAKEVM2']['used_cpu_time'], "3995650268844 uS") self.assertEqual(pi_info['FAKEVM2']['elapsed_cpu_time'], "3377790094595 uS") self.assertEqual(pi_info['FAKEVM2']['min_cpu_count'], "4") self.assertEqual(pi_info['FAKEVM2']['max_cpu_limit'], "10000") self.assertEqual(pi_info['FAKEVM2']['samples_cpu_in_use'], "1966323") self.assertEqual(pi_info['FAKEVM2']['samples_cpu_delay'], "111704") self.assertEqual(pi_info['FAKEVM2']['guest_cpus'], "4") self.assertEqual(pi_info['FAKEVM2']['userid'], "FAKEVM2") self.assertEqual(pi_info['FAKEVM2']['max_memory'], "8388608 KB") self.assertEqual(pi_info['FAKEVM2']['min_memory'], "0 KB") self.assertEqual(pi_info['FAKEVM2']['shared_memory'], "8383048 KB") @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_system_image_performance_query(self, smut_req, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" response_list = ['Virtual server ID: fakevm', 'Record version: "1"', 'Guest flags: "0"', 'Used CPU time: "652337849 uS"', 'Elapsed time: "602181110336 uS"', 'Minimum memory: "0 KB"', 'Max memory: "2097152 KB"', 'Shared memory: "302336 KB"', 'Used memory: "302336 KB"', 'Active CPUs in CEC: "44"', 'Logical CPUs in VM: "6"', 'Guest CPUs: "2"', 'Minimum CPU count: "2"', 'Max CPU limit: "10000"', 'Processor share: "100"', 'Samples CPU in use: "375"', ',Samples CPU delay: "116"', 'Samples page wait: "0"', 'Samples idle: "601671"', 'Samples other: "12"', 'Samples total: "602174"', 'Guest name: "FAKEVM "', '', 'Virtual server ID: fakevm2', 'Record version: "1"', 'Guest flags: "0"', 'Used CPU time: "3995650268844 uS"', 'Elapsed time: "3377790094595 uS"', 'Minimum memory: "0 KB"', 'Max memory: "8388608 KB"', 'Shared memory: "8383048 KB"', 'Used memory: "8383048 KB"', 'Active CPUs in CEC: "44"', 'Logical CPUs in VM: "6"', 'Guest CPUs: "4"', 'Minimum CPU count: "4"', 'Max CPU limit: "10000"', 'Processor share: "100"', 'Samples CPU in use: "1966323"', ',Samples CPU delay: "111704"', 'Samples page wait: "0"', 'Samples idle: "4001258"', 'Samples other: "8855"', 'Samples total: "6088140"', 'Guest name: "FAKEVM2 "', ''] smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': response_list } pi_info = self._smutclient.system_image_performance_query(['fakevm', 'fakevm2']) self.assertEqual(pi_info['FAKEVM']['used_memory'], "302336 KB") self.assertEqual(pi_info['FAKEVM']['used_cpu_time'], "652337849 uS") self.assertEqual(pi_info['FAKEVM']['elapsed_cpu_time'], "602181110336 uS") self.assertEqual(pi_info['FAKEVM']['min_cpu_count'], "2") self.assertEqual(pi_info['FAKEVM']['max_cpu_limit'], "10000") self.assertEqual(pi_info['FAKEVM']['samples_cpu_in_use'], "375") self.assertEqual(pi_info['FAKEVM']['samples_cpu_delay'], "116") self.assertEqual(pi_info['FAKEVM']['guest_cpus'], "2") self.assertEqual(pi_info['FAKEVM']['userid'], "FAKEVM") self.assertEqual(pi_info['FAKEVM']['max_memory'], "2097152 KB") self.assertEqual(pi_info['FAKEVM']['min_memory'], "0 KB") self.assertEqual(pi_info['FAKEVM']['shared_memory'], "302336 KB") self.assertEqual(pi_info['FAKEVM2']['used_memory'], "8383048 KB") self.assertEqual(pi_info['FAKEVM2']['used_cpu_time'], "3995650268844 uS") self.assertEqual(pi_info['FAKEVM2']['elapsed_cpu_time'], "3377790094595 uS") self.assertEqual(pi_info['FAKEVM2']['min_cpu_count'], "4") self.assertEqual(pi_info['FAKEVM2']['max_cpu_limit'], "10000") self.assertEqual(pi_info['FAKEVM2']['samples_cpu_in_use'], "1966323") self.assertEqual(pi_info['FAKEVM2']['samples_cpu_delay'], "111704") self.assertEqual(pi_info['FAKEVM2']['guest_cpus'], "4") self.assertEqual(pi_info['FAKEVM2']['userid'], "FAKEVM2") self.assertEqual(pi_info['FAKEVM2']['max_memory'], "8388608 KB") self.assertEqual(pi_info['FAKEVM2']['min_memory'], "0 KB") self.assertEqual(pi_info['FAKEVM2']['shared_memory'], "8383048 KB") @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_virtual_network_vswitch_query_byte_stats(self, smut_req, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" vsw_data = ['vswitch count: 2', '', 'vswitch number: 1', 'vswitch name: XCATVSW1', 'uplink count: 1', 'uplink_conn: 6240', 'uplink_fr_rx: 3658251', 'uplink_fr_rx_dsc: 0', 'uplink_fr_rx_err: 0', 'uplink_fr_tx: 4209828', 'uplink_fr_tx_dsc: 0', 'uplink_fr_tx_err: 0', 'uplink_rx: 498914052', 'uplink_tx: 2615220898', 'bridge_fr_rx: 0', 'bridge_fr_rx_dsc: 0', 'bridge_fr_rx_err: 0', 'bridge_fr_tx: 0', 'bridge_fr_tx_dsc: 0', 'bridge_fr_tx_err: 0', 'bridge_rx: 0', 'bridge_tx: 0', 'nic count: 2', 'nic_id: INST1 0600', 'nic_fr_rx: 573952', 'nic_fr_rx_dsc: 0', 'nic_fr_rx_err: 0', 'nic_fr_tx: 548780', 'nic_fr_tx_dsc: 0', 'nic_fr_tx_err: 4', 'nic_rx: 103024058', 'nic_tx: 102030890', 'nic_id: INST2 0600', 'nic_fr_rx: 17493', 'nic_fr_rx_dsc: 0', 'nic_fr_rx_err: 0', 'nic_fr_tx: 16886', 'nic_fr_tx_dsc: 0', 'nic_fr_tx_err: 4', 'nic_rx: 3111714', 'nic_tx: 3172646', 'vlan count: 0', '', 'vswitch number: 2', 'vswitch name: XCATVSW2', 'uplink count: 1', 'uplink_conn: 6200', 'uplink_fr_rx: 1608681', 'uplink_fr_rx_dsc: 0', 'uplink_fr_rx_err: 0', 'uplink_fr_tx: 2120075', 'uplink_fr_tx_dsc: 0', 'uplink_fr_tx_err: 0', 'uplink_rx: 314326223', 'uplink_tx: 1503721533', 'bridge_fr_rx: 0', 'bridge_fr_rx_dsc: 0', 'bridge_fr_rx_err: 0', 'bridge_fr_tx: 0', 'bridge_fr_tx_dsc: 0', 'bridge_fr_tx_err: 0', 'bridge_rx: 0', 'bridge_tx: 0', 'nic count: 2', 'nic_id: INST1 1000', 'nic_fr_rx: 34958', 'nic_fr_rx_dsc: 0', 'nic_fr_rx_err: 0', 'nic_fr_tx: 16211', 'nic_fr_tx_dsc: 0', 'nic_fr_tx_err: 0', 'nic_rx: 4684435', 'nic_tx: 3316601', 'nic_id: INST2 1000', 'nic_fr_rx: 27211', 'nic_fr_rx_dsc: 0', 'nic_fr_rx_err: 0', 'nic_fr_tx: 12344', 'nic_fr_tx_dsc: 0', 'nic_fr_tx_err: 0', 'nic_rx: 3577163', 'nic_tx: 2515045', 'vlan count: 0' ] smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': vsw_data } vsw_dict = self._smutclient.virtual_network_vswitch_query_byte_stats() self.assertEqual(2, len(vsw_dict['vswitches'])) self.assertEqual(2, len(vsw_dict['vswitches'][1]['nics'])) self.assertEqual('INST1', vsw_dict['vswitches'][0]['nics'][0]['userid']) self.assertEqual('3577163', vsw_dict['vswitches'][1]['nics'][1]['nic_rx']) @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_host_info(self, smut_req): resp = ['ZCC USERID: OPNCLOUD', 'z/VM Host: OPNSTK2', 'Architecture: s390x', 'CEC Vendor: IBM', 'CEC Model: 2817', 'Hypervisor OS: z/VM 6.4.0', 'Hypervisor Name: OPNSTK2', 'LPAR CPU Total: 6', 'LPAR CPU Used: 6', 'LPAR Memory Total: 50G', 'LPAR Memory Offline: 0', 'LPAR Memory Used: 36.5G', 'IPL Time: IPL at 07/12/17 22:37:47 EDT'] smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': resp} expect = {'architecture': 's390x', 'cec_model': '2817', 'cec_vendor': 'IBM', 'hypervisor_name': 'OPNSTK2', 'hypervisor_os': 'z/VM 6.4.0', 'ipl_time': 'IPL at 07/12/17 22:37:47 EDT', 'lpar_cpu_total': '6', 'lpar_cpu_used': '6', 'lpar_memory_offline': '0', 'lpar_memory_total': '50G', 'lpar_memory_used': '36.5G', 'zcc_userid': 'OPNCLOUD', 'zvm_host': 'OPNSTK2'} host_info = self._smutclient.get_host_info() smut_req.assert_called_once_with('getHost general') self.assertDictEqual(host_info, expect) @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_diskpool_info(self, smut_req): resp = ['XCATECKD Total: 3623.0G', 'XCATECKD Used: 397.4G', 'XCATECKD Free: 3225.6G'] smut_req.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': resp} expect = {'disk_available': '3225.6G', 'disk_total': '3623.0G', 'disk_used': '397.4G'} dp_info = self._smutclient.get_diskpool_info('pool') smut_req.assert_called_once_with('getHost diskpoolspace pool') self.assertDictEqual(dp_info, expect) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_vswitch_list(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" request.return_value = {'overallRC': 0, 'response': ['VSWITCH: Name: VSTEST1', 'VSWITCH: Name: VSTEST2', 'VSWITCH: Name: VSTEST3', 'VSWITCH: Name: VSTEST4']} expect = ['VSTEST1', 'VSTEST2', 'VSTEST3', 'VSTEST4'] rd = ' '.join(( "SMAPI SMUTUSER API Virtual_Network_Vswitch_Query", "--operands", "-s \'*\'")) list = self._smutclient.get_vswitch_list() request.assert_called_once_with(rd) self.assertEqual(list, expect) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_set_vswitch_port_vlan_id(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" request.return_value = {'overallRC': 0} userid = 'FakeID' vswitch_name = 'FakeVS' vlan_id = 'FakeVLAN' rd = ' '.join(( "SMAPI SMUTUSER API Virtual_Network_Vswitch_Set_Extended", "--operands", "-k grant_userid=FakeID", "-k switch_name=FakeVS", "-k user_vlan_id=FakeVLAN", "-k persist=YES")) self._smutclient.set_vswitch_port_vlan_id(vswitch_name, userid, vlan_id) request.assert_called_once_with(rd) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_add_vswitch(self, request, get_smut_userid): get_smut_userid.return_value = 'SMUTUSER' request.return_value = {'overallRC': 0} rd = ' '.join(( "SMAPI SMUTUSER API Virtual_Network_Vswitch_Create_Extended", "--operands", "-k switch_name=fakename", "-k real_device_address='111 222'", "-k connection_value=CONNECT", "-k queue_memory_limit=5", "-k transport_type=ETHERNET", "-k vlan_id=10", "-k persist=NO", "-k port_type=ACCESS", "-k gvrp_value=GVRP", "-k native_vlanid=None", "-k routing_value=NONROUTER")) self._smutclient.add_vswitch("fakename", rdev="111 222", controller='*', connection='CONNECT', network_type='ETHERNET', router="NONROUTER", vid='10', port_type='ACCESS', gvrp='GVRP', queue_mem=5, native_vid=None, persist=False) request.assert_called_with(rd) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_set_vswitch(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" request.return_value = {'overallRC': 0} rd = ' '.join(( "SMAPI SMUTUSER API Virtual_Network_Vswitch_Set_Extended", "--operands", "-k switch_name=fake_vs", "-k real_device_address='1000 1003'")) self._smutclient.set_vswitch("fake_vs", real_device_address='1000 1003') request.assert_called_with(rd) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_set_vswitch_with_errorcode(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" results = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 1, 'logEntries': [], 'rc': 0, 'response': ['fake response']} request.side_effect = exception.SDKSMUTRequestFailed( results, 'fake error') self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient.set_vswitch, "vswitch_name", grant_userid='fake_id') @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_delete_vswitch(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" request.return_value = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 0, 'logEntries': [], 'rc': 0, 'response': ['fake response']} switch_name = 'FakeVS' rd = ' '.join(( "SMAPI SMUTUSER API Virtual_Network_Vswitch_Delete_Extended", "--operands", "-k switch_name=FakeVS", "-k persist=YES")) self._smutclient.delete_vswitch(switch_name, True) request.assert_called_once_with(rd) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_delete_vswitch_with_errorcode(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" results = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 1, 'logEntries': [], 'rc': 0, 'response': ['fake response']} request.side_effect = exception.SDKSMUTRequestFailed( results, 'fake error') self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient.delete_vswitch, "vswitch_name", True) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_delete_vswitch_not_exist(self, request, get_smut_userid): get_smut_userid.return_value = "SMUTUSER" results = {'rs': 40, 'errno': 0, 'strError': '', 'overallRC': 1, 'logEntries': [], 'rc': 212, 'response': ['fake response']} request.side_effect = exception.SDKSMUTRequestFailed( results, 'fake error') switch_name = 'FakeVS' rd = ' '.join(( "SMAPI SMUTUSER API Virtual_Network_Vswitch_Delete_Extended", "--operands", "-k switch_name=FakeVS", "-k persist=YES")) self._smutclient.delete_vswitch(switch_name, True) request.assert_called_once_with(rd) @mock.patch.object(database.NetworkDbOperator, 'switch_select_table') def test_get_available_vdev(self, switch_select_table): switch_select_table.return_value = [ {'userid': 'fake_id', 'interface': '1003', 'switch': None, 'port': None, 'comments': None}, {'userid': 'fake_id', 'interface': '1006', 'switch': None, 'port': None, 'comments': None}] result = self._smutclient._get_available_vdev('fake_id', vdev='1009') switch_select_table.assert_called_with() self.assertEqual(result, '1009') @mock.patch.object(database.NetworkDbOperator, 'switch_select_table') def test_get_available_vdev_without_vdev(self, switch_select_table): switch_select_table.return_value = [ {'userid': 'FAKE_ID', 'interface': '1003', 'switch': None, 'port': None, 'comments': None}, {'userid': 'FAKE_ID', 'interface': '2003', 'switch': None, 'port': None, 'comments': None}] result = self._smutclient._get_available_vdev('fake_id', vdev=None) switch_select_table.assert_called_with() self.assertEqual(result, '2006') @mock.patch.object(database.NetworkDbOperator, 'switch_select_table') def test_get_available_vdev_with_used_vdev(self, switch_select_table): switch_select_table.return_value = [ {'userid': 'FAKE_ID', 'interface': '1003', 'switch': None, 'port': None, 'comments': None}, {'userid': 'FAKE_ID', 'interface': '1006', 'switch': None, 'port': None, 'comments': None}] self.assertRaises(exception.SDKConflictError, self._smutclient._get_available_vdev, 'fake_id', vdev='1004') @mock.patch.object(smutclient.SMUTClient, '_get_available_vdev') @mock.patch.object(smutclient.SMUTClient, '_create_nic') def test_create_nic(self, create_nic, get_vdev): userid = 'fake_id' get_vdev.return_value = '1009' self._smutclient.create_nic(userid, vdev='1009', nic_id='nic_id') create_nic.assert_called_with(userid, '1009', nic_id="nic_id", mac_addr=None, active=False) get_vdev.assert_called_with(userid, vdev='1009') @mock.patch.object(smutclient.SMUTClient, '_get_available_vdev') @mock.patch.object(smutclient.SMUTClient, '_create_nic') def test_create_nic_without_vdev(self, create_nic, get_vdev): userid = 'fake_id' get_vdev.return_value = '2006' self._smutclient.create_nic(userid, nic_id='nic_id') create_nic.assert_called_with(userid, '2006', nic_id='nic_id', mac_addr=None, active=False) get_vdev.assert_called_with(userid, vdev=None) @mock.patch.object(smutclient.SMUTClient, '_get_available_vdev') def test_create_nic_with_used_vdev(self, get_vdev): get_vdev.side_effect = exception.SDKConflictError('network', rs=6, vdev='1004', userid='fake_id', msg="error") self.assertRaises(exception.SDKConflictError, self._smutclient.create_nic, 'fake_id', nic_id="nic_id", vdev='1004') @mock.patch.object(database.NetworkDbOperator, 'switch_add_record') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_private_create_nic_active(self, power_state, request, add_record): request.return_value = {'overallRC': 0} power_state.return_value = 'on' self._smutclient._create_nic("fakenode", "fake_vdev", nic_id="fake_nic", mac_addr='11:22:33:44:55:66', active=True) add_record.assert_called_once_with("fakenode", "fake_vdev", port="fake_nic") rd1 = ' '.join(( 'SMAPI fakenode API Virtual_Network_Adapter_Create_Extended_DM', "--operands", "-k image_device_number=fake_vdev", "-k adapter_type=QDIO", "-k mac_id=445566")) rd2 = ' '.join(( 'SMAPI fakenode API Virtual_Network_Adapter_Create_Extended', "--operands", "-k image_device_number=fake_vdev", "-k adapter_type=QDIO")) request.assert_any_call(rd1) request.assert_any_call(rd2) @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_user_direct(self, req): req.return_value = {'response': 'OK'} resp = self._smutclient.get_user_direct('user1') req.assert_called_once_with('getvm user1 directory') self.assertEqual(resp, 'OK') @mock.patch.object(database.NetworkDbOperator, 'switch_delete_record_for_nic') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') @mock.patch.object(database.NetworkDbOperator, 'switch_select_record_for_userid') def test_delete_nic(self, select_rec, power_state, request, delete_nic): select_rec.return_value = [{"interface": "1000", "comments": None}] power_state.return_value = 'on' userid = 'FakeID' vdev = '1000' rd1 = ' '.join(( "SMAPI FakeID API Virtual_Network_Adapter_Delete_DM", "--operands", '-v 1000')) rd2 = ' '.join(( "SMAPI FakeID API Virtual_Network_Adapter_Delete", "--operands", '-v 1000')) self._smutclient.delete_nic(userid, vdev, True) request.assert_any_call(rd1) request.assert_any_call(rd2) delete_nic.assert_called_with(userid, vdev) @mock.patch.object(smutclient.SMUTClient, '_undedicate_nic') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') @mock.patch.object(database.NetworkDbOperator, 'switch_select_record_for_userid') def test_delete_nic_OSA(self, select_rec, power_state, undedicate_nic): select_rec.return_value = [{"interface": "1000", "comments": "OSA=F000"}] power_state.return_value = 'on' userid = 'FakeID' vdev = '1000' self._smutclient.delete_nic(userid, vdev, True) undedicate_nic.assert_called_with(userid, vdev, active=True) @mock.patch.object(smutclient.SMUTClient, '_couple_nic') def test_couple_nic_to_vswitch(self, couple_nic): self._smutclient.couple_nic_to_vswitch("fake_userid", "fakevdev", "fake_VS_name", True) couple_nic.assert_called_with("fake_userid", "fakevdev", "fake_VS_name", active=True) @mock.patch.object(smutclient.SMUTClient, '_uncouple_nic') def test_uncouple_nic_from_vswitch(self, uncouple_nic): self._smutclient.uncouple_nic_from_vswitch("fake_userid", "fakevdev", False) uncouple_nic.assert_called_with("fake_userid", "fakevdev", active=False) @mock.patch.object(database.NetworkDbOperator, 'switch_update_record_with_switch') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_couple_nic(self, power_state, request, update_switch): request.return_value = {'overallRC': 0} power_state.return_value = 'on' userid = 'FakeID' vdev = 'FakeVdev' vswitch_name = 'FakeVS' requestData1 = ' '.join(( 'SMAPI FakeID', "API Virtual_Network_Adapter_Connect_Vswitch_DM", "--operands", "-v FakeVdev", "-n FakeVS")) requestData2 = ' '.join(( 'SMAPI FakeID', "API Virtual_Network_Adapter_Connect_Vswitch", "--operands", "-v FakeVdev", "-n FakeVS")) self._smutclient._couple_nic(userid, vdev, vswitch_name, active=True) update_switch.assert_called_with(userid, vdev, vswitch_name) request.assert_any_call(requestData1) request.assert_any_call(requestData2) @mock.patch.object(database.NetworkDbOperator, 'switch_update_record_with_switch') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_uncouple_nic(self, power_state, request, update_switch): request.return_value = {'overallRC': 0} power_state.return_value = 'on' userid = 'FakeID' vdev = 'FakeVdev' requestData1 = ' '.join(( 'SMAPI FakeID', "API Virtual_Network_Adapter_Disconnect_DM", "--operands", "-v FakeVdev")) requestData2 = ' '.join(( 'SMAPI FakeID', "API Virtual_Network_Adapter_Disconnect", "--operands", "-v FakeVdev")) self._smutclient._uncouple_nic(userid, vdev, active=True) update_switch.assert_called_with(userid, vdev, None) request.assert_any_call(requestData1) request.assert_any_call(requestData2) @mock.patch.object(database.GuestDbOperator, 'get_migrated_guest_list') @mock.patch.object(database.GuestDbOperator, 'get_guest_list') def test_get_vm_list(self, db_list, migrated_list): db_list.return_value = [(u'9a5c9689-d099-46bb-865f-0c01c384f58c', u'TEST0', u'', u''), (u'3abe0ac8-90b5-4b00-b624-969c184b8158', u'TEST1', u'comm1', u''), (u'aa252ca5-03aa-4407-9c2e-d9737ddb8d24', u'TEST2', u'comm2', u'meta2')] migrated_list.return_value = [] userid_list = self._smutclient.get_vm_list() db_list.assert_called_once() migrated_list.assert_called_once() self.assertListEqual(sorted(userid_list), sorted(['TEST0', 'TEST1', 'TEST2'])) @mock.patch.object(database.GuestDbOperator, 'get_migrated_guest_list') @mock.patch.object(database.GuestDbOperator, 'get_guest_list') def test_get_vm_list_exclude_migrated(self, db_list, migrated_list): db_list.return_value = [(u'9a5c9689-d099-46bb-865f-0c01c384f58c', u'TEST0', u'', u''), (u'3abe0ac8-90b5-4b00-b624-969c184b8158', u'TEST1', u'comm1', u''), (u'aa252ca5-03aa-4407-9c2e-d9737ddb8d24', u'TEST2', u'{"migrated": 1}', u'meta2')] migrated_list.return_value = [(u'aa252ca5-03aa-4407-9c2e-d9737ddb8d24', u'TEST2', u'{"migrated": 1}', u'meta2')] userid_list = self._smutclient.get_vm_list() db_list.assert_called_once() migrated_list.assert_called_once() self.assertListEqual(sorted(userid_list), sorted(['TEST0', 'TEST1'])) @mock.patch.object(smutclient.SMUTClient, '_request') def test_delete_userid(self, request): rd = 'deletevm fuser1 directory' self._smutclient.delete_userid('fuser1') request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') def test_execute_cmd(self, request): rd = 'cmdVM fuser1 CMD \'ls\'' self._smutclient.execute_cmd('fuser1', 'ls') request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') def test_delete_userid_not_exist(self, request): rd = 'deletevm fuser1 directory' results = {'rc': 400, 'rs': 4, 'logEntries': ''} request.side_effect = exception.SDKSMUTRequestFailed(results, "fake error") self._smutclient.delete_userid('fuser1') request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') def test_delete_userid_failed(self, request): rd = 'deletevm fuser1 directory' results = {'rc': 400, 'rs': 104, 'logEntries': ''} request.side_effect = exception.SDKSMUTRequestFailed(results, "fake error") self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient.delete_userid, 'fuser1') request.assert_called_once_with(rd) @mock.patch.object(os, 'rename') @mock.patch.object(database.ImageDbOperator, 'image_add_record') @mock.patch.object(smutclient.SMUTClient, '_get_image_size') @mock.patch.object(smutclient.SMUTClient, '_get_disk_size_units') @mock.patch.object(smutclient.SMUTClient, '_get_md5sum') @mock.patch.object(smutclient.FilesystemBackend, 'image_import') @mock.patch.object(zvmutils.PathUtils, 'create_import_image_repository') @mock.patch.object(database.ImageDbOperator, 'image_query_record') def test_image_import(self, image_query, create_path, image_import, get_md5sum, disk_size_units, image_size, image_add_record, rename): image_name = 'testimage' url = 'file:///tmp/testdummyimg' image_meta = {'os_version': 'rhel6.5', 'md5sum': 'c73ce117eef8077c3420bfc8f473ac2f'} import_image_fpath = '/home/netboot/rhel6.5/testimage/testdummyimg' final_image_fpath = '/home/netboot/rhel6.5/testimage/0100' image_query.return_value = [] create_path.return_value = '/home/netboot/rhel6.5/testimage' get_md5sum.return_value = 'c73ce117eef8077c3420bfc8f473ac2f' disk_size_units.return_value = '3338:CYL' image_size.return_value = '512000' self._smutclient.image_import(image_name, url, image_meta) image_query.assert_called_once_with(image_name) image_import.assert_called_once_with(image_name, url, import_image_fpath, remote_host=None) get_md5sum.assert_called_once_with(import_image_fpath) disk_size_units.assert_called_once_with(final_image_fpath) image_size.assert_called_once_with(final_image_fpath) image_add_record.assert_called_once_with(image_name, 'rhel6.5', 'c73ce117eef8077c3420bfc8f473ac2f', '3338:CYL', '512000', 'rootonly') @mock.patch.object(smutclient.SMUTClient, '_get_image_path_by_name') @mock.patch.object(database.ImageDbOperator, 'image_query_record') def test_image_import_image_already_exist(self, image_query, get_image_path): image_name = 'testimage' url = 'file:///tmp/testdummyimg' image_meta = {'os_version': 'rhel6.5', 'md5sum': 'c73ce117eef8077c3420bfc8f473ac2f'} image_query.return_value = [(u'testimage', u'rhel6.5', u'c73ce117eef8077c3420bfc8f473ac2f', u'3338:CYL', u'5120000', u'netboot', None)] self.assertRaises(exception.SDKImageOperationError, self._smutclient.image_import, image_name, url, image_meta) image_query.assert_called_once_with(image_name) get_image_path.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_md5sum') @mock.patch.object(smutclient.FilesystemBackend, 'image_import') @mock.patch.object(database.ImageDbOperator, 'image_query_record') def test_image_import_invalid_md5sum(self, image_query, image_import, get_md5sum): image_name = 'testimage' url = 'file:///tmp/testdummyimg' image_meta = {'os_version': 'rhel6.5', 'md5sum': 'c73ce117eef8077c3420bfc8f473ac2f'} image_query.return_value = [] get_md5sum.return_value = 'c73ce117eef8077c3420bfc000000' self.assertRaises(exception.SDKImageOperationError, self._smutclient.image_import, image_name, url, image_meta) @mock.patch.object(database.ImageDbOperator, 'image_query_record') def test_image_query(self, image_query): image_name = "testimage" self._smutclient.image_query(image_name) image_query.assert_called_once_with(image_name) @mock.patch.object(database.ImageDbOperator, 'image_delete_record') @mock.patch.object(smutclient.SMUTClient, '_delete_image_file') def test_image_delete(self, delete_file, delete_db_record): image_name = 'testimage' self._smutclient.image_delete(image_name) delete_file.assert_called_once_with(image_name) delete_db_record.assert_called_once_with(image_name) @mock.patch.object(smutclient.SMUTClient, 'image_get_root_disk_size') def test_image_get_root_disk_size(self, query_disk_size_units): image_name = 'testimage' self._smutclient.image_get_root_disk_size(image_name) query_disk_size_units.assert_called_once_with(image_name) @mock.patch.object(database.ImageDbOperator, 'image_query_record') @mock.patch.object(smutclient.FilesystemBackend, 'image_export') def test_image_export(self, image_export, image_query): image_name = u'testimage' dest_url = 'file:///path/to/exported/image' remote_host = 'nova@9.x.x.x' image_query.return_value = [ {'imagename': u'testimage', 'imageosdistro': u'rhel6.5', 'md5sum': u'c73ce117eef8077c3420bfc8f473ac2f', 'disk_size_units': u'3338:CYL', 'image_size_in_bytes': u'5120000', 'type': u'rootonly', 'comments': None}] expect_return = { 'image_name': u'testimage', 'image_path': u'file:///path/to/exported/image', 'os_version': u'rhel6.5', 'md5sum': u'c73ce117eef8077c3420bfc8f473ac2f' } real_return = self._smutclient.image_export(image_name, dest_url, remote_host=remote_host) image_query.assert_called_once_with(image_name) self.assertDictEqual(real_return, expect_return) def test_generate_vdev(self): base = '0100' idx = 1 vdev = self._smutclient._generate_vdev(base, idx) self.assertEqual(vdev, '0101') @mock.patch.object(smutclient.SMUTClient, '_add_mdisk') def test_add_mdisks(self, add_mdisk): userid = 'fakeuser' disk_list = [{'size': '1g', 'is_boot_disk': True, 'disk_pool': 'ECKD:eckdpool1'}, {'size': '200000', 'disk_pool': 'FBA:fbapool1', 'format': 'ext3'}] self._smutclient.add_mdisks(userid, disk_list) add_mdisk.assert_any_call(userid, disk_list[0], '0100') add_mdisk.assert_any_call(userid, disk_list[1], '0101') @mock.patch.object(smutclient.SMUTClient, '_request') def test_dedicate_device(self, request): fake_userid = 'FakeID' vaddr = 'vaddr' raddr = 'raddr' mode = 1 requestData = "changevm FakeID dedicate vaddr raddr 1" request.return_value = {'overallRC': 0} self._smutclient.dedicate_device(fake_userid, vaddr, raddr, mode) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, '_request') def test_undedicate_device(self, request): fake_userid = 'FakeID' vaddr = 'vaddr' requestData = "changevm FakeID undedicate vaddr" request.return_value = {'overallRC': 0} self._smutclient.undedicate_device(fake_userid, vaddr) request.assert_called_once_with(requestData) @mock.patch.object(smutclient.SMUTClient, '_remove_mdisk') def test_remove_mdisks(self, remove_mdisk): userid = 'fakeuser' vdev_list = ['102', '103'] self._smutclient.remove_mdisks(userid, vdev_list) remove_mdisk.assert_any_call(userid, vdev_list[0]) remove_mdisk.assert_any_call(userid, vdev_list[1]) @mock.patch.object(smutclient.SMUTClient, 'image_performance_query') def test_get_image_performance_info(self, ipq): ipq.return_value = { u'FAKEVM': { 'used_memory': u'5222192 KB', 'used_cpu_time': u'25640530229 uS', 'guest_cpus': u'2', 'userid': u'FKAEVM', 'max_memory': u'8388608 KB'}} info = self._smutclient.get_image_performance_info('FAKEVM') self.assertEqual(info['used_memory'], '5222192 KB') @mock.patch.object(smutclient.SMUTClient, 'image_performance_query') def test_get_image_performance_info_not_exist(self, ipq): ipq.return_value = {} info = self._smutclient.get_image_performance_info('fakevm') self.assertEqual(info, None) def test_is_vdev_valid_true(self): vdev = '1009' vdev_info = ['1003', '1006'] result = self._smutclient._is_vdev_valid(vdev, vdev_info) self.assertEqual(result, True) def test_is_vdev_valid_False(self): vdev = '2002' vdev_info = ['2000', '2004'] result = self._smutclient._is_vdev_valid(vdev, vdev_info) self.assertEqual(result, False) @mock.patch.object(zvmutils, 'execute') @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_user_console_output(self, req, execu): req.return_value = self._generate_results(response=['cons: 0001 0002']) execu.side_effect = [(0, 'first line\n'), (0, 'second line\n')] cons_log = self._smutclient.get_user_console_output('fakeuser') req.assert_called_once_with('getvm fakeuser consoleoutput') execu.assert_any_call('sudo /usr/sbin/vmur re -t -O 0001') execu.assert_any_call('sudo /usr/sbin/vmur re -t -O 0002') self.assertEqual(cons_log, 'first line\nsecond line\n') @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_user_console_output_request_failed(self, req): req.side_effect = exception.SDKSMUTRequestFailed({}, 'err') self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient.get_user_console_output, 'fakeuser') @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_reboot(self, req): req.return_value = self._generate_results() self._smutclient.guest_reboot('fakeuser') req.assert_called_once_with('PowerVM fakeuser reboot') @mock.patch.object(smutclient.SMUTClient, '_request') def test_guest_reset(self, req): req.return_value = self._generate_results() self._smutclient.guest_reset('fakeuser') req.assert_called_once_with('PowerVM fakeuser reset') @mock.patch.object(smutclient.SMUTClient, '_request') def test_get_guest_connection_status(self, req): result = self._generate_results(rs=1, response=['testuid: reachable']) req.return_value = result is_reachable = self._smutclient.get_guest_connection_status('testuid') self.assertTrue(is_reachable) @mock.patch.object(database.NetworkDbOperator, 'switch_select_record') def test_get_nic_info(self, select): self._smutclient.get_nic_info(userid='testid', nic_id='fake_nic') select.assert_called_with(userid='testid', nic_id='fake_nic', vswitch=None) @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_guest_capture_get_capture_devices_rh7(self, execcmd): userid = 'fakeid' execcmd.side_effect = [['/dev/disk/by-path/ccw-0.0.0100-part1'], ['/dev/dasda1'], ['0.0.0100(ECKD) at ( 94: 0) is dasda' ' : active at blocksize: 4096,' ' 600840 blocks, 2347 MB']] result = self._smutclient._get_capture_devices(userid) self.assertEqual(result, ['0100']) @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_guest_capture_get_capture_devices_ubuntu(self, execcmd): userid = 'fakeid' execcmd.side_effect = [['UUID=8320ec9d-c2b5-439f-b0a0-cede08afe957' ' allow_lun_scan=0 crashkernel=128M' ' BOOT_IMAGE=0'], ['/dev/dasda1'], ['0.0.0100(ECKD) at ( 94: 0) is dasda' ' : active at blocksize: 4096,' ' 600840 blocks, 2347 MB']] result = self._smutclient._get_capture_devices(userid) self.assertEqual(result, ['0100']) @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_guest_capture_get_os_version_rh7(self, execcmd): userid = 'fakeid' execcmd.side_effect = [['/etc/os-release', '/etc/redhat-release', '/etc/system-release'], ['NAME="Red Hat Enterprise Linux Server"', 'VERSION="7.0 (Maipo)"', 'ID="rhel"', 'ID_LIKE="fedora"', 'VERSION_ID="7.0"', 'PRETTY_NAME="Red Hat Enterprise Linux' ' Server 7.0 (Maipo)"', 'ANSI_COLOR="0;31"', 'CPE_NAME="cpe:/o:redhat:enterprise_linux:' '7.0:GA:server"', 'HOME_URL="https://www.redhat.com/"']] result = self._smutclient._guest_get_os_version(userid) self.assertEqual(result, 'rhel7.0') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_guest_capture_get_os_version_rhel67_sles11(self, execcmd): userid = 'fakeid' execcmd.side_effect = [['/etc/redhat-release', '/etc/system-release'], ['Red Hat Enterprise Linux Server release 6.7' ' (Santiago)']] result = self._smutclient._guest_get_os_version(userid) self.assertEqual(result, 'rhel6.7') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_guest_capture_get_os_version_ubuntu(self, execcmd): userid = 'fakeid' execcmd.side_effect = [['/etc/lsb-release', '/etc/os-release'], ['NAME="Ubuntu"', 'VERSION="16.04 (Xenial Xerus)"', 'ID=ubuntu', 'ID_LIKE=debian', 'PRETTY_NAME="Ubuntu 16.04"', 'VERSION_ID="16.04"', 'HOME_URL="http://www.ubuntu.com/"', 'SUPPORT_URL="http://help.ubuntu.com/"', 'BUG_REPORT_URL="http://bugs.launchpad.net' '/ubuntu/"', 'UBUNTU_CODENAME=xenial']] result = self._smutclient._guest_get_os_version(userid) self.assertEqual(result, 'ubuntu16.04') @mock.patch.object(database.ImageDbOperator, 'image_add_record') @mock.patch.object(zvmutils.PathUtils, 'clean_temp_folder') @mock.patch.object(smutclient.SMUTClient, '_get_image_size') @mock.patch.object(smutclient.SMUTClient, '_get_disk_size_units') @mock.patch.object(smutclient.SMUTClient, '_get_md5sum') @mock.patch.object(zvmutils, 'execute') @mock.patch.object(zvmutils.PathUtils, 'mkdir_if_not_exist') @mock.patch.object(smutclient.SMUTClient, 'guest_softstop') @mock.patch.object(smutclient.SMUTClient, '_get_capture_devices') @mock.patch.object(smutclient.SMUTClient, '_guest_get_os_version') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_guest_capture_good_path(self, get_power_state, execcmd, get_os_version, get_capture_devices, softstop, mkdir, execute, md5sum, disk_size_units, imagesize, rm_folder, image_add_record): userid = 'fakeid' image_name = 'fakeimage' get_power_state.return_value = 'on' execcmd.return_value = ['/'] get_os_version.return_value = 'rhel7.0' get_capture_devices.return_value = ['0100'] image_temp_dir = '/'.join([CONF.image.sdk_image_repository, 'staging', 'rhel7.0', image_name]) image_file_path = '/'.join((image_temp_dir, '0100')) cmd1 = ['sudo', '/opt/zthin/bin/creatediskimage', userid, '0100', image_file_path, '--compression', '6'] execute.side_effect = [(0, ''), (0, '')] image_final_dir = '/'.join((CONF.image.sdk_image_repository, 'netboot', 'rhel7.0', image_name)) image_final_path = '/'.join((image_final_dir, '0100')) cmd2 = ['mv', image_file_path, image_final_path] md5sum.return_value = '547396211b558490d31e0de8e15eef0c' disk_size_units.return_value = '1000:CYL' imagesize.return_value = '1024000' self._smutclient.guest_capture(userid, image_name) get_power_state.assert_called_with(userid) execcmd.assert_called_once_with(userid, 'pwd') get_os_version.assert_called_once_with(userid) get_capture_devices.assert_called_once_with(userid, 'rootonly') softstop.assert_called_once_with(userid) execute.assert_has_calls([mock.call(cmd1), mock.call(cmd2)]) mkdir.assert_has_calls([mock.call(image_temp_dir)], [mock.call(image_final_dir)]) rm_folder.assert_called_once_with(image_temp_dir) md5sum.assert_called_once_with(image_final_path) disk_size_units.assert_called_once_with(image_final_path) imagesize.assert_called_once_with(image_final_path) image_add_record.assert_called_once_with(image_name, 'rhel7.0', '547396211b558490d31e0de8e15eef0c', '1000:CYL', '1024000', 'rootonly') @mock.patch.object(smutclient.SMUTClient, '_guest_get_os_version') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_guest_capture_error_path(self, get_power_state, execcmd, get_os_version): userid = 'fakeid' image_name = 'fakeimage' get_power_state.return_value = 'on' result = {'rs': 101, 'errno': 0, 'strError': '', 'overallRC': 2, 'rc': 4, 'response': ['(Error) ULTVMU0315E IUCV socket error' ' sending command to FP1T0006. cmd: pwd, ' 'rc: 4, rs: 101, out: ERROR: ERROR connecting' ' socket:', 'Network is unreachable', 'Return' ' code 4, Reason code 101.']} execcmd.side_effect = exception.SDKSMUTRequestFailed(result, 'err') self.assertRaises(exception.SDKGuestOperationError, self._smutclient.guest_capture, userid, image_name) get_power_state.assert_called_once_with(userid) execcmd.assert_called_once_with(userid, 'pwd') get_os_version.assert_not_called() @mock.patch.object(database.GuestDbOperator, 'get_guest_by_userid') def test_is_first_network_config_true(self, db_list): db_list.return_value = [u'9a5c9689-d099-46bb-865f-0c01c384f58c', u'TEST', u'', 0] result = self._smutclient.is_first_network_config('TEST') db_list.assert_called_once_with('TEST') self.assertTrue(result) @mock.patch.object(database.GuestDbOperator, 'get_guest_by_userid') def test_is_first_network_config_false(self, db_list): db_list.return_value = [u'9a5c9689-d099-46bb-865f-0c01c384f58c', u'TEST', u'', 1] result = self._smutclient.is_first_network_config('TEST') db_list.assert_called_once_with('TEST') self.assertFalse(result) @mock.patch.object(database.GuestDbOperator, 'update_guest_by_userid') def test_update_guestdb_with_net_set(self, update): self._smutclient.update_guestdb_with_net_set('TEST') update.assert_called_once_with('TEST', net_set='1') @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_query_vswitch_NotExist(self, req, get_id): get_id.return_value = "SMUTUSER" req.side_effect = exception.SDKSMUTRequestFailed( {'rc': 212, 'rs': 40}, 'err') self.assertRaises(exception.SDKObjectNotExistError, self._smutclient.query_vswitch, 'testvs') @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_query_vswitch_RequestFailed(self, req, get_id): get_id.return_value = "SMUTUSER" req.side_effect = exception.SDKSMUTRequestFailed( {'rc': 1, 'rs': 1}, 'err') self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient.query_vswitch, 'testvs') @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_query_OSA_RequestFailed(self, req, get_id): get_id.return_value = "SMUTUSER" req.side_effect = exception.SDKSMUTRequestFailed( {'rc': 1, 'rs': 1}, 'err') self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient._query_OSA) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_query_OSA_NoOSA(self, req, get_id): get_id.return_value = "SMUTUSER" req.side_effect = exception.SDKSMUTRequestFailed( {'rc': 4, 'rs': 4}, 'err') result = self._smutclient._query_OSA() get_id.assert_called_once_with() self.assertEqual(result, {}) @mock.patch.object(zvmutils, 'get_smut_userid') @mock.patch.object(smutclient.SMUTClient, '_request') def test_query_OSA(self, req, get_id): get_id.return_value = "SMUTUSER" osa_info = [ "OSA Address: 0440", "OSA Status: FREE", "OSA Type: OSA", "CHPID Address: 10", "Agent Status: NO", "OSA Address: 0441", "OSA Status: FREE", "OSA Type: OSA", "CHPID Address: 10", "Agent Status: NO", "OSA Address: 4000", "OSA Status: ATTACHED TCPIP", "OSA Type: OSA", "CHPID Address: 3B", "Agent Status: NO", "OSA Address: FB1D", "OSA Status: FREE", "OSA Type: HIPER", "CHPID Address: FB", "Agent Status: NO", ] req.return_value = {'response': osa_info} expected = {'OSA': {'FREE': ['0440', '0441'], 'BOXED': [], 'OFFLINE': [], 'ATTACHED': [('TCPIP', '4000')]}, 'HIPER': {'FREE': ['FB1D'], 'BOXED': [], 'OFFLINE': [], 'ATTACHED': []}} result = self._smutclient._query_OSA() get_id.assert_called_once_with() self.assertEqual(result.keys(), expected.keys()) self.assertEqual(result['OSA'], expected['OSA']) self.assertEqual(result['HIPER'], expected['HIPER']) @mock.patch.object(smutclient.SMUTClient, '_query_OSA') def test_is_OSA_free_noOSA(self, query_osa): query_osa.return_value = {'HIPER': {}} result = self._smutclient._is_OSA_free('0100') query_osa.assert_called_once_with() self.assertFalse(result) @mock.patch.object(smutclient.SMUTClient, '_query_OSA') def test_is_OSA_free_noFree(self, query_osa): query_osa.return_value = {'OSA': {'FREE': []}} result = self._smutclient._is_OSA_free('0100') query_osa.assert_called_once_with() self.assertFalse(result) @mock.patch.object(smutclient.SMUTClient, '_query_OSA') def test_is_OSA_free_notallFree(self, query_osa): query_osa.return_value = {'OSA': {'FREE': ['0100', '0101']}} result = self._smutclient._is_OSA_free('0100') query_osa.assert_called_once_with() self.assertFalse(result) @mock.patch.object(smutclient.SMUTClient, '_query_OSA') def test_is_OSA_free_OK_num(self, query_osa): query_osa.return_value = {'OSA': {'FREE': ['0100', '0101', '0102']}} result = self._smutclient._is_OSA_free('0100') query_osa.assert_called_once_with() self.assertTrue(result) @mock.patch.object(smutclient.SMUTClient, '_query_OSA') def test_is_OSA_free_OK_character(self, query_osa): query_osa.return_value = {'OSA': {'FREE': ['0AA0', '0AA1', '0AA2']}} result = self._smutclient._is_OSA_free('AA0') query_osa.assert_called_once_with() self.assertTrue(result) @mock.patch.object(smutclient.SMUTClient, '_get_available_vdev') @mock.patch.object(smutclient.SMUTClient, '_is_OSA_free') @mock.patch.object(smutclient.SMUTClient, '_dedicate_OSA') def test_dedicate_OSA(self, attach_osa, OSA_free, get_vdev): OSA_free.return_value = True get_vdev.return_value = '1000' result = self._smutclient.dedicate_OSA('userid', 'OSA_device', vdev='nic_vdev', active=True) get_vdev.assert_called_once_with('userid', vdev='nic_vdev') OSA_free.assert_called_once_with('OSA_device') attach_osa.assert_called_once_with('userid', 'OSA_device', '1000', active=True) self.assertEqual(result, '1000') @mock.patch.object(smutclient.SMUTClient, '_get_available_vdev') @mock.patch.object(smutclient.SMUTClient, '_is_OSA_free') def test_dedicate_OSA_notFree(self, OSA_free, get_vdev): OSA_free.return_value = False get_vdev.return_value = '1000' self.assertRaises(exception.SDKConflictError, self._smutclient.dedicate_OSA, 'userid', 'OSA_device', 'nic_vdev', active=True) @mock.patch.object(database.NetworkDbOperator, 'switch_add_record') @mock.patch.object(smutclient.SMUTClient, '_request') def test_private_dedicate_OSA_notActive(self, request, add_rec): request_response = ['', '', '', '', '', ''] request.side_effect = request_response self._smutclient._dedicate_OSA('userid', 'f000', '1000', active=False) request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1002 -r f002") add_rec.assert_called_once_with('userid', '1000', comments='OSA=f000') @mock.patch.object(smutclient.SMUTClient, '_request') def test_private_dedicate_OSA_notActive_Fail_Input(self, request): request_response = ['', ''] request_response.append(exception.SDKSMUTRequestFailed( {'rc': 404, 'rs': 4}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 1, 'rs': 1}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 404, 'rs': 8}, 'err')) request.side_effect = request_response self.assertRaises(exception.SDKConflictError, self._smutclient._dedicate_OSA, 'userid', 'f000', '1000', active=False) request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1001") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1000") @mock.patch.object(smutclient.SMUTClient, '_request') def test_private_dedicate_OSA_notActive_Fail_Lock(self, request): request_response = ['', ''] request_response.append(exception.SDKSMUTRequestFailed( {'rc': 404, 'rs': 12}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 1, 'rs': 1}, 'err')) request_response.append('') request.side_effect = request_response self.assertRaises(exception.SDKConflictError, self._smutclient._dedicate_OSA, 'userid', 'f000', '1000', active=False) request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1001") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1000") @mock.patch.object(database.NetworkDbOperator, 'switch_add_record') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_private_dedicate_OSA_Active(self, power_state, request, add_rec): power_state.return_value = 'on' request_response = ['', '', '', '', '', ''] request.side_effect = request_response self._smutclient._dedicate_OSA('userid', 'f000', '1000', active=True) request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1002 -r f002") add_rec.assert_called_once_with('userid', '1000', comments='OSA=f000') @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_private_dedicate_OSA_Active_Fail(self, power_state, request): power_state.return_value = 'on' request_response = ['', '', '', '', ''] request_response.append(exception.SDKSMUTRequestFailed( {'rc': 300, 'rs': 0}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 404, 'rs': 8}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 400, 'rs': 8}, 'err')) request_response.append('') request_response.append(exception.SDKSMUTRequestFailed( {'rc': 204, 'rs': 8}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 200, 'rs': 8}, 'err')) request.side_effect = request_response self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient._dedicate_OSA, 'userid', 'f000', '1000', active=True) request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1000") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1001") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1002") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Undedicate ' "--operands -v 1000") request.assert_any_call('SMAPI userid API Image_Device_Undedicate ' "--operands -v 1001") @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'get_power_state') def test_private_dedicate_OSA_Active_Fail_Input(self, power, request): power.return_value = 'on' request_response = ['', '', '', '', ''] request_response.append(exception.SDKSMUTRequestFailed( {'rc': 204, 'rs': 8}, 'err')) request_response.append('') request_response.append('') request_response.append('') request_response.append(exception.SDKSMUTRequestFailed( {'rc': 204, 'rs': 8}, 'err')) request_response.append(exception.SDKSMUTRequestFailed( {'rc': 200, 'rs': 8}, 'err')) request.side_effect = request_response self.assertRaises(exception.SDKConflictError, self._smutclient._dedicate_OSA, 'userid', 'f000', '1000', active=True) request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate_DM ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1000") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1001") request.assert_any_call('SMAPI userid API Image_Device_Undedicate_DM ' "--operands -v 1002") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1000 -r f000") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1001 -r f001") request.assert_any_call('SMAPI userid API Image_Device_Dedicate ' "--operands -v 1002 -r f002") request.assert_any_call('SMAPI userid API Image_Device_Undedicate ' "--operands -v 1000") request.assert_any_call('SMAPI userid API Image_Device_Undedicate ' "--operands -v 1001") @mock.patch.object(smutclient.SMUTClient, '_request_with_error_ignored') def test_namelist_add(self, req): self._smutclient.namelist_add('tnlist', 'testid') rd = "SMAPI tnlist API Name_List_Add --operands -n testid" req.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request_with_error_ignored') def test_namelist_remove(self, req): self._smutclient.namelist_remove('tnlist', 'testid') rd = "SMAPI tnlist API Name_List_Remove --operands -n testid" req.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request_with_error_ignored') def test_namelist_query(self, req): req.return_value = {'response': ['t1', 't2']} resp = self._smutclient.namelist_query('tnlist') rd = "SMAPI tnlist API Name_List_Query" req.assert_called_once_with(rd) self.assertEqual(['t1', 't2'], resp) @mock.patch.object(smutclient.SMUTClient, '_request') def test_namelist_query_err(self, req): req.side_effect = exception.SDKSMUTRequestFailed({}, 'err') resp = self._smutclient.namelist_query('tnlist') rd = "SMAPI tnlist API Name_List_Query" req.assert_called_once_with(rd) self.assertEqual([], resp) @mock.patch.object(smutclient.SMUTClient, '_request_with_error_ignored') def test_namelist_destroy(self, req): self._smutclient.namelist_destroy('tnlist') rd = "SMAPI tnlist API Name_List_Destroy" req.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, 'get_user_direct') def test_private_get_defined_cpu_addrs(self, get_user_direct): get_user_direct.return_value = ['USER TESTUID LBYONLY 1024m 64G G', 'INCLUDE OSDFLT', 'CPU 00 BASE', 'CPU 0A', 'IPL 0100', 'MACHINE ESA 32', 'NICDEF 1000 TYPE QDIO LAN ' 'SYSTEM XCATVSW2 DEVICES 3', 'MDISK 0100 3390 52509 1100 OMB1AB MR', ''] (max_cpus, defined_addrs) = self._smutclient._get_defined_cpu_addrs( 'TESTUID') get_user_direct.assert_called_once_with('TESTUID') self.assertEqual(max_cpus, 32) self.assertEqual(defined_addrs, ['00', '0A']) @mock.patch.object(smutclient.SMUTClient, 'get_user_direct') def test_private_get_defined_cpu_addrs_no_max_cpu(self, get_user_direct): get_user_direct.return_value = ['USER TESTUID LBYONLY 1024m 64G G', 'INCLUDE OSDFLT', 'CPU 00 BASE', 'CPU 0A', 'IPL 0100', 'NICDEF 1000 TYPE QDIO LAN ' 'SYSTEM XCATVSW2 DEVICES 3', 'MDISK 0100 3390 52509 1100 OMB1AB MR', ''] (max_cpus, defined_addrs) = self._smutclient._get_defined_cpu_addrs( 'TESTUID') get_user_direct.assert_called_once_with('TESTUID') self.assertEqual(max_cpus, 0) self.assertEqual(defined_addrs, ['00', '0A']) def test_private_get_available_cpu_addrs(self): used = ['00', '01', '1A', '1F'] max = 32 avail_expected = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1B', '1C', '1D', '1E'] avail_addrs = self._smutclient._get_available_cpu_addrs(used, max) avail_addrs.sort() self.assertListEqual(avail_addrs, avail_expected) @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_private_get_active_cpu_addrs(self, exec_cmd): active_cpus = [('# The following is the parsable format, which can ' 'be fed to other'), ('# programs. Each different item in every column has ' 'an unique ID'), '# starting from zero.', '# Address', '0', '3', '10', '19'] exec_cmd.return_value = active_cpus addrs = self._smutclient._get_active_cpu_addrs('TESTUID') exec_cmd.assert_called_once_with('TESTUID', "lscpu --parse=ADDRESS") addrs.sort() self.assertListEqual(addrs, ['00', '03', '0A', '13']) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01'] resize.return_value = (1, ['02', '03'], 32) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst self._smutclient.live_resize_cpus(userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_called_once_with(['00', '01'], 32) cmd_def_cpu = "vmcp def cpu 02 03" cmd_rescan_cpu = "chcpu -r" exec_cmd.assert_has_calls([mock.call(userid, cmd_def_cpu), mock.call(userid, cmd_rescan_cpu)]) request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_equal_active(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01', '02', '03'] resize.return_value = (1, ['02', '03'], 32) self._smutclient.live_resize_cpus(userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_not_called() exec_cmd.assert_not_called() request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_less_active(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01', '02', '03', '04'] self.assertRaises(exception.SDKConflictError, self._smutclient.live_resize_cpus, userid, count) get_active.assert_called_once_with(userid) resize.assert_not_called() get_avail.assert_not_called() exec_cmd.assert_not_called() request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_revert_definition_equal(self, get_active, resize, get_avail, exec_cmd, request): # Test case: active update failed, definition not updated userid = 'testuid' count = 4 get_active.return_value = ['00', '01'] resize.return_value = (0, [], 32) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst exec_cmd.side_effect = [exception.SDKSMUTRequestFailed({}, 'err'), ""] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_cpus, userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_called_once_with(['00', '01'], 32) exec_cmd.assert_called_once_with(userid, "vmcp def cpu 02 03") request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_revert_added_cpus(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01'] resize.return_value = (1, ['01', '02', '03'], 32) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst exec_cmd.side_effect = [exception.SDKSMUTRequestFailed({}, 'err'), ""] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_cpus, userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_called_once_with(['00', '01'], 32) exec_cmd.assert_called_once_with(userid, "vmcp def cpu 02 03") rd = ("SMAPI testuid API Image_Definition_Delete_DM --operands " "-k CPU=CPUADDR=01 -k CPU=CPUADDR=02 -k CPU=CPUADDR=03") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_revert_deleted_cpus(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01'] resize.return_value = (2, ['04', '0A'], 32) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst exec_cmd.side_effect = [exception.SDKSMUTRequestFailed({}, 'err'), ""] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_cpus, userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_called_once_with(['00', '01'], 32) exec_cmd.assert_called_once_with(userid, "vmcp def cpu 02 03") rd = ("SMAPI testuid API Image_Definition_Create_DM --operands " "-k CPU=CPUADDR=04 -k CPU=CPUADDR=0A") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_revert_failed(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01'] resize.return_value = (2, ['04', '0A'], 32) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst exec_cmd.side_effect = [exception.SDKSMUTRequestFailed({}, 'err'), ""] request.side_effect = [exception.SDKSMUTRequestFailed({}, 'err'), ""] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_cpus, userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_called_once_with(['00', '01'], 32) exec_cmd.assert_called_once_with(userid, "vmcp def cpu 02 03") rd = ("SMAPI testuid API Image_Definition_Create_DM --operands " "-k CPU=CPUADDR=04 -k CPU=CPUADDR=0A") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, 'resize_cpus') @mock.patch.object(smutclient.SMUTClient, '_get_active_cpu_addrs') def test_live_resize_cpus_rescan_failed(self, get_active, resize, get_avail, exec_cmd, request): userid = 'testuid' count = 4 get_active.return_value = ['00', '01'] resize.return_value = (2, ['04', '0A'], 32) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst exec_cmd.side_effect = ["", exception.SDKSMUTRequestFailed({}, 'err')] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_cpus, userid, count) get_active.assert_called_once_with(userid) resize.assert_called_once_with(userid, count) get_avail.assert_called_once_with(['00', '01'], 32) cmd_def_cpu = "vmcp def cpu 02 03" cmd_rescan_cpu = "chcpu -r" exec_cmd.assert_has_calls([mock.call(userid, cmd_def_cpu), mock.call(userid, cmd_rescan_cpu)]) request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_equal_count(self, get_defined, get_avail, request): userid = 'testuid' count = 2 get_defined.return_value = (32, ['00', '01']) return_data = self._smutclient.resize_cpus(userid, count) self.assertTupleEqual(return_data, (0, [], 32)) get_defined.assert_called_once_with(userid) get_avail.assert_not_called() request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_add(self, get_defined, get_avail, request): userid = 'testuid' count = 4 get_defined.return_value = (32, ['00', '01']) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst return_data = self._smutclient.resize_cpus(userid, count) self.assertTupleEqual(return_data, (1, ['02', '03'], 32)) get_defined.assert_called_once_with(userid) get_avail.assert_called_once_with(['00', '01'], 32) rd = ("SMAPI testuid API Image_Definition_Update_DM --operands " "-k CPU=CPUADDR=02 -k CPU=CPUADDR=03") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_delete(self, get_defined, get_avail, request): userid = 'testuid' count = 4 get_defined.return_value = (32, ['00', '1A', '02', '01', '11', '10']) return_data = self._smutclient.resize_cpus(userid, count) self.assertTupleEqual(return_data, (2, ['11', '1A'], 32)) get_defined.assert_called_once_with(userid) get_avail.assert_not_called() rd = ("SMAPI testuid API Image_Definition_Delete_DM --operands " "-k CPU=CPUADDR=11 -k CPU=CPUADDR=1A") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_max_not_defined(self, get_defined, get_avail, request): userid = 'testuid' count = 4 get_defined.return_value = (0, ['00', '01']) self.assertRaises(exception.SDKConflictError, self._smutclient.resize_cpus, userid, count) get_defined.assert_called_once_with(userid) get_avail.assert_not_called() request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_req_exceeds_max(self, get_defined, get_avail, request): userid = 'testuid' count = 40 get_defined.return_value = (32, ['00', '01']) self.assertRaises(exception.SDKConflictError, self._smutclient.resize_cpus, userid, count) get_defined.assert_called_once_with(userid) get_avail.assert_not_called() request.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_add_failed(self, get_defined, get_avail, request): userid = 'testuid' count = 4 get_defined.return_value = (32, ['00', '01']) avail_lst = ['02', '03', '04', '05', '06', '07', '08', '09', '0A', '0B', '0C', '0D', '0E', '0F', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '1A', '1B', '1C', '1D', '1E', '1F'] get_avail.return_value = avail_lst request.side_effect = exception.SDKSMUTRequestFailed({}, 'err') self.assertRaises(exception.SDKGuestOperationError, self._smutclient.resize_cpus, userid, count) get_defined.assert_called_once_with(userid) get_avail.assert_called_once_with(['00', '01'], 32) rd = ("SMAPI testuid API Image_Definition_Update_DM --operands " "-k CPU=CPUADDR=02 -k CPU=CPUADDR=03") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_request') @mock.patch.object(smutclient.SMUTClient, '_get_available_cpu_addrs') @mock.patch.object(smutclient.SMUTClient, '_get_defined_cpu_addrs') def test_resize_cpus_delete_failed(self, get_defined, get_avail, request): userid = 'testuid' count = 4 get_defined.return_value = (32, ['00', '01', '02', '03', '04', '05']) request.side_effect = exception.SDKSMUTRequestFailed({}, 'err') self.assertRaises(exception.SDKGuestOperationError, self._smutclient.resize_cpus, userid, count) get_defined.assert_called_once_with(userid) get_avail.assert_not_called() rd = ("SMAPI testuid API Image_Definition_Delete_DM --operands " "-k CPU=CPUADDR=04 -k CPU=CPUADDR=05") request.assert_called_once_with(rd) @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_reserved_not_defined(self, replace_def, get_defined): userid = 'testuid' size = '2g' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_defined.return_value = (4096, 65536, -1, sample_definition) self.assertRaises(exception.SDKConflictError, self._smutclient.resize_memory, userid, size) replace_def.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_exceed_max_size(self, replace_def, get_defined): userid = 'testuid' size = '65g' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_defined.return_value = (4096, 65536, 61440, sample_definition) self.assertRaises(exception.SDKConflictError, self._smutclient.resize_memory, userid, size) replace_def.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_equal_size(self, replace_def, get_defined): userid = 'testuid' size = '4g' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_defined.return_value = (4096, 65536, 61440, sample_definition) (action, defined_mem, max_mem, user_direct) = \ self._smutclient.resize_memory(userid, size) self.assertEqual(action, 0) replace_def.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_lock_user_direct') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_increase(self, replace_def, lock_def, get_def): userid = 'testuid' size = '10240M' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_def.return_value = (4096, 65536, 61440, sample_definition) (action, defined_mem, max_mem, user_direct) = \ self._smutclient.resize_memory(userid, size) self.assertEqual(action, 1) get_def.assert_called_once_with(userid) lock_def.assert_called_once_with(userid) new_entry = ("USER TESTUID LBYONLY 10240M 64G G\n" "INCLUDE OSDFLT\n" "COMMAND DEF STOR RESERVED 55296M\n" "CPU 00 BASE\n" "IPL 0100\n" "MDISK 0100 3390 5501 5500 OMB1BA MR\n") replace_def.assert_called_once_with(userid, new_entry) @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_lock_user_direct') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_decrease(self, replace_def, lock_def, get_def): userid = 'testuid' size = '2g' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_def.return_value = (4096, 65536, 61440, sample_definition) (action, defined_mem, max_mem, user_direct) = \ self._smutclient.resize_memory(userid, size) self.assertEqual(action, 1) get_def.assert_called_once_with(userid) lock_def.assert_called_once_with(userid) new_entry = ("USER TESTUID LBYONLY 2048M 64G G\n" "INCLUDE OSDFLT\n" "COMMAND DEF STOR RESERVED 63488M\n" "CPU 00 BASE\n" "IPL 0100\n" "MDISK 0100 3390 5501 5500 OMB1BA MR\n") replace_def.assert_called_once_with(userid, new_entry) @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_lock_user_direct') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_lock_failed(self, replace_def, lock_def, get_def): userid = 'testuid' size = '2g' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_def.return_value = (4096, 65536, 61440, sample_definition) lock_def.side_effect = exception.SDKSMUTRequestFailed({}, 'err') self.assertRaises(exception.SDKGuestOperationError, self._smutclient.resize_memory, userid, size) get_def.assert_called_once_with(userid) lock_def.assert_called_once_with(userid) replace_def.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_defined_memory') @mock.patch.object(smutclient.SMUTClient, '_lock_user_direct') @mock.patch.object(smutclient.SMUTClient, '_replace_user_direct') def test_resize_memory_replace_failed(self, replace_def, lock_def, get_def): userid = 'testuid' size = '2g' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_def.return_value = (4096, 65536, 61440, sample_definition) replace_def.side_effect = exception.SDKSMUTRequestFailed({}, 'err') self.assertRaises(exception.SDKGuestOperationError, self._smutclient.resize_memory, userid, size) get_def.assert_called_once_with(userid) lock_def.assert_called_once_with(userid) new_entry = ("USER TESTUID LBYONLY 2048M 64G G\n" "INCLUDE OSDFLT\n" "COMMAND DEF STOR RESERVED 63488M\n" "CPU 00 BASE\n" "IPL 0100\n" "MDISK 0100 3390 5501 5500 OMB1BA MR\n") replace_def.assert_called_once_with(userid, new_entry) @mock.patch.object(smutclient.SMUTClient, 'get_user_direct') def test_get_defined_memory(self, get_user_direct): userid = 'testuid' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_user_direct.return_value = sample_definition (defined_mem, max_mem, reserved_mem, user_direct) = \ self._smutclient._get_defined_memory(userid) self.assertEqual(defined_mem, 4096) self.assertEqual(max_mem, 65536) self.assertEqual(reserved_mem, 61440) self.assertListEqual(user_direct, sample_definition) @mock.patch.object(smutclient.SMUTClient, 'get_user_direct') def test_get_defined_memory_reserved_not_defined(self, get_user_direct): userid = 'testuid' sample_definition = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] get_user_direct.return_value = sample_definition (defined_mem, max_mem, reserved_mem, user_direct) = \ self._smutclient._get_defined_memory(userid) self.assertEqual(defined_mem, 4096) self.assertEqual(max_mem, 65536) self.assertEqual(reserved_mem, -1) self.assertListEqual(user_direct, sample_definition) @mock.patch.object(smutclient.SMUTClient, '_request') def test_replace_user_direct_err(self, req): userid = 'testuid' user_entry = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] req.side_effect = [exception.SDKSMUTRequestFailed({}, 'err'), ""] self.assertRaises(exception.SDKSMUTRequestFailed, self._smutclient._replace_user_direct, userid, user_entry) @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_get_active_memory(self, execute_cmd): userid = 'testuid' sample_lsmem = [u'Address Range Size (MB) \ State Removable Device', u'==================================================\ =============================', u'0x0000000000000000-0x000000000fffffff 256 \ online no 0-1', u'0x0000000010000000-0x000000003fffffff 768 \ online yes 2-7', u'0x0000000040000000-0x000000007fffffff 1024 \ online no 8-15', u'0x0000000080000000-0x00000000ffffffff 2048 \ online yes 16-31', u'0x0000000100000000-0x0000000fffffffff 61440 \ offline - 32-511', u'', u'Memory device size : 128 MB', u'Memory block size : 256 MB', u'Total online memory : 4096 MB', u'Total offline memory: 61440 MB' ] execute_cmd.return_value = sample_lsmem active_mem = self._smutclient._get_active_memory(userid) self.assertEqual(active_mem, 4096) @mock.patch.object(smutclient.SMUTClient, '_get_active_memory') @mock.patch.object(smutclient.SMUTClient, 'resize_memory') def test_live_resize_memory_less(self, resize_mem, get_active_mem): userid = 'testuid' req_mem = "1g" get_active_mem.return_value = 2048 self.assertRaises(exception.SDKConflictError, self._smutclient.live_resize_memory, userid, req_mem) resize_mem.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_active_memory') @mock.patch.object(smutclient.SMUTClient, 'resize_memory') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') def test_live_resize_memory_equal(self, exec_cmd, resize_mem, get_active_mem): userid = 'testuid' req_mem = "2g" get_active_mem.return_value = 2048 resize_mem.return_value = (1, 2048, 65536, []) self._smutclient.live_resize_memory(userid, req_mem) resize_mem.assert_called_once_with(userid, req_mem) exec_cmd.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_active_memory') @mock.patch.object(smutclient.SMUTClient, 'resize_memory') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_revert_user_direct') def test_live_resize_memory_more(self, revert, exec_cmd, resize_mem, get_active_mem): userid = 'testuid' req_mem = "4096m" get_active_mem.return_value = 2048 resize_mem.return_value = (1, 2048, 65536, []) exec_cmd.side_effect = ['', ''] self._smutclient.live_resize_memory(userid, req_mem) resize_mem.assert_called_once_with(userid, req_mem) def_standby_cmd = "vmcp def storage standby 2048M" online_mem_cmd = "chmem -e 2048M" exec_cmd.assert_has_calls([mock.call(userid, def_standby_cmd), mock.call(userid, online_mem_cmd)]) revert.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_active_memory') @mock.patch.object(smutclient.SMUTClient, 'resize_memory') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_revert_user_direct') def test_live_resize_memory_standby_failed(self, revert, exec_cmd, resize_mem, get_active_mem): userid = 'testuid' req_mem = "4096m" get_active_mem.return_value = 2048 sample_direct = [u'USER TESTUID LBYONLY 2048M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] resize_mem.return_value = (1, 2048, 65536, sample_direct) exec_cmd.side_effect = exception.SDKSMUTRequestFailed({}, 'fake err') self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_memory, userid, req_mem) resize_mem.assert_called_once_with(userid, req_mem) def_standby_cmd = "vmcp def storage standby 2048M" exec_cmd.assert_called_with(userid, def_standby_cmd) revert.assert_called_once_with(userid, sample_direct) @mock.patch.object(smutclient.SMUTClient, '_get_active_memory') @mock.patch.object(smutclient.SMUTClient, 'resize_memory') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_revert_user_direct') def test_live_resize_memory_standby_failed_no_revert(self, revert, exec_cmd, resize_mem, get_active_mem): userid = 'testuid' req_mem = "4096m" get_active_mem.return_value = 2048 sample_direct = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] resize_mem.return_value = (0, 4096, 65536, sample_direct) exec_cmd.side_effect = [exception.SDKSMUTRequestFailed({}, 'fake err'), ''] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_memory, userid, req_mem) resize_mem.assert_called_once_with(userid, req_mem) def_standby_cmd = "vmcp def storage standby 2048M" exec_cmd.assert_called_with(userid, def_standby_cmd) revert.assert_not_called() @mock.patch.object(smutclient.SMUTClient, '_get_active_memory') @mock.patch.object(smutclient.SMUTClient, 'resize_memory') @mock.patch.object(smutclient.SMUTClient, 'execute_cmd') @mock.patch.object(smutclient.SMUTClient, '_revert_user_direct') def test_live_resize_memory_online_failed(self, revert, exec_cmd, resize_mem, get_active_mem): userid = 'testuid' req_mem = "4096m" get_active_mem.return_value = 2048 sample_direct = [u'USER TESTUID LBYONLY 4096M 64G G', u'INCLUDE OSDFLT', u'COMMAND DEF STOR RESERVED 61440M', u'CPU 00 BASE', u'IPL 0100', u'MDISK 0100 3390 5501 5500 OMB1BA MR', u''] resize_mem.return_value = (1, 4096, 65536, sample_direct) exec_cmd.side_effect = ['', exception.SDKSMUTRequestFailed({}, 'fake err'), ''] self.assertRaises(exception.SDKGuestOperationError, self._smutclient.live_resize_memory, userid, req_mem) resize_mem.assert_called_once_with(userid, req_mem) def_standby_cmd = "vmcp def storage standby 2048M" online_mem_cmd = "chmem -e 2048M" revert_standby_cmd = "vmcp def storage standby 0M" exec_cmd.assert_has_calls([mock.call(userid, def_standby_cmd), mock.call(userid, online_mem_cmd), mock.call(userid, revert_standby_cmd)]) revert.assert_called_once_with(userid, sample_direct) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_utils.py0000664000175000017510000000263713442676317022753 0ustar ruirui00000000000000# 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. import mock import zvmsdk.utils as zvmutils from zvmsdk.tests.unit import base class ZVMUtilsTestCases(base.SDKTestCase): def test_convert_to_mb(self): self.assertEqual(2355.2, zvmutils.convert_to_mb('2.3G')) self.assertEqual(20, zvmutils.convert_to_mb('20M')) self.assertEqual(1153433.6, zvmutils.convert_to_mb('1.1T')) @mock.patch.object(zvmutils, 'get_smut_userid') def test_get_namelist(self, gsu): gsu.return_value = 'TUID' self.assertEqual('TSTNLIST', zvmutils.get_namelist()) base.set_conf('zvm', 'namelist', None) gsu.return_value = 'TUID' self.assertEqual('NL00TUID', zvmutils.get_namelist()) gsu.return_value = 'TESTUSER' self.assertEqual('NLSTUSER', zvmutils.get_namelist()) base.set_conf('zvm', 'namelist', 'TSTNLIST') zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_config.py0000664000175000017510000000427013371225174023043 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from zvmsdk import config from zvmsdk.tests.unit import base CONFOPTS = config.CONFOPTS class ZVMConfigTestCases(base.SDKTestCase): def test_check_zvm_disk_pool_fba(self): CONFOPTS._check_zvm_disk_pool('fba:pool1') def test_check_zvm_disk_pool_eckd(self): CONFOPTS._check_zvm_disk_pool('eCKD:pool1') def test_check_zvm_disk_pool_err1(self): self.assertRaises(config.OptFormatError, CONFOPTS._check_zvm_disk_pool, 'fbapool1') def test_check_zvm_disk_pool_err2(self): self.assertRaises(config.OptFormatError, CONFOPTS._check_zvm_disk_pool, 'ECKD:') def test_check_user_default_max_memory(self): CONFOPTS._check_user_default_max_memory('30G') CONFOPTS._check_user_default_max_memory('1234M') def test_check_user_default_max_memory_err1(self): self.assertRaises(config.OptFormatError, CONFOPTS._check_user_default_max_memory, '12.0G') def test_check_user_default_max_memory_err2(self): self.assertRaises(config.OptFormatError, CONFOPTS._check_user_default_max_memory, '12') def test_check_user_default_max_memory_err3(self): self.assertRaises(config.OptFormatError, CONFOPTS._check_user_default_max_memory, '12345M') def test_check_user_default_max_cpu(self): CONFOPTS._check_user_default_max_cpu(1) def test_check_user_default_max_cpu_err(self): self.assertRaises(config.OptFormatError, CONFOPTS._check_user_default_max_cpu, 65) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_imageops.py0000664000175000017510000000471613442676317023417 0ustar ruirui00000000000000# 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. import mock from zvmsdk import config from zvmsdk import imageops from zvmsdk import utils as zvmutils from zvmsdk.tests.unit import base CONF = config.CONF class SDKImageOpsTestCase(base.SDKTestCase): def setUp(self): self._image_ops = imageops.get_imageops() self._pathutil = zvmutils.PathUtils() @mock.patch("zvmsdk.smutclient.SMUTClient.image_import") def test_image_import(self, image_import): image_name = '95a4da37-9f9b-4fb2-841f-f0bb441b7544' url = 'file:///path/to/image/file' image_meta = {'os_version': 'rhel7.2', 'md5sum': 'e34166f61130fc9221415d76298d7987'} remote_host = 'image@192.168.99.1' self._image_ops.image_import(image_name, url, image_meta, remote_host) image_import.assert_called_once_with(image_name, url, image_meta, remote_host) @mock.patch("zvmsdk.smutclient.SMUTClient.image_query") def test_image_query(self, image_query): imagekeyword = 'eae09a9f_7958_4024_a58c_83d3b2fc0aab' self._image_ops.image_query(imagekeyword) image_query.assert_called_once_with(imagekeyword) @mock.patch("zvmsdk.smutclient.SMUTClient.image_delete") def test_image_delete(self, image_delete): image_name = 'eae09a9f_7958_4024_a58c_83d3b2fc0aab' self._image_ops.image_delete(image_name) image_delete.assert_called_once_with(image_name) @mock.patch("zvmsdk.smutclient.SMUTClient.image_export") def test_image_export(self, image_export): image_name = 'testimage' dest_url = 'file:///path/to/export/image' self._image_ops.image_export(image_name, dest_url) image_export.assert_called_once_with(image_name, dest_url, None) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_monitor.py0000664000175000017510000005414513442676317023303 0ustar ruirui00000000000000# 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. import mock from zvmsdk import monitor from zvmsdk.tests.unit import base CPUMEM_SAMPLE1 = { 'userid': 'USERID1', 'guest_cpus': '1', 'used_cpu_time': '6185838 uS', 'elapsed_cpu_time': '35232895 uS', 'min_cpu_count': '2', 'max_cpu_limit': '10000', 'samples_cpu_in_use': '0', 'samples_cpu_delay': '0', 'used_memory': '290232 KB', 'max_memory': '2097152 KB', 'min_memory': '0 KB', 'shared_memory': '5222192 KB', } CPUMEM_SAMPLE2 = { 'userid': 'USERID2', 'guest_cpus': '3', 'used_cpu_time': '14293629 uS', 'elapsed_cpu_time': '4868976371 uS', 'min_cpu_count': '3', 'max_cpu_limit': '10000', 'samples_cpu_in_use': '0', 'samples_cpu_delay': '0', 'used_memory': '305020 KB', 'max_memory': '2097152 KB', 'min_memory': '0 KB', 'shared_memory': '5222190 KB', } MEM_KEYS = ['used_mem_kb', 'max_mem_kb', 'min_mem_kb', 'shared_mem_kb'] SMCLI_VSW_NIC_DATA = {'vswitches': [ {'vswitch_name': 'TESTVSW1', 'nics': [ {'nic_fr_rx_dsc': '0', 'nic_fr_rx_err': '0', 'nic_fr_tx_err': '4', 'userid': 'USERID1', 'nic_rx': '103024058', 'nic_fr_rx': '573952', 'nic_fr_tx': '548780', 'vdev': '0600', 'nic_fr_tx_dsc': '0', 'nic_tx': '102030890'}, {'nic_fr_rx_dsc': '0', 'nic_fr_rx_err': '0', 'nic_fr_tx_err': '4', 'userid': 'USERID2', 'nic_rx': '3111714', 'nic_fr_rx': '17493', 'nic_fr_tx': '16886', 'vdev': '0600', 'nic_fr_tx_dsc': '0', 'nic_tx': '3172646'}]}, {'vswitch_name': 'TESTVSW2', 'nics': [ {'nic_fr_rx_dsc': '0', 'nic_fr_rx_err': '0', 'nic_fr_tx_err': '0', 'userid': 'USERID1', 'nic_rx': '4684435', 'nic_fr_rx': '34958', 'nic_fr_tx': '16211', 'vdev': '1000', 'nic_fr_tx_dsc': '0', 'nic_tx': '3316601'}, {'nic_fr_rx_dsc': '0', 'nic_fr_rx_err': '0', 'nic_fr_tx_err': '0', 'userid': 'USERID2', 'nic_rx': '3577163', 'nic_fr_rx': '27211', 'nic_fr_tx': '12344', 'vdev': '1000', 'nic_fr_tx_dsc': '0', 'nic_tx': '2515045'}]}], 'vswitch_count': 2} INST_NICS_SAMPLE1 = [ {'nic_fr_rx': 573952, 'nic_fr_rx_dsc': 0, 'nic_fr_rx_err': 0, 'nic_fr_tx': 548780, 'nic_fr_tx_dsc': 0, 'nic_fr_tx_err': 4, 'nic_rx': 103024058, 'nic_tx': 102030890, 'nic_vdev': '0600', 'vswitch_name': 'TESTVSW1'}, {'nic_fr_rx': 34958, 'nic_fr_rx_dsc': 0, 'nic_fr_rx_err': 0, 'nic_fr_tx': 16211, 'nic_fr_tx_dsc': 0, 'nic_fr_tx_err': 0, 'nic_rx': 4684435, 'nic_tx': 3316601, 'nic_vdev': '1000', 'vswitch_name': 'TESTVSW2'} ] INST_NICS_SAMPLE2 = [ {'nic_fr_rx': 17493, 'nic_fr_rx_dsc': 0, 'nic_fr_rx_err': 0, 'nic_fr_tx': 16886, 'nic_fr_tx_dsc': 0, 'nic_fr_tx_err': 4, 'nic_rx': 3111714, 'nic_tx': 3172646, 'nic_vdev': '0600', 'vswitch_name': 'TESTVSW1'}, {'nic_fr_rx': 27211, 'nic_fr_rx_dsc': 0, 'nic_fr_rx_err': 0, 'nic_fr_tx': 12344, 'nic_fr_tx_dsc': 0, 'nic_fr_tx_err': 0, 'nic_rx': 3577163, 'nic_tx': 2515045, 'nic_vdev': '1000', 'vswitch_name': 'TESTVSW2'} ] class SDKMonitorTestCase(base.SDKTestCase): def setUp(self): self._monitor = monitor.ZVMMonitor() @mock.patch("zvmsdk.monitor.MeteringCache.get") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") def test_private_get_inspect_data_cache_hit_single(self, cache_enabled, get_ps, cache_get): cache_get.return_value = CPUMEM_SAMPLE1 rdata = self._monitor._get_inspect_data('cpumem', ['USERID1']) self.assertEqual(list(rdata.keys()), ['USERID1']) self.assertEqual(sorted(list(rdata['USERID1'].keys())), sorted(CPUMEM_SAMPLE1.keys())) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') self.assertEqual(rdata['USERID1']['shared_memory'], '5222192 KB') get_ps.assert_not_called() cache_enabled.assert_not_called() @mock.patch("zvmsdk.monitor.MeteringCache.get") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") def test_private_get_inspect_data_cache_hit_multi(self, cache_enabled, get_ps, cache_get): cache_get.side_effect = [CPUMEM_SAMPLE1, CPUMEM_SAMPLE2] rdata = self._monitor._get_inspect_data('cpumem', ['USERID1', 'USERID2']) self.assertEqual(sorted(rdata.keys()), ['USERID1', 'USERID2']) self.assertEqual(sorted(rdata['USERID1'].keys()), sorted(CPUMEM_SAMPLE1.keys())) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') self.assertEqual(rdata['USERID1']['shared_memory'], '5222192 KB') self.assertEqual(rdata['USERID2']['guest_cpus'], '3') self.assertEqual(rdata['USERID2']['used_cpu_time'], '14293629 uS') self.assertEqual(rdata['USERID2']['used_memory'], '305020 KB') self.assertEqual(rdata['USERID2']['shared_memory'], '5222190 KB') get_ps.assert_not_called() cache_enabled.assert_not_called() @mock.patch("zvmsdk.monitor.MeteringCache.get") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") @mock.patch("zvmsdk.monitor.ZVMMonitor._update_cpumem_data") def test_private_get_inspect_data_cache_miss_single(self, update_cpumem_data, get_ps, cache_get): cache_get.return_value = None get_ps.return_value = 'on' update_cpumem_data.return_value = { 'USERID1': CPUMEM_SAMPLE1, 'USERID2': CPUMEM_SAMPLE2 } rdata = self._monitor._get_inspect_data('cpumem', ['userid1']) get_ps.assert_called_once_with('userid1') update_cpumem_data.assert_called_once_with(['userid1']) self.assertEqual(sorted(rdata.keys()), sorted(['USERID1', 'USERID2'])) self.assertEqual(sorted(rdata['USERID1'].keys()), sorted(CPUMEM_SAMPLE1.keys())) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') @mock.patch("zvmsdk.monitor.MeteringCache.get") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") @mock.patch("zvmsdk.monitor.ZVMMonitor._update_cpumem_data") def test_private_get_inspect_data_cache_miss_multi(self, update_cpumem_data, get_ps, cache_get): cache_get.side_effect = [{ 'userid': 'USERID1', 'guest_cpus': '1', 'used_cpu_time': '7185838 uS', 'elapsed_cpu_time': '35232895 uS', 'min_cpu_count': '2', 'max_cpu_limit': '10000', 'samples_cpu_in_use': '0', 'samples_cpu_delay': '0', 'used_memory': '390232 KB', 'max_memory': '2097152 KB', 'min_memory': '0 KB', 'shared_memory': '4222192 KB', }, None] get_ps.return_value = 'on' update_cpumem_data.return_value = { 'USERID1': CPUMEM_SAMPLE1, 'USERID2': CPUMEM_SAMPLE2 } rdata = self._monitor._get_inspect_data('cpumem', ['userid1', 'userid2']) get_ps.assert_called_once_with('userid2') update_cpumem_data.assert_called_once_with(['userid1', 'userid2']) self.assertEqual(sorted(rdata.keys()), sorted(['USERID1', 'USERID2'])) self.assertEqual(sorted(rdata['USERID1'].keys()), sorted(CPUMEM_SAMPLE1.keys())) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') self.assertEqual(rdata['USERID1']['shared_memory'], '5222192 KB') @mock.patch("zvmsdk.monitor.MeteringCache.get") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") @mock.patch("zvmsdk.monitor.ZVMMonitor._update_cpumem_data") def test_private_get_inspect_data_guest_off(self, update_cpumem_data, get_ps, cache_get): cache_get.return_value = None get_ps.return_value = 'off' rdata = self._monitor._get_inspect_data('cpumem', ['userid1']) get_ps.assert_called_once_with('userid1') update_cpumem_data.assert_not_called() self.assertEqual(rdata, {}) @mock.patch("zvmsdk.monitor.MeteringCache.get") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") @mock.patch("zvmsdk.monitor.ZVMMonitor._update_nic_data") def test_private_get_inspect_data_vnics(self, update_nic_data, get_ps, cache_get): cache_get.return_value = None get_ps.return_value = 'on' update_nic_data.return_value = {'USERID1': INST_NICS_SAMPLE1, 'USERID2': INST_NICS_SAMPLE2 } rdata = self._monitor._get_inspect_data('vnics', ['USERID1']) get_ps.assert_called_once_with('USERID1') update_nic_data.assert_called_once_with() self.assertEqual(rdata, {'USERID1': INST_NICS_SAMPLE1, 'USERID2': INST_NICS_SAMPLE2 }) @mock.patch("zvmsdk.smutclient.SMUTClient.system_image_performance_query") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") @mock.patch("zvmsdk.smutclient.SMUTClient.get_vm_list") @mock.patch("zvmsdk.smutclient.SMUTClient.namelist_query") def test_private_update_cpumem_data_cache_enabled(self, namelist_query, get_vm_list, cache_enabled, image_performance_query): cache_enabled.return_value = True namelist_query.return_value = ['USERID1', 'USERID2'] get_vm_list.return_value = ['USERID1', 'USERID2'] image_performance_query.return_value = { 'USERID1': CPUMEM_SAMPLE1, 'USERID2': CPUMEM_SAMPLE2 } rdata = self._monitor._update_cpumem_data(['userid1']) image_performance_query.assert_called_once_with('TSTNLIST') namelist_query.assert_called_once_with('TSTNLIST') get_vm_list.assert_called_once_with() self.assertEqual(sorted(rdata.keys()), sorted(['USERID1', 'USERID2'])) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') self.assertEqual( self._monitor._cache._cache['cpumem']['data']['USERID2']['guest_cpus'], '3') @mock.patch("zvmsdk.smutclient.SMUTClient.system_image_performance_query") @mock.patch("zvmsdk.smutclient.SMUTClient.namelist_add") @mock.patch("zvmsdk.smutclient.SMUTClient.get_vm_list") @mock.patch("zvmsdk.smutclient.SMUTClient.namelist_query") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") def test_private_update_cpumem_data_cache_not_in_namelist(self, cache_enabled, namelist_query, get_vm_list, namelist_add, image_performance_query): cache_enabled.return_value = True namelist_query.return_value = ['USERID1'] get_vm_list.return_value = ['USERID1', 'USERID2'] image_performance_query.return_value = { 'USERID1': CPUMEM_SAMPLE1, 'USERID2': CPUMEM_SAMPLE2, } rdata = self._monitor._update_cpumem_data(['USERID1', 'USERID2']) image_performance_query.assert_called_once_with('TSTNLIST') namelist_query.assert_called_once_with('TSTNLIST') get_vm_list.assert_called_once_with() namelist_add.assert_called_once_with('TSTNLIST', 'USERID2') self.assertEqual(sorted(rdata.keys()), sorted(['USERID1', 'USERID2'])) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') self.assertEqual( self._monitor._cache._cache['cpumem']['data']['USERID2']['guest_cpus'], '3') @mock.patch("zvmsdk.smutclient.SMUTClient.get_vm_list") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") @mock.patch("zvmsdk.smutclient.SMUTClient.system_image_performance_query") @mock.patch("zvmsdk.smutclient.SMUTClient.namelist_query") def test_private_update_cpumem_data_cache_disabled(self, namelist_query, image_perform_query, cache_enabled, get_vm_list): namelist_query.return_value = ['USERID1', 'USERID2'] cache_enabled.return_value = False image_perform_query.return_value = { 'USERID1': CPUMEM_SAMPLE1 } get_vm_list.return_value = ['USERID1', 'USERID2'] rdata = self._monitor._update_cpumem_data(['userid1']) namelist_query.assert_called_once_with('TSTNLIST') get_vm_list.assert_called_once_with() image_perform_query.assert_called_once_with('TSTNLIST') self.assertEqual(list(rdata.keys()), ['USERID1']) self.assertEqual(sorted(rdata['USERID1'].keys()), sorted(CPUMEM_SAMPLE1.keys())) self.assertEqual(rdata['USERID1']['guest_cpus'], '1') self.assertEqual(rdata['USERID1']['used_cpu_time'], '6185838 uS') self.assertEqual(rdata['USERID1']['used_memory'], '290232 KB') self.assertEqual( list(self._monitor._cache._cache['cpumem']['data'].keys()), []) @mock.patch("zvmsdk.monitor.ZVMMonitor._get_inspect_data") def test_inspect_stats_single(self, _get_inspect_data): _get_inspect_data.return_value = { 'USERID1': CPUMEM_SAMPLE1, 'USERID2': CPUMEM_SAMPLE2 } rdata = self._monitor.inspect_stats(['USERID1']) _get_inspect_data.assert_called_once_with('cpumem', ['USERID1']) self.assertEqual(sorted(rdata.keys()), sorted(['USERID1'])) self.assertEqual(rdata['USERID1']['guest_cpus'], 1) self.assertEqual(rdata['USERID1']['used_cpu_time_us'], 6185838) self.assertEqual(rdata['USERID1']['elapsed_cpu_time_us'], 35232895) self.assertEqual(rdata['USERID1']['min_cpu_count'], 2) self.assertEqual(rdata['USERID1']['max_cpu_limit'], 10000) self.assertEqual(rdata['USERID1']['used_mem_kb'], 290232) self.assertEqual(rdata['USERID1']['max_mem_kb'], 2097152) self.assertEqual(rdata['USERID1']['min_mem_kb'], 0) self.assertEqual(rdata['USERID1']['shared_mem_kb'], 5222192) @mock.patch("zvmsdk.monitor.ZVMMonitor._get_inspect_data") def test_inspect_stats_multi(self, _get_inspect_data): _get_inspect_data.return_value = { 'USERID1': CPUMEM_SAMPLE1, 'USERID2': CPUMEM_SAMPLE2 } rdata = self._monitor.inspect_stats(['USERID1', 'USERID2']) _get_inspect_data.assert_called_once_with('cpumem', ['USERID1', 'USERID2']) self.assertEqual(sorted(rdata.keys()), sorted(['USERID1', 'USERID2'])) self.assertEqual(rdata['USERID1']['guest_cpus'], 1) self.assertEqual(rdata['USERID1']['used_cpu_time_us'], 6185838) self.assertEqual(rdata['USERID1']['elapsed_cpu_time_us'], 35232895) self.assertEqual(rdata['USERID1']['min_cpu_count'], 2) self.assertEqual(rdata['USERID1']['max_cpu_limit'], 10000) self.assertEqual(rdata['USERID2']['guest_cpus'], 3) self.assertEqual(rdata['USERID2']['used_cpu_time_us'], 14293629) self.assertEqual(rdata['USERID2']['elapsed_cpu_time_us'], 4868976371) self.assertEqual(rdata['USERID2']['min_cpu_count'], 3) self.assertEqual(rdata['USERID2']['max_cpu_limit'], 10000) self.assertEqual(rdata['USERID1']['used_mem_kb'], 290232) self.assertEqual(rdata['USERID1']['max_mem_kb'], 2097152) self.assertEqual(rdata['USERID1']['min_mem_kb'], 0) self.assertEqual(rdata['USERID1']['shared_mem_kb'], 5222192) self.assertEqual(rdata['USERID2']['used_mem_kb'], 305020) self.assertEqual(rdata['USERID2']['max_mem_kb'], 2097152) self.assertEqual(rdata['USERID2']['min_mem_kb'], 0) self.assertEqual(rdata['USERID2']['shared_mem_kb'], 5222190) @mock.patch("zvmsdk.monitor.ZVMMonitor._get_inspect_data") def test_inspect_stats_single_off_or_not_exist(self, _get_inspect_data): _get_inspect_data.return_value = { 'USERID2': CPUMEM_SAMPLE2 } rdata = self._monitor.inspect_stats(['userid1']) _get_inspect_data.assert_called_once_with('cpumem', ['userid1']) self.assertEqual(rdata, {}) @mock.patch("zvmsdk.monitor.ZVMMonitor._get_inspect_data") def test_inspect_stats_multi_off_or_not_exist(self, _get_inspect_data): _get_inspect_data.return_value = { 'USERID1': CPUMEM_SAMPLE1 } rdata = self._monitor.inspect_stats(['USERID1', 'USERID2']) _get_inspect_data.assert_called_once_with('cpumem', ['USERID1', 'USERID2']) self.assertEqual(sorted(rdata.keys()), sorted(['USERID1'])) self.assertEqual(rdata['USERID1']['guest_cpus'], 1) self.assertEqual(rdata['USERID1']['used_cpu_time_us'], 6185838) self.assertEqual(rdata['USERID1']['elapsed_cpu_time_us'], 35232895) self.assertEqual(rdata['USERID1']['min_cpu_count'], 2) self.assertEqual(rdata['USERID1']['max_cpu_limit'], 10000) self.assertEqual(rdata['USERID1']['used_mem_kb'], 290232) self.assertEqual(rdata['USERID1']['max_mem_kb'], 2097152) self.assertEqual(rdata['USERID1']['min_mem_kb'], 0) self.assertEqual(rdata['USERID1']['shared_mem_kb'], 5222192) @mock.patch("zvmsdk.smutclient.SMUTClient" ".virtual_network_vswitch_query_byte_stats") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") def test_private_update_nic_data(self, cache_enabled, smcli_iuo_query): smcli_iuo_query.return_value = SMCLI_VSW_NIC_DATA cache_enabled.return_value = True nics_dict = self._monitor._update_nic_data() self.assertEqual(sorted(["USERID1", "USERID2"]), sorted(nics_dict.keys())) self.assertEqual(nics_dict['USERID1'], INST_NICS_SAMPLE1) self.assertEqual(nics_dict['USERID2'], INST_NICS_SAMPLE2) self.assertEqual(self._monitor._cache.get('vnics', 'USERID1'), INST_NICS_SAMPLE1) self.assertEqual(self._monitor._cache.get('vnics', 'USERID2'), INST_NICS_SAMPLE2) @mock.patch("zvmsdk.smutclient.SMUTClient" ".virtual_network_vswitch_query_byte_stats") @mock.patch("zvmsdk.monitor.ZVMMonitor._cache_enabled") def test_private_update_nic_data_cache_disabled(self, cache_enabled, smcli_iuo_query): smcli_iuo_query.return_value = SMCLI_VSW_NIC_DATA cache_enabled.return_value = False nics_dict = self._monitor._update_nic_data() self.assertEqual(sorted(["USERID1", "USERID2"]), sorted(nics_dict.keys())) self.assertEqual(self._monitor._cache.get('vnics', 'USERID1'), None) self.assertEqual(self._monitor._cache.get('vnics', 'USERID2'), None) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_hostops.py0000664000175000017510000000530713442676317023307 0ustar ruirui00000000000000# 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. import mock from zvmsdk import config from zvmsdk import hostops from zvmsdk.tests.unit import base CONF = config.CONF class SDKHostOpsTestCase(base.SDKTestCase): def setUp(self): self._hostops = hostops.get_hostops() @mock.patch("zvmsdk.hostops.HOSTOps.diskpool_get_info") @mock.patch("zvmsdk.smutclient.SMUTClient.get_host_info") def test_get_host_info(self, get_host_info, diskpool_get_info): get_host_info.return_value = { "zcc_userid": "FAKEUSER", "zvm_host": "FAKENODE", "zhcp": "fakehcp.fake.com", "cec_vendor": "FAKE", "cec_model": "2097", "hypervisor_os": "z/VM 6.1.0", "hypervisor_name": "fakenode", "architecture": "s390x", "lpar_cpu_total": "10", "lpar_cpu_used": "10", "lpar_memory_total": "16G", "lpar_memory_used": "16.0G", "lpar_memory_offline": "0", "ipl_time": "IPL at 03/13/14 21:43:12 EDT", } diskpool_get_info.return_value = { "disk_total": 406105, "disk_used": 367263, "disk_available": 38843, } host_info = self._hostops.get_info() get_host_info.assert_called_once_with() diskpool = CONF.zvm.disk_pool.split(':')[1] diskpool_get_info.assert_called_once_with(diskpool) self.assertEqual(host_info['vcpus'], 10) self.assertEqual(host_info['hypervisor_version'], 610) self.assertEqual(host_info['disk_total'], 406105) @mock.patch("zvmsdk.smutclient.SMUTClient.get_diskpool_info") def test_get_diskpool_info(self, get_diskpool_info): get_diskpool_info.return_value = { "disk_total": "406105.3G", "disk_used": "367262.6G", "disk_available": "38842.7M", } dp_info = self._hostops.diskpool_get_info("fakepool") get_diskpool_info.assert_called_once_with("fakepool") self.assertEqual(dp_info['disk_total'], 406105) self.assertEqual(dp_info['disk_used'], 367263) self.assertEqual(dp_info['disk_available'], 38) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/base.py0000775000175000017510000000273613371225174021461 0ustar ruirui00000000000000# 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. import unittest from zvmsdk import config CONF = config.CONF def set_conf(section, opt, value): CONF[section][opt] = value class SDKTestCase(unittest.TestCase): @classmethod def setUpClass(cls): # This can be used to set up confs before running all cases super(SDKTestCase, cls).setUpClass() cls.old_db_dir = CONF.database.dir set_conf('database', 'dir', '/tmp/') set_conf('zvm', 'disk_pool', 'ECKD:TESTPOOL') set_conf('image', 'sdk_image_repository', '/tmp/') set_conf('zvm', 'namelist', 'TSTNLIST') @classmethod def tearDownClass(cls): super(SDKTestCase, cls).tearDownClass() # Restore the original db path CONF.database.dir = cls.old_db_dir def setUp(self): super(SDKTestCase, self).setUp() def _fake_fun(self, value=None): return lambda *args, **kwargs: value zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_vmops.py0000664000175000017510000003537013442676317022757 0ustar ruirui00000000000000# 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. import mock from zvmsdk import exception from zvmsdk import vmops from zvmsdk.tests.unit import base class SDKVMOpsTestCase(base.SDKTestCase): def setUp(self): super(SDKVMOpsTestCase, self).setUp() self.vmops = vmops.get_vmops() @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") def test_get_power_state(self, gps): gps.return_value = 'on' self.vmops.get_power_state('cbi00063') gps.assert_called_with('cbi00063') @mock.patch("zvmsdk.smutclient.SMUTClient.get_guest_connection_status") def test_is_reachable(self, ggcs): ggcs.return_value = True ret = self.vmops.is_reachable('cbi00063') self.assertEqual(ret, True) @mock.patch("zvmsdk.smutclient.SMUTClient.guest_start") def test_guest_start(self, guest_start): self.vmops.guest_start('cbi00063') guest_start.assert_called_once_with('cbi00063') @mock.patch("zvmsdk.smutclient.SMUTClient.guest_pause") def test_guest_pause(self, guest_pause): self.vmops.guest_pause('cbi00063') guest_pause.assert_called_once_with('cbi00063') @mock.patch("zvmsdk.smutclient.SMUTClient.guest_unpause") def test_guest_unpause(self, guest_unpause): self.vmops.guest_unpause('cbi00063') guest_unpause.assert_called_once_with('cbi00063') @mock.patch("zvmsdk.smutclient.SMUTClient.namelist_add") @mock.patch("zvmsdk.smutclient.SMUTClient.create_vm") def test_create_vm(self, create_vm, namelistadd): userid = 'fakeuser' cpu = 2 memory = '2g' disk_list = [] user_profile = 'testprof' max_cpu = 10 max_mem = '4G' self.vmops.create_vm(userid, cpu, memory, disk_list, user_profile, max_cpu, max_mem) create_vm.assert_called_once_with(userid, cpu, memory, disk_list, user_profile, max_cpu, max_mem) namelistadd.assert_called_once_with('TSTNLIST', userid) @mock.patch("zvmsdk.smutclient.SMUTClient.process_additional_minidisks") def test_guest_config_minidisks(self, process_additional_minidisks): userid = 'userid' disk_list = [{'vdev': '0101', 'format': 'ext3', 'mntdir': '/mnt/0101'}] self.vmops.guest_config_minidisks(userid, disk_list) process_additional_minidisks.assert_called_once_with(userid, disk_list) @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") def test_is_powered_off(self, check_stat): check_stat.return_value = 'off' ret = self.vmops.is_powered_off('cbi00063') self.assertEqual(True, ret) @mock.patch("zvmsdk.smutclient.SMUTClient.get_image_performance_info") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_get_info(self, gps, gipi): gps.return_value = 'on' gipi.return_value = {'used_memory': u'4872872 KB', 'used_cpu_time': u'6911844399 uS', 'guest_cpus': u'2', 'userid': u'CMABVT', 'max_memory': u'8388608 KB'} vm_info = self.vmops.get_info('fakeid') gps.assert_called_once_with('fakeid') gipi.assert_called_once_with('fakeid') self.assertEqual(vm_info['power_state'], 'on') self.assertEqual(vm_info['max_mem_kb'], 8388608) self.assertEqual(vm_info['mem_kb'], 4872872) self.assertEqual(vm_info['num_cpu'], 2) self.assertEqual(vm_info['cpu_time_us'], 6911844399) @mock.patch("zvmsdk.smutclient.SMUTClient.get_image_performance_info") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_get_info_error(self, gps, gipi): gps.return_value = 'on' gipi.side_effect = exception.ZVMVirtualMachineNotExist( zvm_host='fakehost', userid='fakeid') self.assertRaises(exception.ZVMVirtualMachineNotExist, self.vmops.get_info, 'fakeid') @mock.patch("zvmsdk.smutclient.SMUTClient.get_user_direct") @mock.patch("zvmsdk.smutclient.SMUTClient.get_image_performance_info") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_get_info_shutdown(self, gps, gipi, gud): gps.return_value = 'off' gipi.return_value = None gud.return_value = [ u'USER FAKEUSER DFLTPASS 2048m 2048m G', u'INCLUDE PROFILE', u'CPU 00 BASE', u'CPU 01', u'IPL 0100', u'NICDEF 1000 TYPE QDIO LAN SYSTEM VSW2 MACID 0E4E8E', u'MDISK 0100 3390 34269 3338 OMB1A9 MR', u''] vm_info = self.vmops.get_info('fakeid') gps.assert_called_once_with('fakeid') gud.assert_called_once_with('fakeid') self.assertEqual(vm_info['power_state'], 'off') self.assertEqual(vm_info['max_mem_kb'], 2097152) self.assertEqual(vm_info['mem_kb'], 0) self.assertEqual(vm_info['num_cpu'], 2) self.assertEqual(vm_info['cpu_time_us'], 0) @mock.patch("zvmsdk.smutclient.SMUTClient.get_user_direct") @mock.patch("zvmsdk.smutclient.SMUTClient.get_image_performance_info") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_get_info_get_uid_failed(self, gps, gipi, gud): gps.return_value = 'off' gipi.return_value = None gud.side_effect = exception.ZVMVirtualMachineNotExist(userid='fakeid', zvm_host='fakehost') self.assertRaises(exception.ZVMVirtualMachineNotExist, self.vmops.get_info, 'fakeid') @mock.patch("zvmsdk.smutclient.SMUTClient.guest_deploy") def test_guest_deploy(self, deploy_image_to_vm): self.vmops.guest_deploy('fakevm', 'fakeimg', '/test/transport.tgz') deploy_image_to_vm.assert_called_with('fakevm', 'fakeimg', '/test/transport.tgz', None, None) @mock.patch('zvmsdk.vmops.VMOps.set_hostname') @mock.patch("zvmsdk.database.ImageDbOperator.image_query_record") @mock.patch("zvmsdk.smutclient.SMUTClient.guest_deploy") def test_guest_deploy_sethostname(self, deploy_image_to_vm, img_query, set_hostname): fake_hostname = 'fakehost' img_query.return_value = [{'imageosdistro': 'rhel6.7'}] self.vmops.guest_deploy('fakevm', 'fakeimg', hostname=fake_hostname) deploy_image_to_vm.assert_called_with('fakevm', 'fakeimg', None, None, None) img_query.assert_called_once_with('fakeimg') set_hostname.assert_called_once_with('fakevm', fake_hostname, 'rhel6.7') @mock.patch("zvmsdk.smutclient.SMUTClient.guest_capture") def test_guest_capture(self, guest_capture): self.vmops.guest_capture('fakevm', 'fakeimg') guest_capture.assert_called_once_with('fakevm', 'fakeimg', capture_type = 'rootonly', compress_level = 6) @mock.patch("zvmsdk.smutclient.SMUTClient.get_user_direct") def test_get_definition_info(self, get_user_direct): get_user_direct.return_value = [ 'line1', 'NICDEF 1000 TYPE QDIO LAN SYSTEM VSWITCH'] self.vmops.get_definition_info("fake_user_id", nic_coupled='1000') get_user_direct.assert_called_with("fake_user_id") @mock.patch("zvmsdk.smutclient.SMUTClient.namelist_remove") @mock.patch("zvmsdk.smutclient.SMUTClient.delete_vm") def test_delete_vm(self, delete_vm, namelistremove): userid = 'userid' self.vmops.delete_vm(userid) delete_vm.assert_called_once_with(userid) namelistremove.assert_called_once_with('TSTNLIST', userid) @mock.patch("zvmsdk.smutclient.SMUTClient.execute_cmd") def test_execute_cmd(self, execute_cmd): userid = 'userid' cmdStr = 'ls' self.vmops.execute_cmd(userid, cmdStr) execute_cmd.assert_called_once_with(userid, cmdStr) @mock.patch("zvmsdk.smutclient.SMUTClient.guest_stop") def test_guest_stop(self, gs): userid = 'userid' self.vmops.guest_stop(userid) gs.assert_called_once_with(userid) @mock.patch("zvmsdk.smutclient.SMUTClient.guest_stop") def test_guest_stop_with_timeout(self, gs): userid = 'userid' gs.return_value = u'off' self.vmops.guest_stop(userid, timeout=300, poll_interval=10) gs.assert_called_once_with(userid, timeout=300, poll_interval=10) @mock.patch("zvmsdk.smutclient.SMUTClient.get_vm_list") def test_guest_list(self, get_vm_list): self.vmops.guest_list() get_vm_list.assert_called_once_with() @mock.patch("zvmsdk.smutclient.SMUTClient.add_mdisks") @mock.patch("zvmsdk.smutclient.SMUTClient.get_user_direct") def test_create_disks(self, gud, amds): user_direct = ['USER TEST TEST', 'MDISK 100 3390', 'MDISK 101 3390'] gud.return_value = user_direct self.vmops.create_disks('userid', []) gud.assert_called_once_with('userid') amds.assert_called_once_with('userid', [], '0102') @mock.patch("zvmsdk.smutclient.SMUTClient.add_mdisks") @mock.patch("zvmsdk.smutclient.SMUTClient.get_user_direct") def test_create_disks_200(self, gud, amds): user_direct = ['USER TEST TEST', 'MDISK 100 3390', 'MDISK 200 3390'] gud.return_value = user_direct self.vmops.create_disks('userid', []) gud.assert_called_once_with('userid') amds.assert_called_once_with('userid', [], '0201') @mock.patch("zvmsdk.smutclient.SMUTClient.remove_mdisks") @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") def test_delete_disks(self, gps, rmd): gps.return_value = 'off' self.vmops.delete_disks('userid', ['101', '102']) rmd.assert_called_once_with('userid', ['101', '102']) @mock.patch("zvmsdk.smutclient.SMUTClient.get_power_state") def test_delete_disks_active(self, gps): gps.return_value = 'on' self.assertRaises(exception.SDKFunctionNotImplementError, self.vmops.delete_disks, 'userid', ['101', '102']) gps.assert_called_once_with('userid') @mock.patch("zvmsdk.smutclient.SMUTClient.guest_reboot") def test_guest_reboot(self, guest_reboot): self.vmops.guest_reboot('cbi00063') guest_reboot.assert_called_once_with('cbi00063') @mock.patch("zvmsdk.smutclient.SMUTClient.guest_reset") def test_guest_reset(self, guest_reset): self.vmops.guest_reset('cbi00063') guest_reset.assert_called_once_with('cbi00063') @mock.patch("zvmsdk.smutclient.SMUTClient.live_resize_cpus") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_live_resize_cpus(self, power_state, do_resize): userid = 'testuid' cpu_cnt = 3 power_state.return_value = 'on' self.vmops.live_resize_cpus(userid, cpu_cnt) power_state.assert_called_once_with(userid) do_resize.assert_called_once_with(userid, cpu_cnt) @mock.patch("zvmsdk.smutclient.SMUTClient.live_resize_cpus") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_live_resize_cpus_guest_inactive(self, power_state, do_resize): userid = 'testuid' cpu_cnt = 3 power_state.return_value = 'off' self.assertRaises(exception.SDKConflictError, self.vmops.live_resize_cpus, userid, cpu_cnt) power_state.assert_called_once_with(userid) do_resize.assert_not_called() @mock.patch("zvmsdk.smutclient.SMUTClient.resize_cpus") def test_resize_cpus(self, do_resize): userid = 'testuid' cpu_cnt = 3 self.vmops.resize_cpus(userid, cpu_cnt) do_resize.assert_called_once_with(userid, cpu_cnt) @mock.patch("zvmsdk.smutclient.SMUTClient.live_resize_memory") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_live_resize_memory(self, power_state, do_resize): userid = 'testuid' size = '1g' power_state.return_value = 'on' self.vmops.live_resize_memory(userid, size) power_state.assert_called_once_with(userid) do_resize.assert_called_once_with(userid, size) @mock.patch("zvmsdk.smutclient.SMUTClient.live_resize_memory") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_live_resize_memory_guest_inactive(self, power_state, do_resize): userid = 'testuid' size = '1g' power_state.return_value = 'off' self.assertRaises(exception.SDKConflictError, self.vmops.live_resize_memory, userid, size) power_state.assert_called_once_with(userid) do_resize.assert_not_called() @mock.patch("zvmsdk.smutclient.SMUTClient.resize_memory") def test_resize_memory(self, do_resize): userid = 'testuid' size = '1g' self.vmops.resize_memory(userid, size) do_resize.assert_called_once_with(userid, size) @mock.patch("zvmsdk.smutclient.SMUTClient.live_migrate_move") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_live_migrate_vm(self, power_state, live_migrate_vm): userid = 'testuid' destination = 'testssi' parms = {} action = "move" power_state.return_value = 'on' self.vmops.live_migrate_vm(userid, destination, parms, action) power_state.assert_called_once_with(userid) live_migrate_vm.assert_called_once_with(userid, destination, parms) @mock.patch("zvmsdk.smutclient.SMUTClient.live_migrate_test") @mock.patch('zvmsdk.vmops.VMOps.get_power_state') def test_live_migrate_test(self, power_state, live_migrate_vm): userid = 'testuid' destination = 'testssi' parms = {} action = "test" power_state.return_value = 'on' self.vmops.live_migrate_vm(userid, destination, parms, action) power_state.assert_called_once_with(userid) live_migrate_vm.assert_called_once_with(userid, destination) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/0000775000175000017510000000000013442723341021633 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/__init__.py0000664000175000017510000000000013371225174023734 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/test_utils.py0000664000175000017510000000374013371225174024412 0ustar ruirui00000000000000# 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. import unittest from zvmsdk.sdkwsgi import util class SDKWsgiUtilsTestCase(unittest.TestCase): def __init__(self, methodName='runTest'): super(SDKWsgiUtilsTestCase, self).__init__(methodName) def test_get_http_code_from_sdk_return(self): msg = {} msg['overallRC'] = 404 ret = util.get_http_code_from_sdk_return(msg, default=201) self.assertEqual(404, ret) msg['overallRC'] = 400 ret = util.get_http_code_from_sdk_return(msg) self.assertEqual(400, ret) msg['overallRC'] = 100 ret = util.get_http_code_from_sdk_return(msg, default=200) self.assertEqual(400, ret) msg['overallRC'] = 0 ret = util.get_http_code_from_sdk_return(msg, default=204) self.assertEqual(204, ret) msg['overallRC'] = 300 ret = util.get_http_code_from_sdk_return(msg, default=201) self.assertEqual(500, ret) def test_get_http_code_from_sdk_return_with_already_exist(self): msg = {} msg['overallRC'] = 8 msg['rc'] = 212 msg['rs'] = 36 ret = util.get_http_code_from_sdk_return(msg, additional_handler=util.handle_already_exists) self.assertEqual(409, ret) msg['rc'] = 100 ret = util.get_http_code_from_sdk_return(msg, additional_handler=util.handle_already_exists) self.assertEqual(500, ret) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/0000775000175000017510000000000013442723341023433 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/__init__.py0000664000175000017510000000000013371225174025534 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/test_image.py0000664000175000017510000001526413371225174026140 0ustar ruirui00000000000000# 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. import datetime import jwt import mock import unittest from zvmsdk import exception from zvmsdk import config from zvmsdk.sdkwsgi.handlers import image from zvmsdk.sdkwsgi import util CONF = config.CONF FAKE_UUID = '00000000-0000-0000-0000-000000000000' def set_conf(section, opt, value): CONF[section][opt] = value class FakeResp(object): def __init__(self): self.body = {} class FakeReq(object): def __init__(self): self.headers = {} self.environ = {} self.response = FakeResp() self.__name__ = '' def __getitem__(self, name): return self.headers class HandlersImageTest(unittest.TestCase): def setUp(self): set_conf('wsgi', 'auth', 'none') expired_elapse = datetime.timedelta(seconds=100) expired_time = datetime.datetime.utcnow() + expired_elapse payload = jwt.encode({'exp': expired_time}, 'username') self.req = FakeReq() self.req.headers['X-Auth-Token'] = payload @mock.patch.object(image.ImageAction, 'create') def test_image_create(self, mock_create): body_str = """{"image": {"image_name": "46a4aea3-54b6-4b1c", "url": "file:///tmp/test.img", "image_meta": { "os_version": "rhel7.2", "md5sum": "12345678912345678912345678912345" }, "remotehost": "hostname" } }""" self.req.body = body_str body = { 'image': { 'remotehost': 'hostname', 'image_meta': { 'os_version': 'rhel7.2', 'md5sum': '12345678912345678912345678912345' }, 'url': 'file:///tmp/test.img', 'image_name': '46a4aea3-54b6-4b1c' } } mock_create.return_value = '' image.image_create(self.req) mock_create.assert_called_once_with(body=body) def test_image_create_invalidname(self): body_str = '{"image": {"version": ""}}' self.req.body = body_str self.assertRaises(exception.ValidationError, image.image_create, self.req) def test_image_create_invalid_os_version(self): body_str = """{"image": {"image_name": "46a4aea3-54b6-4b1c", "url": "file:///tmp/test.img", "image_meta": { "os_version": "rhel2.2", "md5sum": "12345678912345678912345678912345" }, "remotehost": "hostname" } }""" self.req.body = body_str self.assertRaises(exception.ValidationError, image.image_create, self.req) def test_image_create_invalid_url(self): # FIXME: need url format validation pass def test_image_create_invalid_image_meta(self): # miss os_version param body_str = """{"image": {"url": "file:///tmp/test.img", "image_meta": { "md5sum": "12345678912345678912345678912345" }, "remotehost": "hostname" } }""" self.req.body = body_str self.assertRaises(exception.ValidationError, image.image_create, self.req) def test_image_create_invalid_image_meta_md5sum(self): # md5sum is less than 32 chars body_str = """{"image": {"url": "file://tmp/test.img", "image_meta": { "os_version": "rhel7.2", "md5sum": "2345678912345678912345678912345" }, "remotehost": "hostname" } }""" self.req.body = body_str self.assertRaises(exception.ValidationError, image.image_create, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(image.ImageAction, 'get_root_disk_size') def test_image_get_root_disk_size(self, mock_get, mock_name): mock_name.return_value = 'dummy' mock_get.return_value = '100' image.image_get_root_disk_size(self.req) mock_get.assert_called_once_with(self.req, 'dummy') @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(image.ImageAction, 'delete') def test_image_delete(self, mock_delete, mock_name): mock_delete.return_value = '' mock_name.return_value = 'dummy' image.image_delete(self.req) mock_delete.assert_called_once_with('dummy') @mock.patch.object(image.ImageAction, 'query') def test_image_query(self, mock_query): mock_query.return_value = '[]' self.req.GET = {} self.req.GET['imagename'] = 'image1' image.image_query(self.req) mock_query.assert_called_once_with(self.req, 'image1') @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(image.ImageAction, 'export') def test_image_export(self, mock_export, mock_get): mock_export.return_value = '{}' body_str = """{"location":{ "dest_url": "file:///tmp/images/image1", "remote_host": "192.168.12.34" } }""" body = {u'location': {u'dest_url': u'file:///tmp/images/image1', u'remote_host': u'192.168.12.34'}} fake_image_name = '46a4aea3-54b6-4b1c' mock_get.return_value = fake_image_name self.req.body = body_str image.image_export(self.req) mock_export.assert_called_once_with(fake_image_name, body=body) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/test_volume.py0000664000175000017510000000572513371225174026366 0ustar ruirui00000000000000# 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. import datetime import jwt import json import mock import unittest from zvmsdk import config from zvmsdk.sdkwsgi.handlers import volume CONF = config.CONF FAKE_UUID = '00000000-0000-0000-0000-000000000000' def set_conf(section, opt, value): CONF[section][opt] = value class FakeResp(object): def __init__(self): self.body = {} class FakeReq(object): def __init__(self): self.headers = {} self.environ = {} self.__name__ = '' self.response = FakeResp() def __getitem__(self, name): return self.headers class HandlersVolumeTest(unittest.TestCase): def setUp(self): set_conf('wsgi', 'auth', 'none') expired_elapse = datetime.timedelta(seconds=100) expired_time = datetime.datetime.utcnow() + expired_elapse payload = jwt.encode({'exp': expired_time}, 'username') self.req = FakeReq() self.req.headers['X-Auth-Token'] = payload @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_volume_attach(self, mock_attach): mock_attach.return_value = {'overallRC': 0} connection_info = {"assigner_id": "username", "zvm_fcp": "1fc5", "target_wwpn": "0x5005076801401234", "target_lun": "0x0026000000000000", "os_version": "rhel7.2", "multipath": "true", "mount_point": ""} body_str = {"info": {"connection": connection_info}} self.req.body = json.dumps(body_str) volume.volume_attach(self.req) mock_attach.assert_called_once_with( 'volume_attach', connection_info) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_volume_detach(self, mock_detach): mock_detach.return_value = {'overallRC': 0} connection_info = {"assigner_id": "username", "zvm_fcp": "1fc5", "target_wwpn": "0x5005076801401234", "target_lun": "0x0026000000000000", "os_version": "rhel7.2", "multipath": "true", "mount_point": ""} body_str = {"info": {"connection": connection_info}} self.req.body = json.dumps(body_str) volume.volume_detach(self.req) mock_detach.assert_called_once_with( 'volume_detach', connection_info) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/test_guest.py0000664000175000017510000012135213375735014026204 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import jwt import mock import unittest import webob.exc from zvmsdk import exception from zvmsdk.sdkwsgi.handlers import guest from zvmsdk.sdkwsgi import util FAKE_USERID = '00000000-0000-0000-0000-000000000000' FAKE_USERID_LIST_STR = 'ab,c,userid1' FAKE_USERID_LIST = ['ab', 'c', 'userid1'] class FakeReqGet(object): def get(self, userid): return FAKE_USERID_LIST_STR def keys(self): return ['userid'] def values(self): return FAKE_USERID_LIST class FakeResp(object): def __init__(self): self.body = {} class FakeReq(object): def __init__(self): self.headers = {} self.environ = {} self.body = {} self.response = FakeResp() self.__name__ = '' def __getitem__(self, name): return self.headers class SDKWSGITest(unittest.TestCase): def setUp(self): expired_elapse = datetime.timedelta(seconds=100) expired_time = datetime.datetime.utcnow() + expired_elapse payload = jwt.encode({'exp': expired_time}, 'username') self.req = FakeReq() self.req.headers['X-Auth-Token'] = payload class GuestActionsTest(SDKWSGITest): @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_start(self, mock_action, mock_userid): self.req.body = '{"action": "start"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_start', FAKE_USERID) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_stop(self, mock_action, mock_userid): self.req.body = '{"action": "stop"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_stop', FAKE_USERID, timeout=None, poll_interval=None) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_stop_with_timeout(self, mock_action, mock_userid): self.req.body = '{"action": "stop", "timeout": 300}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_stop', FAKE_USERID, timeout=300, poll_interval=None) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_softstop_with_timeout_poll_interval(self, mock_action, mock_userid): self.req.body = """{"action": "softstop", "timeout": 300, "poll_interval": 15}""" mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_softstop', FAKE_USERID, timeout=300, poll_interval=15) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_get_console_output(self, mock_action, mock_userid): self.req.body = '{"action": "get_console_output"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_get_console_output', FAKE_USERID) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_live_resize_cpus(self, mock_action, mock_userid): self.req.body = '{"action": "live_resize_cpus", "cpu_cnt": 3}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_live_resize_cpus', FAKE_USERID, 3) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_live_resize_cpus_missing_param(self, mock_action, mock_userid): self.req.body = '{"action": "live_resize_cpus"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_live_resize_cpus_invalid_cpu_cnt_1(self, mock_action, mock_userid): self.req.body = '{"action": "live_resize_cpus", "cpu_cnt": 65}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_live_resize_cpus_invalid_cpu_cnt_2(self, mock_action, mock_userid): self.req.body = '{"action": "live_resize_cpus", "cpu_cnt": 0}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_live_resize_cpus_invalid_cpu_cnt_type(self, mock_action, mock_userid): self.req.body = '{"action": "live_resize_cpus", "cpu_cnt": "2"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_cpus(self, mock_action, mock_userid): self.req.body = '{"action": "resize_cpus", "cpu_cnt": 3}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_resize_cpus', FAKE_USERID, 3) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_cpus_missing_param(self, mock_action, mock_userid): self.req.body = '{"action": "resize_cpus"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_cpus_invalid_cpu_cnt_1(self, mock_action, mock_userid): self.req.body = '{"action": "resize_cpus", "cpu_cnt": 65}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_cpus_invalid_cpu_cnt_2(self, mock_action, mock_userid): self.req.body = '{"action": "resize_cpus", "cpu_cnt": 0}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_cpus_invalid_cpu_cnt_type(self, mock_action, mock_userid): self.req.body = '{"action": "resize_cpus", "cpu_cnt": "2"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_mem(self, mock_action, mock_userid): self.req.body = '{"action": "resize_mem", "size": "4096m"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_resize_mem', FAKE_USERID, "4096m") @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_mem_missing_param(self, mock_action, mock_userid): self.req.body = '{"action": "resize_mem"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_mem_invalid_mem_1(self, mock_action, mock_userid): self.req.body = '{"action": "resize_mem", "size": "88888M"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_mem_invalid_mem_2(self, mock_action, mock_userid): self.req.body = '{"action": "resize_mem", "size": "123"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_resize_cpus_invalid_mem_type(self, mock_action, mock_userid): self.req.body = '{"action": "resize_mem", "size": 1024}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_live_resize_mem(self, mock_action, mock_userid): self.req.body = '{"action": "live_resize_mem", "size": "4G"}' mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_live_resize_mem', FAKE_USERID, "4G") @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_deploy(self, mock_action, mock_userid): self.req.body = """{"action": "deploy", "image": "image1", "transportfiles": "file1", "remotehost": "test@host1.x.y", "vdev": "1000"}""" mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_deploy', FAKE_USERID, 'image1', remotehost='test@host1.x.y', transportfiles='file1', vdev='1000', hostname=None) @mock.patch.object(util, 'wsgi_path_item') def test_guest_deploy_missing_param(self, mock_userid): self.req.body = """{"action": "deploy", "transportfiles": "file1", "remotehost": "test@host1.x.y", "vdev": "1000"}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_deploy_invalid_vdev(self, mock_userid): # vdev not string type self.req.body = """{"action": "deploy", "image": "image1", "transportfiles": "file1", "remotehost": "test@host.com.cn", "vdev": 1000}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_deploy_invalid_remotehost(self, mock_userid): self.req.body = """{"action": "deploy", "image": "image1", "transportfiles": "file1", "remotehost": ".122.sd..", "vdev": "1000"}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_deploy_with_ip_in_remotehost(self, mock_action, mock_userid): self.req.body = """{"action": "deploy", "image": "image1", "transportfiles": "file1", "remotehost": "test@192.168.99.99", "vdev": "1000"}""" mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_deploy', FAKE_USERID, 'image1', remotehost='test@192.168.99.99', transportfiles='file1', vdev='1000', hostname=None) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_deploy_with_fqdn_in_remotehost(self, mock_action, mock_userid): # remote host with Hostname + DomainName in it self.req.body = """{"action": "deploy", "image": "image1", "transportfiles": "file1", "remotehost": "test123@test.xyz.com", "vdev": "1000"}""" mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with('guest_deploy', FAKE_USERID, 'image1', remotehost='test123@test.xyz.com', transportfiles='file1', vdev='1000', hostname=None) @mock.patch.object(util, 'wsgi_path_item') def test_guest_deploy_without_username_in_remotehost(self, mock_userid): # remote host without username in it self.req.body = """{"action": "deploy", "image": "image1", "transportfiles": "file1", "remotehost": "@test.xyz.com", "vdev": "1000"}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_deploy_additional_param(self, mock_userid): # A typo in the transportfiles self.req.body = """{"action": "deploy", "image": "image1", "transportfiless": "file1", "remotehost": "test@192.168.99.1", "vdev": "1000"}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_invalid_action(self, mock_userid): self.req.body = '{"fake": "None"}' mock_userid.return_value = FAKE_USERID self.assertRaises(webob.exc.HTTPBadRequest, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_capture_additional_param(self, mock_userid): # Put wrong parameter compressionlevel, it should be compresslevel self.req.body = """{"action": "capture", "image": "image1", "capture_type": "rootonly", "compression_level": "6"}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_capture_invalid_capturetype(self, mock_userid): # Put compresslevel to be invalid 10 self.req.body = """{"action": "capture", "image": "image1", "capture_type": "rootdisk", "compress_level": "10"}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_capture_invalid_compresslevel(self, mock_userid): # Put capture type to be invalid value self.req.body = """{"action": "capture", "image": "image1", "capture_type": "faketype", "compress_level": 9}""" mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_action, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_capture(self, mock_action, mock_userid): self.req.body = """{"action": "capture", "image": "image1", "capture_type": "rootonly", "compress_level": 6}""" mock_action.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_action(self.req) mock_action.assert_called_once_with("guest_capture", FAKE_USERID, "image1", capture_type="rootonly", compress_level=6) class HandlersGuestTest(SDKWSGITest): @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create(self, mock_create): body_str = '{"guest": {"userid": "name1", "vcpus": 1, "memory": 1}}' self.req.body = body_str mock_create.return_value = '' guest.guest_create(self.req) mock_create.assert_called_once_with('guest_create', 'name1', 1, 1) def test_guest_create_invalid_userid(self): body_str = '{"guest": {"userid": ""}}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create_with_disk_list(self, mock_create): body_str = """{"guest": {"userid": "name1", "vcpus": 1, "memory": 1, "disk_list": [{"size": "1g", "format": "xfs", "disk_pool": "ECKD:poolname"} ]}}""" self.req.body = body_str mock_create.return_value = '' guest.guest_create(self.req) mock_create.assert_called_once_with('guest_create', 'name1', 1, 1, disk_list=[{u'size': u'1g', 'format': 'xfs', 'disk_pool': 'ECKD:poolname'}]) def test_guest_create_invalid_disk_list(self): body_str = """{"guest": {"userid": "name1", "vcpus": 1, "memory": 1, "disk_list": [{"size": 1}]}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create_with_invalid_format(self, mock_create): body_str = """{"guest": {"userid": "name1", "vcpus": 1, "memory": 1, "disk_list": [{"size": "1g", "format": "dummy", "disk_pool": "ECKD:poolname"} ]}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) def test_guest_create_invalid_disk_list_param(self): body_str = """{"guest": {"userid": "name1", "vcpus": 1, "memory": 1, "disk_list": [{"size": "1g", "dummy": 1}]}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) def test_guest_create_invalid_disk_list_poolname(self): body_str = """{"guest": {"userid": "name1", "vcpus": 1, "memory": 1, "disk_list": [{"size": "1g", "disk_pool": "pool"}]}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) def test_guest_create_invalid_cpu(self): body_str = '{"guest": {"userid": "name1", "vcpus": "dummy"}}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) def test_guest_create_invalid_mem(self): body_str = '{"guest": {"userid": "name1", "memory": "dummy"}}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) def test_guest_create_false_input(self): body_str = '{"guest": {"userid": "name1", "dummy": "dummy"}}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) body_str = '{"guest": {"userid": "name1"}, "dummy": "dummy"}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create, self.req) @mock.patch.object(guest.VMHandler, 'list') def test_guest_list(self, mock_list): mock_list.return_value = '' guest.guest_list(self.req) mock_list.assert_called_once_with() @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(guest.VMHandler, 'get_info') def test_guest_get_info(self, mock_get, mock_userid): mock_get.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_get_info(self.req) mock_get.assert_called_once_with(self.req, FAKE_USERID) @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(guest.VMHandler, 'get_power_state') def test_guest_power_state(self, mock_get, mock_userid): mock_get.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_get_power_state(self.req) mock_get.assert_called_once_with(self.req, FAKE_USERID) @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(guest.VMHandler, 'delete') def test_guest_delete(self, mock_delete, mock_userid): mock_delete.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_delete(self.req) mock_delete.assert_called_once_with(FAKE_USERID) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_list(self, mock_interface): mock_interface.return_value = '' guest.guest_list(self.req) mock_interface.assert_called_once_with('guest_list') @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create_nic(self, mock_create, mock_userid): vdev = '1234' nic_id = "514fec03-0d96-4349-a670-d972805fb579" mac_addr = "02:00:00:11:22:33" body_str = """{"nic": {"vdev": "1234", "nic_id": "514fec03-0d96-4349-a670-d972805fb579", "mac_addr": "02:00:00:11:22:33"} }""" self.req.body = body_str mock_create.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_create_nic(self.req) mock_create.assert_called_once_with('guest_create_nic', FAKE_USERID, active=False, mac_addr=mac_addr, nic_id=nic_id, vdev=vdev) def test_guest_create_nic_invalid_vdev(self): body_str = '{"nic": {"vdev": 123}}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create_nic, self.req) def test_guest_create_nic_invalid_mac_addr(self): body_str = '{"nic": {"mac_addr": "11:22:33:44:55:6s"}}' self.req.body = body_str self.assertRaises(exception.ValidationError, guest.guest_create_nic, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create_network_interface(self, mock_interface, mock_userid): os_version = 'rhel6' guest_networks = [{'ip_addr': '192.168.12.34', 'dns_addr': ['9.1.2.3'], 'gateway_addr': '192.168.95.1', 'cidr': '192.168.95.0/24', 'nic_vdev': '1000', 'mac_addr': '02:00:00:12:34:56'}] bstr = """{"interface": {"os_version": "rhel6", "guest_networks": [ {"ip_addr": "192.168.12.34", "dns_addr": ["9.1.2.3"], "gateway_addr": "192.168.95.1", "cidr": "192.168.95.0/24", "nic_vdev": "1000", "mac_addr": "02:00:00:12:34:56"}]}}""" self.req.body = bstr mock_userid.return_value = FAKE_USERID mock_interface.return_value = '' guest.guest_create_network_interface(self.req) mock_interface.assert_called_once_with( 'guest_create_network_interface', FAKE_USERID, os_version=os_version, guest_networks=guest_networks, active=False) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create_network_interface_OSA(self, mock_interface, mock_userid): os_version = 'rhel6' guest_networks = [{'ip_addr': '192.168.12.34', 'dns_addr': ['9.1.2.3'], 'gateway_addr': '192.168.95.1', 'cidr': '192.168.95.0/24', 'nic_vdev': '1000', 'mac_addr': '02:00:00:12:34:56', 'osa_device': 'AABB'}] bstr = """{"interface": {"os_version": "rhel6", "guest_networks": [ {"ip_addr": "192.168.12.34", "dns_addr": ["9.1.2.3"], "gateway_addr": "192.168.95.1", "cidr": "192.168.95.0/24", "nic_vdev": "1000", "mac_addr": "02:00:00:12:34:56", "osa_device": "AABB"}]}}""" self.req.body = bstr mock_userid.return_value = FAKE_USERID mock_interface.return_value = '' guest.guest_create_network_interface(self.req) mock_interface.assert_called_once_with( 'guest_create_network_interface', FAKE_USERID, os_version=os_version, guest_networks=guest_networks, active=False) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_delete_network_interface(self, mock_interface, mock_userid): os_version = 'rhel6' vdev = '1000' bstr = """{"interface": {"os_version": "rhel6", "vdev": "1000"}}""" self.req.body = bstr mock_userid.return_value = FAKE_USERID mock_interface.return_value = '' guest.guest_delete_network_interface(self.req) mock_interface.assert_called_once_with( 'guest_delete_network_interface', FAKE_USERID, os_version, vdev, active=False) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guests_get_nic_info(self, mock_interface): self.req.GET = {} mock_interface.return_value = '' guest.guests_get_nic_info(self.req) mock_interface.assert_called_once_with( 'guests_get_nic_info', userid=None, nic_id=None, vswitch=None) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guests_get_nic_info_all(self, mock_interface): userid = 'fakeid' nic_id = 'fake_nic_id' vswitch = 'vswitch' self.req.GET = {} self.req.GET['userid'] = userid self.req.GET['nic_id'] = nic_id self.req.GET['vswitch'] = vswitch mock_interface.return_value = '' guest.guests_get_nic_info(self.req) mock_interface.assert_called_once_with( 'guests_get_nic_info', userid=userid, nic_id=nic_id, vswitch=vswitch) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guests_get_nic_info_with_userid(self, mock_interface): userid = 'fakeid' self.req.GET = {} self.req.GET['userid'] = userid mock_interface.return_value = '' guest.guests_get_nic_info(self.req) mock_interface.assert_called_once_with( 'guests_get_nic_info', userid=userid, nic_id=None, vswitch=None) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guests_get_nic_info_with_nicid(self, mock_interface): nic_id = 'fake_nic_id' self.req.GET = {} self.req.GET['nic_id'] = nic_id mock_interface.return_value = '' guest.guests_get_nic_info(self.req) mock_interface.assert_called_once_with( 'guests_get_nic_info', userid=None, nic_id=nic_id, vswitch=None) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guests_get_nic_info_with_vswitch(self, mock_interface): vswitch = 'vswitch' self.req.GET = {} self.req.GET['vswitch'] = vswitch mock_interface.return_value = '' guest.guests_get_nic_info(self.req) mock_interface.assert_called_once_with( 'guests_get_nic_info', userid=None, nic_id=None, vswitch=vswitch) # TODO: move this test to sdk layer instead of API layer # or we can use validation to validate cidr @unittest.skip("we use send_request now.....") @mock.patch.object(util, 'wsgi_path_item') def test_guest_create_network_interface_invalid_cidr(self, mock_userid): # / not in cidr bstr = """{"interface": {"os_version": "rhel6", "guest_networks": [ {"ip_addr": "192.168.12.34", "dns_addr": ["9.1.2.3"], "gateway_addr": "192.168.95.1", "cidr": "192.168.95.0", "nic_vdev": "1000", "mac_addr": "02:00:00:12:34:56"}]}}""" self.req.body = bstr mock_userid.return_value = FAKE_USERID self.assertRaises(webob.exc.HTTPBadRequest, guest.guest_create_network_interface, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch('zvmconnector.connector.ZVMConnector.send_request') def test_guest_create_disks(self, mock_create, mock_userid): disk_list = [{u'size': u'1g', 'disk_pool': 'ECKD:poolname'}] body_str = """{"disk_info": {"disk_list": [{"size": "1g", "disk_pool": "ECKD:poolname"} ]}}""" mock_create.return_value = '' self.req.body = body_str mock_userid.return_value = FAKE_USERID guest.guest_create_disks(self.req) mock_create.assert_called_once_with('guest_create_disks', FAKE_USERID, disk_list) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') @mock.patch.object(util, 'wsgi_path_item') def test_guest_config_minidisks(self, mock_userid, mock_config): disk_list = [{'vdev': '0101', 'format': 'ext3', 'mntdir': '/mnt/0101'}] mock_config.return_value = '' body_str = """{"disk_info": {"disk_list": [{"vdev": "0101", "format": "ext3", "mntdir": "/mnt/0101"} ]}}""" self.req.body = body_str mock_userid.return_value = FAKE_USERID guest.guest_config_disks(self.req) mock_config.assert_called_once_with('guest_config_minidisks', FAKE_USERID, disk_list) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') @mock.patch.object(util, 'wsgi_path_item') def test_guest_delete_disks(self, mock_userid, mock_delete): vdev_list = ['0101'] mock_delete.return_value = '' body_str = """{"vdev_info": {"vdev_list": ["0101"]}}""" self.req.body = body_str mock_userid.return_value = FAKE_USERID guest.guest_delete_disks(self.req) mock_delete.assert_called_once_with('guest_delete_disks', FAKE_USERID, vdev_list) @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(guest.VMHandler, 'get_definition_info') def test_guest_get(self, mock_get, mock_userid): mock_get.return_value = '' mock_userid.return_value = FAKE_USERID guest.guest_get(self.req) mock_get.assert_called_once_with(self.req, FAKE_USERID) @mock.patch.object(guest.VMHandler, 'inspect_stats') def test_guest_get_stats(self, mock_get): self.req.GET = FakeReqGet() mock_get.return_value = '{}' guest.guest_get_stats(self.req) mock_get.assert_called_once_with(self.req, FAKE_USERID_LIST) @mock.patch.object(guest.VMHandler, 'inspect_vnics') def test_guest_get_interface_stats(self, mock_get): self.req.GET = FakeReqGet() mock_get.return_value = '{}' guest.guest_get_interface_stats(self.req) mock_get.assert_called_once_with(self.req, FAKE_USERID_LIST) def mock_get_userid_vdev(self, env, param): if param == 'userid': return FAKE_USERID else: return '1000' @mock.patch('zvmconnector.connector.ZVMConnector.send_request') @mock.patch.object(util, 'wsgi_path_item') def test_delete_nic(self, mock_userid, mock_delete): body_str = '{"info": {}}' self.req.body = body_str mock_delete.return_value = {'overallRC': 0} mock_userid.side_effect = self.mock_get_userid_vdev guest.guest_delete_nic(self.req) mock_delete.assert_called_once_with('guest_delete_nic', FAKE_USERID, "1000", active=False) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') @mock.patch.object(util, 'wsgi_path_item') def test_guest_couple_nic(self, mock_userid, mock_couple): body_str = '{"info": {"couple": "true", "vswitch": "vsw1"}}' self.req.body = body_str mock_couple.return_value = '' mock_userid.side_effect = self.mock_get_userid_vdev guest.guest_couple_uncouple_nic(self.req) mock_couple.assert_called_once_with('guest_nic_couple_to_vswitch', FAKE_USERID, "1000", "vsw1", active=False) @mock.patch('zvmconnector.connector.ZVMConnector.send_request') @mock.patch.object(util, 'wsgi_path_item') def test_guest_uncouple_nic(self, mock_userid, mock_uncouple): body_str = '{"info": {"couple": "false"}}' self.req.body = body_str mock_uncouple.return_value = '' mock_userid.side_effect = self.mock_get_userid_vdev guest.guest_couple_uncouple_nic(self.req) mock_uncouple.assert_called_once_with( 'guest_nic_uncouple_from_vswitch', FAKE_USERID, "1000", active=False) @mock.patch.object(util, 'wsgi_path_item') def test_guest_couple_nic_missing_required_1(self, mock_userid): body_str = '{"info": {}}' self.req.body = body_str mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_couple_uncouple_nic, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_uncouple_nic_bad_vswitch(self, mock_userid): body_str = '{"info": {"couple": "false", "active": "dummy"}}' self.req.body = body_str mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_couple_uncouple_nic, self.req) @mock.patch.object(util, 'wsgi_path_item') def test_guest_uncouple_nic_bad_couple(self, mock_userid): body_str = '{"info": {"couple": "couple"}}' self.req.body = body_str mock_userid.return_value = FAKE_USERID self.assertRaises(exception.ValidationError, guest.guest_couple_uncouple_nic, self.req) @mock.patch.object(guest.VMHandler, 'create') def test_guest_create_unauthorized(self, mock_create): body_str = '{"guest": {"userid": "name1", "vcpus": 1, "memory": 1}}' self.req.body = body_str mock_create.side_effect = webob.exc.HTTPBadRequest self.assertRaises(webob.exc.HTTPBadRequest, guest.guest_create, self.req) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/test_host.py0000664000175000017510000000354513371225174026032 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import jwt import mock import unittest from zvmsdk.sdkwsgi.handlers import host class FakeResp(object): def __init__(self): self.body = {} class FakeReq(object): def __init__(self): self.headers = {} self.environ = {} self.__name__ = '' self.response = FakeResp() def __getitem__(self, name): return self.headers class HandlersHostTest(unittest.TestCase): def setUp(self): expired_elapse = datetime.timedelta(seconds=100) expired_time = datetime.datetime.utcnow() + expired_elapse payload = jwt.encode({'exp': expired_time}, 'username') self.req = FakeReq() self.req.headers['X-Auth-Token'] = payload @mock.patch.object(host.HostAction, 'get_info') def test_host_get_info(self, mock_get_info): mock_get_info.return_value = '' host.host_get_info(self.req) self.assertTrue(mock_get_info.called) @mock.patch.object(host.HostAction, 'diskpool_get_info') def test_host_get_disk_info(self, mock_get_disk_info): mock_get_disk_info.return_value = '' self.req.GET = {} self.req.GET['poolname'] = 'disk1' host.host_get_disk_info(self.req) self.assertTrue(mock_get_disk_info.called) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/test_vswitch.py0000664000175000017510000001723313371225174026543 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import datetime import jwt import mock import unittest from zvmsdk import exception from zvmsdk import config from zvmsdk.sdkwsgi.handlers import vswitch from zvmsdk.sdkwsgi import util CONF = config.CONF FAKE_UUID = '00000000-0000-0000-0000-000000000000' def set_conf(section, opt, value): CONF[section][opt] = value class FakeResp(object): def __init__(self): self.body = {} class FakeReq(object): def __init__(self): self.headers = {} self.environ = {} self.__name__ = '' self.response = FakeResp() def __getitem__(self, name): return self.headers class HandlersGuestTest(unittest.TestCase): def setUp(self): set_conf('wsgi', 'auth', 'none') expired_elapse = datetime.timedelta(seconds=100) expired_time = datetime.datetime.utcnow() + expired_elapse payload = jwt.encode({'exp': expired_time}, 'username') self.req = FakeReq() self.req.headers['X-Auth-Token'] = payload @mock.patch.object(vswitch.VswitchAction, 'list') def test_vswitch_list(self, mock_list): mock_list.return_value = '' vswitch.vswitch_list(self.req) self.assertTrue(mock_list.called) @mock.patch.object(vswitch.VswitchAction, 'create') def test_vswitch_create(self, mock_create): mock_create.return_value = {} body_str = """{"vswitch": {"name": "name1", "rdev": "1234 abcd 123F", "port_type": 1, "controller": "*"}}""" self.req.body = body_str vswitch.vswitch_create(self.req) body = util.extract_json(body_str) mock_create.assert_called_once_with(body=body) @mock.patch.object(vswitch.VswitchAction, 'create') def test_vswitch_create_with_userid_controller(self, mock_create): mock_create.return_value = {} body_str = """{"vswitch": {"name": "name1", "rdev": "1234 abcd 123F", "port_type": 1, "controller": "userid01"}}""" self.req.body = body_str vswitch.vswitch_create(self.req) body = util.extract_json(body_str) mock_create.assert_called_once_with(body=body) def test_vswitch_create_invalidname(self): body_str = '{"vswitch": {"name": "", "rdev": "1234"}}' self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_rdevlist(self): body_str = '{"vswitch": {"name": "name1", "rdev": "12345 sss"}}' self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(vswitch.VswitchAction, 'delete') def test_vswitch_delete(self, mock_delete, mock_name): mock_delete.return_value = {} mock_name.return_value = 'vsw1' vswitch.vswitch_delete(self.req) mock_delete.assert_called_once_with('vsw1') @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(vswitch.VswitchAction, 'query') def test_vswitch_query(self, mock_query, mock_name): mock_query.return_value = {} mock_name.return_value = 'vsw1' vswitch.vswitch_query(self.req) mock_query.assert_called_once_with('vsw1') def test_vswitch_create_invalid_connection(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "connection": 3}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_queue_mem(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "queue_mem": 10}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_network_type(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "network_type": 3}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_update(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "update": 4}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_vid(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "vid": -1}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_native_vid(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "native_vid": 4096}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_router(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "router": 3}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_grvp(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "gvrp": 3}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) def test_vswitch_create_invalid_controller(self): body_str = """{"vswitch": {"name": "name1", "rdev": "1234", "controller": "node12345"}}""" self.req.body = body_str self.assertRaises(exception.ValidationError, vswitch.vswitch_create, self.req) @mock.patch.object(util, 'wsgi_path_item') @mock.patch.object(vswitch.VswitchAction, 'update') def test_vswitch_update(self, mock_update, mock_name): mock_name.return_value = 'vsw1' body_str = '{"vswitch": {"grant_userid": "user1"}}' mock_update.return_value = {} self.req.body = body_str vswitch.vswitch_update(self.req) body = util.extract_json(body_str) mock_update.assert_called_once_with('vsw1', body=body) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/handlers/test_version.py0000664000175000017510000000321213371225174026531 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import json import mock import unittest from zvmsdk.sdkwsgi.handlers import version from zvmsdk import version as sdk_version class HandlersRootTest(unittest.TestCase): def setUp(self): pass def test_version(self): req = mock.Mock() ver_str = {"rc": 0, "overallRC": 0, "errmsg": "", "modID": None, "output": {"api_version": version.APIVERSION, "min_version": version.APIVERSION, "version": sdk_version.__version__, "max_version": version.APIVERSION, }, "rs": 0} res = version.version(req) self.assertEqual('application/json', req.response.content_type) # version_json = json.dumps(ver_res) # version_str = utils.to_utf8(version_json) ver_res = json.loads(req.response.body.decode('utf-8')) self.assertEqual(ver_str, ver_res) self.assertEqual('application/json', res.content_type) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkwsgi/test_handler.py0000664000175000017510000006442413371225174024675 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import testtools import unittest import webob.exc from zvmsdk import exception from zvmsdk.sdkwsgi import handler from zvmsdk.sdkwsgi.handlers import tokens env = {'SERVER_SOFTWARE': 'WSGIServer/0.1 Python/2.7.3', 'SCRIPT_NAME': '', 'REQUEST_METHOD': 'GET', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_PORT': '8001', 'HTTP_HOST': '127.0.0.1:8001', 'wsgi.version': (1, 0), 'HTTP_ACCEPT': '*/*', 'LESSCLOSE': '/usr/bin/lesspipe %s %s', 'wsgi.run_once': False, 'QUERY_STRING': '', 'wsgi.multiprocess': False, 'SERVER_NAME': 'localhost', 'REMOTE_ADDR': '127.0.0.1', 'wsgi.url_scheme': 'http', 'CONTENT_LENGTH': '', 'wsgi.multithread': True, 'CONTENT_TYPE': 'text/plain', 'REMOTE_HOST': 'localhost'} def dummy(status, headerlist): pass class GuestActionNegativeTest(unittest.TestCase): def setUp(self): self.env = env def test_guest_invalid_resource(self): self.env['PATH_INFO'] = '/guests/1/action' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) class GuestActionTest(unittest.TestCase): def setUp(self): self.env = env @mock.patch('zvmsdk.sdkwsgi.util.extract_json') def test_guest_start(self, mock_json): mock_json.return_value = {"action": "start"} self.env['PATH_INFO'] = '/guests/1/action' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() with mock.patch('zvmsdk.sdkwsgi.handlers.guest.VMAction.start') \ as start: start.return_value = {'overallRC': 0} h(self.env, dummy) start.assert_called_once_with('1', body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') def test_guest_deploy(self, mock_json): mock_json.return_value = {"action": "deploy"} self.env['PATH_INFO'] = '/guests/1/action' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() with mock.patch('zvmsdk.sdkwsgi.handlers.guest.VMAction.deploy') \ as deploy: deploy.return_value = {'overallRC': 0} h(self.env, dummy) deploy.assert_called_once_with('1', body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') def test_guest_stop(self, mock_json): mock_json.return_value = {"action": "stop"} self.env['PATH_INFO'] = '/guests/1/action' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() with mock.patch('zvmsdk.sdkwsgi.handlers.guest.VMAction.stop') \ as stop: stop.return_value = {'overallRC': 0} h(self.env, dummy) stop.assert_called_once_with('1', body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') def test_guest_softstop(self, mock_json): mock_json.return_value = {"action": "softstop"} self.env['PATH_INFO'] = '/guests/1/action' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() with mock.patch('zvmsdk.sdkwsgi.handlers.guest.VMAction.softstop') \ as stop: stop.return_value = {'overallRC': 0} h(self.env, dummy) stop.assert_called_once_with('1', body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') def test_guest_get_console_output(self, mock_json): mock_json.return_value = {"action": "get_console_output"} self.env['PATH_INFO'] = '/guests/1/action' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() url = 'zvmsdk.sdkwsgi.handlers.guest.VMAction.get_console_output' with mock.patch(url) as get_console_output: get_console_output.return_value = {'overallRC': 0} h(self.env, dummy) get_console_output.assert_called_once_with('1', body={}) class GuestHandlerNegativeTest(unittest.TestCase): def setUp(self): self.env = env def test_guest_invalid_resource(self): self.env['PATH_INFO'] = '/gueba' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPNotFound, h, self.env, dummy) def test_guest_list_invalid(self): self.env['PATH_INFO'] = '/guests' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) def test_guest_stats_method_invalid(self): self.env['PATH_INFO'] = '/guests/stats' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) def test_guest_get_info_method_invalid(self): self.env['PATH_INFO'] = '/guests/1/info' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) def test_guest_get_info_resource_invalid(self): self.env['PATH_INFO'] = '/guests/1/info1' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPNotFound, h, self.env, dummy) @testtools.skip('temply disable because of volume not support now') def test_guest_volume_invalid_method(self): self.env['PATH_INFO'] = '/guests/1/volumes' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) def test_guest_network_interface_invalid_method(self): self.env['PATH_INFO'] = '/guests/1/interface' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) class GuestHandlerTest(unittest.TestCase): def setUp(self): self.env = env @mock.patch.object(tokens, 'validate') def test_guest_list(self, mock_validate): self.env['PATH_INFO'] = '/guests' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() with mock.patch('zvmsdk.sdkwsgi.handlers.guest.VMHandler.list') \ as list: list.return_value = {'overallRC': 0} h(self.env, dummy) self.assertTrue(list.called) @mock.patch.object(tokens, 'validate') def test_guest_get_info(self, mock_validate): self.env['PATH_INFO'] = '/guests/1/info' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() with mock.patch('zvmsdk.sdkwsgi.handlers.guest.VMHandler.get_info') \ as get_info: get_info.return_value = {'overallRC': 0} h(self.env, dummy) get_info.assert_called_once_with(mock.ANY, '1') @mock.patch.object(tokens, 'validate') def test_guest_get(self, mock_validate): self.env['PATH_INFO'] = '/guests/1' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler.get_definition_info' with mock.patch(func) as get: get.return_value = {'overallRC': 0} h(self.env, dummy) get.assert_called_once_with(mock.ANY, '1') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_delete_nic(self, mock_validate, mock_json): mock_json.return_value = '' self.env['PATH_INFO'] = '/guests/1/nic/1000' self.env['REQUEST_METHOD'] = 'DELETE' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler.delete_nic' with mock.patch(func) as delete_nic: delete_nic.return_value = {'overallRC': 0} h(self.env, dummy) delete_nic.assert_called_once_with('1', '1000', '') @mock.patch.object(tokens, 'validate') def test_guest_get_power_state(self, mock_validate): self.env['PATH_INFO'] = '/guests/1/power_state' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler'\ '.get_power_state' with mock.patch(func) as get_power: get_power.return_value = {'overallRC': 0} h(self.env, dummy) get_power.assert_called_once_with(mock.ANY, '1') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_create(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/guests' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler.create' with mock.patch(func) as create: create.return_value = {'overallRC': 0} h(self.env, dummy) self.assertTrue(create.called) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_delete(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/guests/1' self.env['REQUEST_METHOD'] = 'DELETE' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler.delete' with mock.patch(func) as delete: delete.return_value = {'overallRC': 0} h(self.env, dummy) delete.assert_called_once_with('1') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_create_nic(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/guests/1/nic' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler.create_nic' with mock.patch(func) as create_nic: create_nic.return_value = {'overallRC': 0} h(self.env, dummy) create_nic.assert_called_once_with('1', body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_update_nic(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/guests/1/nic/1000' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.guest.VMHandler.nic_couple_uncouple' with mock.patch(func) as update_nic: update_nic.return_value = {'overallRC': 0} h(self.env, dummy) update_nic.assert_called_once_with('1', '1000', body={}) @mock.patch.object(tokens, 'validate') def test_guest_get_stats_empty_userid_list(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/stats' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = '' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as get_info: get_info.return_value = {'overallRC': 0} h(self.env, dummy) get_info.assert_called_once_with('guest_inspect_stats', []) @mock.patch.object(tokens, 'validate') def test_guest_get_stats_userid_list(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/stats' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = 'userid=l1,l2' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as get_info: get_info.return_value = {'overallRC': 0} h(self.env, dummy) get_info.assert_called_once_with('guest_inspect_stats', ['l1', 'l2']) @mock.patch.object(tokens, 'validate') def test_guest_get_stats_invalid(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/stats' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = 'userid=l1,l2&userd=l3,l4' h = handler.SdkHandler() self.assertRaises(exception.ValidationError, h, self.env, dummy) @mock.patch.object(tokens, 'validate') def test_guest_get_interface_stats_empty_userid_list(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/interfacestats' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = '' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as get_info: get_info.return_value = {'overallRC': 0} h(self.env, dummy) get_info.assert_called_once_with('guest_inspect_vnics', []) @mock.patch.object(tokens, 'validate') def test_guest_get_interface_stats_user_list(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/interfacestats' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = 'userid=l1,l2' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as get_info: get_info.return_value = {'overallRC': 0} h(self.env, dummy) get_info.assert_called_once_with('guest_inspect_vnics', ['l1', 'l2']) @mock.patch.object(tokens, 'validate') def test_guest_get_interface_stats_invalid(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/interfacestats' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = 'use=l1,l2' h = handler.SdkHandler() self.assertRaises(exception.ValidationError, h, self.env, dummy) @mock.patch.object(tokens, 'validate') def test_guests_get_nic_info_without_limitation(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/nics' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = '' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as guests_get_nic_info: guests_get_nic_info.return_value = {'overallRC': 0} h(self.env, dummy) guests_get_nic_info.assert_called_once_with('guests_get_nic_info', userid=None, nic_id=None, vswitch=None) @mock.patch.object(tokens, 'validate') def test_guests_get_nic_info_with_userid(self, mock_validate): self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/nics' self.env['REQUEST_METHOD'] = 'GET' self.env['QUERY_STRING'] = 'userid=test' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as guests_get_nic_info: guests_get_nic_info.return_value = {'overallRC': 0} h(self.env, dummy) guests_get_nic_info.assert_called_once_with('guests_get_nic_info', userid='test', nic_id=None, vswitch=None) @testtools.skip('temply disable because of volume not support now') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_attach_volume(self, mock_validate, mock_json): mock_json.return_value = {} self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/1/volumes' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.volume.VolumeAction.attach' with mock.patch(func) as attach: attach.return_value = {'overallRC': 0} h(self.env, dummy) attach.assert_called_once_with('1', {}) @testtools.skip('temply disable because of volume not support now') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def _test_guest_detach_volume(self, mock_validate, mock_json): mock_json.return_value = {} self.env['wsgiorg.routing_args'] = () self.env['PATH_INFO'] = '/guests/1/volumes' self.env['REQUEST_METHOD'] = 'DELETE' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.volume.VolumeAction.detach' with mock.patch(func) as detach: detach.return_value = {'overallRC': 0} h(self.env, dummy) detach.assert_called_once_with('1', {}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_delete_network_interface(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/guests/1/interface' self.env['REQUEST_METHOD'] = 'DELETE' h = handler.SdkHandler() func = ('zvmsdk.sdkwsgi.handlers.guest.VMHandler.' 'delete_network_interface') with mock.patch(func) as delete_network_interface: delete_network_interface.return_value = {'overallRC': 0} h(self.env, dummy) delete_network_interface.assert_called_once_with('1', body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_guest_create_network_interface(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/guests/1/interface' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() func = ('zvmsdk.sdkwsgi.handlers.guest.VMHandler.' 'create_network_interface') with mock.patch(func) as create_network_interface: create_network_interface.return_value = {'overallRC': 0} h(self.env, dummy) create_network_interface.assert_called_once_with('1', body={}) class ImageHandlerNegativeTest(unittest.TestCase): def setUp(self): self.env = env def test_image_create_invalid_method(self): self.env['PATH_INFO'] = '/images' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) def test_image_get_root_disk_size_invalid(self): self.env['PATH_INFO'] = '/images/image1/root_size' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPNotFound, h, self.env, dummy) class ImageHandlerTest(unittest.TestCase): def setUp(self): self.env = env @mock.patch.object(tokens, 'validate') def test_image_root_disk_size(self, mock_validate): self.env['PATH_INFO'] = '/images/image1/root_disk_size' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as get_size: get_size.return_value = {'overallRC': 0} h(self.env, dummy) get_size.assert_called_once_with('image_get_root_disk_size', 'image1') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_image_create(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/images' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() func = 'zvmsdk.sdkwsgi.handlers.image.ImageAction.create' with mock.patch(func) as create: create.return_value = {'overallRC': 0} h(self.env, dummy) create.assert_called_once_with(body={}) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_image_delete(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/images/image1' self.env['REQUEST_METHOD'] = 'DELETE' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as delete: delete.return_value = {'overallRC': 0} h(self.env, dummy) delete.assert_called_once_with('image_delete', 'image1') @mock.patch.object(tokens, 'validate') def test_image_query(self, mock_validate): self.env['PATH_INFO'] = '/images' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() func = 'zvmconnector.connector.ZVMConnector.send_request' with mock.patch(func) as query: query.return_value = {'overallRC': 0} h(self.env, dummy) query.assert_called_once_with('image_query', None) class HostHandlerNegativeTest(unittest.TestCase): def setUp(self): self.env = env def test_host_get_resource_invalid(self): self.env['PATH_INFO'] = '/host1' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPNotFound, h, self.env, dummy) def test_host_get_info_invalid(self): self.env['PATH_INFO'] = '/host/inf' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPNotFound, h, self.env, dummy) def test_host_get_disk_size_invalid(self): self.env['PATH_INFO'] = '/host/disk_inf/d1' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPNotFound, h, self.env, dummy) class HostHandlerTest(unittest.TestCase): def setUp(self): self.env = env @mock.patch.object(tokens, 'validate') def test_host_get_info(self, mock_validate): self.env['PATH_INFO'] = '/host' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.host.HostAction.get_info' with mock.patch(function) as get_info: get_info.return_value = {'overallRC': 0} h(self.env, dummy) self.assertTrue(get_info.called) @mock.patch.object(tokens, 'validate') def test_host_get_disk_info(self, mock_validate): self.env['PATH_INFO'] = '/host/diskpool' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.host.HostAction.diskpool_get_info' with mock.patch(function) as get_disk_info: get_disk_info.return_value = {'overallRC': 0} h(self.env, dummy) get_disk_info.assert_called_once_with(mock.ANY, None) class VswitchHandlerNegativeTest(unittest.TestCase): def setUp(self): self.env = env def test_vswitch_put_method_invalid(self): self.env['PATH_INFO'] = '/vswitches' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() self.assertRaises(webob.exc.HTTPMethodNotAllowed, h, self.env, dummy) class VswitchHandlerTest(unittest.TestCase): def setUp(self): self.env = env @mock.patch.object(tokens, 'validate') def test_vswitch_list(self, mock_validate): self.env['PATH_INFO'] = '/vswitches' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.vswitch.VswitchAction.list' with mock.patch(function) as list: list.return_value = {'overallRC': 0} h(self.env, dummy) self.assertTrue(list.called) @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_vswitch_create(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/vswitches' self.env['REQUEST_METHOD'] = 'POST' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.vswitch.VswitchAction.create' with mock.patch(function) as create: create.return_value = {'overallRC': 0} h(self.env, dummy) self.assertTrue(create.called) @mock.patch.object(tokens, 'validate') def test_vswitch_delete(self, mock_validate): self.env['PATH_INFO'] = '/vswitches/vsw1' self.env['REQUEST_METHOD'] = 'DELETE' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.vswitch.VswitchAction.delete' with mock.patch(function) as delete: delete.return_value = {'overallRC': 0} h(self.env, dummy) delete.assert_called_once_with('vsw1') @mock.patch.object(tokens, 'validate') def test_vswitch_query(self, mock_validate): self.env['PATH_INFO'] = '/vswitches/vsw1' self.env['REQUEST_METHOD'] = 'GET' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.vswitch.VswitchAction.query' with mock.patch(function) as query: query.return_value = {'overallRC': 0} h(self.env, dummy) query.assert_called_once_with('vsw1') @mock.patch('zvmsdk.sdkwsgi.util.extract_json') @mock.patch.object(tokens, 'validate') def test_vswitch_update(self, mock_validate, mock_json): mock_json.return_value = {} self.env['PATH_INFO'] = '/vswitches/vsw1' self.env['REQUEST_METHOD'] = 'PUT' h = handler.SdkHandler() function = 'zvmsdk.sdkwsgi.handlers.vswitch.VswitchAction.update' with mock.patch(function) as update: update.return_value = {'overallRC': 0} h(self.env, dummy) update.assert_called_once_with('vsw1', body={}) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_database.py0000664000175000017510000007440213371225174023346 0ustar ruirui00000000000000# 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. import mock import uuid from zvmsdk import config from zvmsdk import database from zvmsdk import exception from zvmsdk import log from zvmsdk.tests.unit import base CONF = config.CONF LOG = log.LOG class NetworkDbOperatorTestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(NetworkDbOperatorTestCase, cls).setUpClass() cls.db_op = database.NetworkDbOperator() cls.userid = 'FAKEUSER' cls.rec_list = [('ID01', '1000', 'port_id01'), ('ID01', '2000', 'port_id02'), ('ID02', '1000', 'port_id02'), ('ID03', '1000', 'port_id03')] @classmethod def tearDownClass(cls): with database.get_network_conn() as conn: conn.execute("DROP TABLE switch") super(NetworkDbOperatorTestCase, cls).tearDownClass() @mock.patch.object(database.NetworkDbOperator, '_create_switch_table') def test__init__(self, create_table): self.db_op.__init__() create_table.assert_called_once_with() def test_switch_add_record(self): interface = '1000' port = None # insert a record without port self.db_op.switch_add_record(self.userid, interface, port) # query switch_record = self.db_op.switch_select_table() expected = [{'userid': self.userid, 'interface': interface, 'switch': None, 'port': port, 'comments': None}] self.assertEqual(expected, switch_record) # clean test switch self.db_op.switch_delete_record_for_userid(self.userid) port = 'testport' # insert a record with port self.db_op.switch_add_record(self.userid, interface, port) # query switch_record = self.db_op.switch_select_table() expected = [{'userid': self.userid, 'interface': interface, 'switch': None, 'port': port, 'comments': None}] self.assertEqual(expected, switch_record) # clean test switch self.db_op.switch_delete_record_for_userid(self.userid) switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) def test_switch_add_record_migrated(self): interface = '1000' switch = 'XCATVSW1' self.db_op.switch_add_record_migrated(self.userid, interface, switch) # query switch_record = self.db_op.switch_select_table() expected = [{'userid': self.userid, 'interface': interface, 'switch': switch, 'port': None, 'comments': None}] self.assertEqual(expected, switch_record) # clean test switch self.db_op.switch_delete_record_for_userid(self.userid) @mock.patch.object(database.NetworkDbOperator, '_get_switch_by_user_interface') def test_switch_update_record_with_switch_fail(self, get_record): get_record.return_value = None interface = '1000' switch = 'testswitch' self.assertRaises(exception.SDKObjectNotExistError, self.db_op.switch_update_record_with_switch, self.userid, interface, switch) def test_switch_update_record_with_switch(self): interface = '1000' port = 'testport' switch = 'testswitch' # insert a record first self.db_op.switch_add_record(self.userid, interface, port) # update record with switch info self.db_op.switch_update_record_with_switch(self.userid, interface, switch) # query switch_record = self.db_op.switch_select_table() expected = [{'userid': self.userid, 'interface': interface, 'switch': switch, 'port': port, 'comments': None}] self.assertEqual(expected, switch_record) switch = None # update record to remove switch info self.db_op.switch_update_record_with_switch(self.userid, interface, switch) # query switch_record = self.db_op.switch_select_table() expected = [{'userid': self.userid, 'interface': interface, 'switch': switch, 'port': port, 'comments': None}] self.assertEqual(expected, switch_record) # clean test switch self.db_op.switch_delete_record_for_userid(self.userid) switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) def test_switch_delete_record_for_userid(self): # insert multiple records for (userid, interface, port) in self.rec_list: self.db_op.switch_add_record(userid, interface, port) self.addCleanup(self.db_op.switch_delete_record_for_userid, userid) # delete specific records userid = 'ID01' self.db_op.switch_delete_record_for_userid(userid) # query: specific records removed switch_record = self.db_op.switch_select_record_for_userid(userid) expected = [] self.assertEqual(expected, switch_record) # query: the other records still exist switch_record = self.db_op.switch_select_record_for_userid('ID02') expected = [{'userid': 'ID02', 'interface': '1000', 'switch': None, 'port': 'port_id02', 'comments': None}] self.assertEqual(expected, switch_record) switch_record = self.db_op.switch_select_record_for_userid('ID03') expected = [{'userid': 'ID03', 'interface': '1000', 'switch': None, 'port': 'port_id03', 'comments': None}] self.assertEqual(expected, switch_record) def test_switch_delete_record_for_nic(self): # insert multiple records for (userid, interface, port) in self.rec_list: self.db_op.switch_add_record(userid, interface, port) self.addCleanup(self.db_op.switch_delete_record_for_userid, userid) # query: specific record in the table record = {'userid': 'ID01', 'interface': '1000', 'switch': None, 'port': 'port_id01', 'comments': None} switch_record = self.db_op.switch_select_table() self.assertEqual(record in switch_record, True) # delete one specific record userid = 'ID01' interface = '1000' self.db_op.switch_delete_record_for_nic(userid, interface) # query: specific record not in the table switch_record = self.db_op.switch_select_table() self.assertEqual(record not in switch_record, True) # clean test switch self.db_op.switch_delete_record_for_userid('ID01') self.db_op.switch_delete_record_for_userid('ID02') self.db_op.switch_delete_record_for_userid('ID03') switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) def test_switch_select_table(self): # empty table switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) # insert multiple records for (userid, interface, port) in self.rec_list: self.db_op.switch_add_record(userid, interface, port) self.addCleanup(self.db_op.switch_delete_record_for_userid, userid) # query: specific record in the table record = [{'userid': 'ID01', 'interface': '1000', 'switch': None, 'port': 'port_id01', 'comments': None}, {'userid': 'ID01', 'interface': '2000', 'switch': None, 'port': 'port_id02', 'comments': None}, {'userid': 'ID02', 'interface': '1000', 'switch': None, 'port': 'port_id02', 'comments': None}, {'userid': 'ID03', 'interface': '1000', 'switch': None, 'port': 'port_id03', 'comments': None}] switch_record = self.db_op.switch_select_table() self.assertEqual(record, switch_record) # clean test switch self.db_op.switch_delete_record_for_userid('ID01') self.db_op.switch_delete_record_for_userid('ID02') self.db_op.switch_delete_record_for_userid('ID03') switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) def test_switch_select_record_for_userid(self): # insert multiple records for (userid, interface, port) in self.rec_list: self.db_op.switch_add_record(userid, interface, port) self.addCleanup(self.db_op.switch_delete_record_for_userid, userid) # query: specific record in the table record = [{'userid': 'ID01', 'interface': '1000', 'switch': None, 'port': 'port_id01', 'comments': None}, {'userid': 'ID01', 'interface': '2000', 'switch': None, 'port': 'port_id02', 'comments': None}] switch_record = self.db_op.switch_select_record_for_userid('ID01') self.assertEqual(record, switch_record) # clean test switch self.db_op.switch_delete_record_for_userid('ID01') self.db_op.switch_delete_record_for_userid('ID02') self.db_op.switch_delete_record_for_userid('ID03') switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) def test_switch_select_record(self): # insert multiple records for (userid, interface, port) in self.rec_list: self.db_op.switch_add_record(userid, interface, port) self.addCleanup(self.db_op.switch_delete_record_for_userid, userid) # all record record = [{'userid': 'ID01', 'interface': '1000', 'switch': 'switch01', 'port': 'port_id01', 'comments': None}, {'userid': 'ID01', 'interface': '2000', 'switch': 'switch01', 'port': 'port_id02', 'comments': None}, {'userid': 'ID02', 'interface': '1000', 'switch': 'switch02', 'port': 'port_id02', 'comments': None}, {'userid': 'ID03', 'interface': '1000', 'switch': 'switch02', 'port': 'port_id03', 'comments': None}] # update record with switch info self.db_op.switch_update_record_with_switch('ID01', '1000', 'switch01') self.db_op.switch_update_record_with_switch('ID01', '2000', 'switch01') self.db_op.switch_update_record_with_switch('ID02', '1000', 'switch02') self.db_op.switch_update_record_with_switch('ID03', '1000', 'switch02') switch_record = self.db_op.switch_select_record() self.assertEqual(record, switch_record) switch_record = self.db_op.switch_select_record(userid='ID01') self.assertEqual([record[0], record[1]], switch_record) switch_record = self.db_op.switch_select_record(nic_id='port_id02') self.assertEqual([record[1], record[2]], switch_record) switch_record = self.db_op.switch_select_record(vswitch='switch02') self.assertEqual([record[2], record[3]], switch_record) switch_record = self.db_op.switch_select_record(nic_id='port_id02', vswitch='switch02') self.assertEqual([record[2]], switch_record) # clean test switch self.db_op.switch_delete_record_for_userid('ID01') self.db_op.switch_delete_record_for_userid('ID02') self.db_op.switch_delete_record_for_userid('ID03') switch_record = self.db_op.switch_select_table() expected = [] self.assertEqual(expected, switch_record) class FCPDbOperatorTestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(FCPDbOperatorTestCase, cls).setUpClass() cls.db_op = database.FCPDbOperator() # tearDownClass deleted to work around bug of 'no such table:fcp' def test_new(self): self.db_op.new('1111') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.db_op.delete('1111') def test_assign(self): self.db_op.new('1111') try: self.db_op.assign('1111', 'incuser') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.db_op.increase_usage('1111') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual(2, fcp[2]) finally: self.db_op.delete('1111') def test_get_all_free(self): self.db_op.new('1111') self.db_op.new('1112') self.db_op.new('1113') self.db_op.new('1114') try: self.db_op.assign('1111', 'user1') self.db_op.assign('1112', 'user2') self.db_op.decrease_usage('1112') self.db_op.reserve('1114') fcp_list = self.db_op.get_all_free_unreserved() self.assertEqual(2, len(fcp_list)) fcp_list.sort() fcp = fcp_list[0] self.assertEqual('1112', fcp[0]) fcp = fcp_list[1] self.assertEqual('1113', fcp[0]) finally: self.db_op.delete('1111') self.db_op.delete('1112') self.db_op.delete('1113') self.db_op.delete('1114') def test_get_from_assigner(self): self.db_op.new('1111') self.db_op.new('1112') try: self.db_op.assign('1111', 'user1') self.db_op.assign('1112', 'user2') fcp_list = self.db_op.get_from_assigner('user2') self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1112', fcp[0]) self.assertEqual('user2', fcp[1]) finally: self.db_op.delete('1111') self.db_op.delete('1112') def test_get_from_fcp(self): self.db_op.new('1111') self.db_op.new('1112') try: self.db_op.assign('1111', 'user1') self.db_op.assign('1112', 'user2') fcp_list = self.db_op.get_from_fcp('1111') self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual('user1', fcp[1]) finally: self.db_op.delete('1111') self.db_op.delete('1112') def test_reserve_unreserve(self): self.db_op.new('1111') try: self.db_op.reserve('1111') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual(1, fcp[3]) self.db_op.unreserve('1111') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual(0, fcp[3]) finally: self.db_op.delete('1111') def test_find_and_reserve(self): self.db_op.new('1111') self.db_op.new('1112') try: fcp = self.db_op.find_and_reserve() self.assertEqual('1111', fcp) fcp_list = self.db_op.get_from_fcp('1111') self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual(1, fcp[3]) finally: self.db_op.delete('1111') self.db_op.delete('1112') def test_decrease_usage(self): self.db_op.new('1111') try: self.db_op.assign('1111', 'decuser') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.db_op.increase_usage('1111') self.db_op.increase_usage('1111') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual(3, fcp[2]) self.db_op.decrease_usage('1111') fcp_list = self.db_op.get_all() self.assertEqual(1, len(fcp_list)) fcp = fcp_list[0] self.assertEqual('1111', fcp[0]) self.assertEqual(2, fcp[2]) finally: self.db_op.delete('1111') class GuestDbOperatorTestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(GuestDbOperatorTestCase, cls).setUpClass() cls.db_op = database.GuestDbOperator() cls.userid = 'FAKEUSER' @classmethod def tearDownClass(cls): with database.get_guest_conn() as conn: conn.execute("DROP TABLE guests") super(GuestDbOperatorTestCase, cls).tearDownClass() @mock.patch.object(uuid, 'uuid4') def test_add_guest(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Query, the guest should in table guests = self.db_op.get_guest_list() self.assertEqual(1, len(guests)) self.assertListEqual([(u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'fakemeta=1, fakemeta2=True', 0, u'')], guests) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_add_guest_migrated(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' net = 1 get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest_migrated(self.userid, meta, net) # Query, the guest should in table guests = self.db_op.get_guest_list() self.assertEqual(1, len(guests)) self.assertListEqual([(u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'fakemeta=1, fakemeta2=True', 1, None)], guests) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_add_guest_twice_error(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Add same user the second time self.assertRaises(exception.SDKGuestOperationError, self.db_op.add_guest, self.userid) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_delete_guest_by_id(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Delete self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') guests = self.db_op.get_guest_list() self.assertListEqual([], guests) def test_delete_guest_by_id_not_exist(self): self.db_op.delete_guest_by_id('Fakeid') @mock.patch.object(uuid, 'uuid4') def test_delete_guest_by_userid(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Delete self.db_op.delete_guest_by_userid(self.userid) guests = self.db_op.get_guest_list() self.assertListEqual([], guests) def test_delete_guest_by_userid_not_exist(self): self.db_op.delete_guest_by_id(self.userid) @mock.patch.object(uuid, 'uuid4') def test_get_guest_by_userid(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # get guest guest = self.db_op.get_guest_by_userid(self.userid) self.assertEqual((u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'fakemeta=1, fakemeta2=True', 0, u''), guest) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_get_metadata_by_userid(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77d' self.db_op.add_guest(self.userid, meta=meta) # get metadata metadata = self.db_op.get_metadata_by_userid(self.userid) self.assertEqual(meta, metadata) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77d') def test_get_guest_by_userid_not_exist(self): guest = self.db_op.get_guest_by_userid(self.userid) self.assertEqual(None, guest) @mock.patch.object(uuid, 'uuid4') def test_get_guest_by_id(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # get guest guest = self.db_op.get_guest_by_id( 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') self.assertEqual((u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'fakemeta=1, fakemeta2=True', 0, u''), guest) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') def test_get_guest_by_id_not_exist(self): guest = self.db_op.get_guest_by_id( 'aa8f352e-4c9e-4335-aafa-4f4eb2fcc77c') self.assertEqual(None, guest) @mock.patch.object(uuid, 'uuid4') def test_update_guest_by_id(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Update self.db_op.update_guest_by_id( 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', meta='newmeta', net_set='1', comments='newcomment') guest = self.db_op.get_guest_by_id( 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') self.assertEqual((u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'newmeta', 1, u'newcomment'), guest) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_update_guest_by_id_wrong_input(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Update self.assertRaises(exception.SDKInternalError, self.db_op.update_guest_by_id, 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') def test_update_guest_by_id_not_exist(self): self.assertRaises(exception.SDKObjectNotExistError, self.db_op.update_guest_by_id, 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', meta='newmeta') @mock.patch.object(uuid, 'uuid4') def test_update_guest_by_id_null_value(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Update self.db_op.update_guest_by_id( 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', meta='', comments='') guest = self.db_op.get_guest_by_id( 'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') self.assertEqual((u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'', 0, u''), guest) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_update_guest_by_userid(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Update self.db_op.update_guest_by_userid( self.userid, meta='newmetauserid', net_set='1', comments={'newcommentuserid': '1'}) guest = self.db_op.get_guest_by_userid(self.userid) self.assertEqual((u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'newmetauserid', 1, u'{"newcommentuserid": "1"}'), guest) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') @mock.patch.object(uuid, 'uuid4') def test_update_guest_by_userid_wrong_input(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Update self.assertRaises(exception.SDKInternalError, self.db_op.update_guest_by_userid, self.userid) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') def test_update_guest_by_userid_not_exist(self): self.assertRaises(exception.SDKObjectNotExistError, self.db_op.update_guest_by_userid, self.userid, meta='newmeta') @mock.patch.object(uuid, 'uuid4') def test_update_guest_by_userid_null_value(self, get_uuid): meta = 'fakemeta=1, fakemeta2=True' get_uuid.return_value = u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c' self.db_op.add_guest(self.userid, meta=meta) # Update self.db_op.update_guest_by_userid( self.userid, meta='', comments='') guest = self.db_op.get_guest_by_userid(self.userid) self.assertEqual((u'ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c', u'FAKEUSER', u'', 0, u'""'), guest) self.db_op.delete_guest_by_id('ad8f352e-4c9e-4335-aafa-4f4eb2fcc77c') class ImageDbOperatorTestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(ImageDbOperatorTestCase, cls).setUpClass() cls.db_op = database.ImageDbOperator() @classmethod def tearDownClass(cls): with database.get_image_conn() as conn: conn.execute("DROP TABLE image") super(ImageDbOperatorTestCase, cls).tearDownClass() def test_image_add_query_delete_record(self): imagename = 'test' imageosdistro = 'rhel6.5' md5sum = 'c73ce117eef8077c3420bfc8f473ac2f' disk_size_units = '3338:CYL' image_size_in_bytes = '5120000' type = 'netboot' # Add an record self.db_op.image_add_record( imagename, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type) # Query the record image_record = self.db_op.image_query_record(imagename) self.assertEqual(1, len(image_record)) self.assertListEqual( [{'imagename': u'test', 'imageosdistro': u'rhel6.5', 'md5sum': u'c73ce117eef8077c3420bfc8f473ac2f', 'disk_size_units': u'3338:CYL', 'image_size_in_bytes': u'5120000', 'type': u'netboot', 'comments': None}], image_record) # Delete it self.db_op.image_delete_record(imagename) self.assertRaises(exception.SDKObjectNotExistError, self.db_op.image_query_record, imagename) def test_image_add_record_with_existing_imagename(self): imagename = 'test' imageosdistro = 'rhel6.5' md5sum = 'c73ce117eef8077c3420bfc8f473ac2f' disk_size_units = '3338:CYL' image_size_in_bytes = '5120000' type = 'netboot' # Add an record self.db_op.image_add_record( imagename, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type) self.assertRaises( exception.SDKDatabaseException, self.db_op.image_add_record, imagename, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type) self.db_op.image_delete_record(imagename) def test_image_query_record_multiple_image(self): imagename1 = 'testimage1' imagename2 = 'testimage2' imageosdistro = 'rhel6.5' md5sum = 'c73ce117eef8077c3420bfc8f473ac2f' disk_size_units = '3338:CYL' image_size_in_bytes = '5120000' type = 'netboot' # Add two records self.db_op.image_add_record( imagename1, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type) self.db_op.image_add_record( imagename2, imageosdistro, md5sum, disk_size_units, image_size_in_bytes, type) image_records = self.db_op.image_query_record() self.assertEqual(2, len(image_records)) self.assertListEqual( [{'imagename': u'testimage1', 'imageosdistro': u'rhel6.5', 'md5sum': u'c73ce117eef8077c3420bfc8f473ac2f', 'disk_size_units': u'3338:CYL', 'image_size_in_bytes': u'5120000', 'type': u'netboot', 'comments': None}, {'imagename': u'testimage2', 'imageosdistro': u'rhel6.5', 'md5sum': u'c73ce117eef8077c3420bfc8f473ac2f', 'disk_size_units': u'3338:CYL', 'image_size_in_bytes': u'5120000', 'type': u'netboot', 'comments': None}], image_records) # Clean up the images self.db_op.image_delete_record(imagename1) self.db_op.image_delete_record(imagename2) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_networkops.py0000664000175000017510000003342413442676317024024 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import shutil from zvmsdk.tests.unit import base from zvmsdk import dist from zvmsdk import networkops class SDKNetworkOpsTestCase(base.SDKTestCase): def setUp(self): self.networkops = networkops.get_networkops() @mock.patch('zvmsdk.smutclient.SMUTClient.create_nic') def test_create_nic(self, create_nic): self.networkops.create_nic("fakeid", '1000', 'Fake_nic_id', active=True) create_nic.assert_called_with("fakeid", vdev='1000', nic_id='Fake_nic_id', mac_addr=None, active=True) @mock.patch('zvmsdk.smutclient.SMUTClient.get_vswitch_list') def test_get_vswitch_list(self, get_vswitch_list): self.networkops.get_vswitch_list() get_vswitch_list.assert_called_with() @mock.patch('zvmsdk.smutclient.SMUTClient.couple_nic_to_vswitch') def test_couple_nic_to_vswitch(self, couple_nic_to_vswitch): self.networkops.couple_nic_to_vswitch("fake_userid", "nic_vdev", "fake_VS_name", True) couple_nic_to_vswitch.assert_called_with("fake_userid", "nic_vdev", "fake_VS_name", active=True) @mock.patch('zvmsdk.smutclient.SMUTClient.uncouple_nic_from_vswitch') def test_uncouple_nic_from_vswitch(self, uncouple_nic_from_vswitch): self.networkops.uncouple_nic_from_vswitch("fake_userid", "nic_vdev", True) uncouple_nic_from_vswitch.assert_called_with("fake_userid", "nic_vdev", active=True) @mock.patch('zvmsdk.smutclient.SMUTClient.add_vswitch') def test_add_vswitch(self, add_vswitch): self.networkops.add_vswitch("fakename", "fakerdev", controller='*', connection='CONNECT', network_type='ETHERNET', router="NONROUTER", vid='UNAWARE', port_type='ACCESS', gvrp='GVRP', queue_mem=8, native_vid=2, persist=False) add_vswitch.assert_called_with("fakename", rdev="fakerdev", controller='*', connection='CONNECT', network_type='ETHERNET', router="NONROUTER", vid='UNAWARE', port_type='ACCESS', gvrp='GVRP', queue_mem=8, native_vid=2, persist=False) @mock.patch('zvmsdk.smutclient.SMUTClient.grant_user_to_vswitch') def test_grant_user_to_vswitch(self, grant_user): self.networkops.grant_user_to_vswitch("vswitch_name", "userid") grant_user.assert_called_with("vswitch_name", "userid") @mock.patch('zvmsdk.smutclient.SMUTClient.revoke_user_from_vswitch') def test_revoke_user_from_vswitch(self, revoke_user): self.networkops.revoke_user_from_vswitch("vswitch_name", "userid") revoke_user.assert_called_with("vswitch_name", "userid") @mock.patch('zvmsdk.smutclient.SMUTClient.set_vswitch_port_vlan_id') def test_set_vswitch_port_vlan_id(self, set_vswitch): self.networkops.set_vswitch_port_vlan_id("vswitch_name", "userid", "vlan_id") set_vswitch.assert_called_with("vswitch_name", "userid", "vlan_id") @mock.patch('zvmsdk.smutclient.SMUTClient.set_vswitch') def test_set_vswitch(self, set_vswitch): self.networkops.set_vswitch("vswitch_name", grant_userid='fake_id') set_vswitch.assert_called_with("vswitch_name", grant_userid='fake_id') @mock.patch('zvmsdk.smutclient.SMUTClient.delete_vswitch') def test_delete_vswitch(self, delete_vswitch): self.networkops.delete_vswitch("vswitch_name", True) delete_vswitch.assert_called_with("vswitch_name", True) @mock.patch('zvmsdk.smutclient.SMUTClient.delete_nic') def test_delete_nic(self, delete_nic): self.networkops.delete_nic("userid", "vdev", True) delete_nic.assert_called_with("userid", "vdev", active=True) @mock.patch('zvmsdk.smutclient.SMUTClient.get_nic_info') def test_get_nic_info(self, get_nic_info): self.networkops.get_nic_info(userid='testid', vswitch='VSWITCH') get_nic_info.assert_called_with(userid='testid', nic_id=None, vswitch='VSWITCH') @mock.patch.object(shutil, 'rmtree') @mock.patch('zvmsdk.smutclient.SMUTClient.execute_cmd') @mock.patch('zvmsdk.smutclient.SMUTClient.update_guestdb_with_net_set') @mock.patch('zvmsdk.smutclient.SMUTClient.punch_file') @mock.patch('zvmsdk.networkops.NetworkOPS._generate_network_doscript') @mock.patch('zvmsdk.smutclient.SMUTClient.is_first_network_config') @mock.patch('zvmsdk.smutclient.SMUTClient.get_guest_temp_path') def test_network_configuration(self, temp_path, is_first, doscript, punch, update_guestdb, execute_cmd, rmtree): userid = 'fakeid' os_version = 'rhel7.2' network_info = [] network_file_path = '/tmp' active_cmds = 'execute command' network_doscript = 'file' temp_path.return_value = network_file_path is_first.return_value = True doscript.return_value = (network_doscript, active_cmds) rmtree.return_value = None self.networkops.network_configuration(userid, os_version, network_info, active=True) temp_path.assert_called_with(userid) is_first.assert_called_with(userid) doscript.assert_called_with(userid, os_version, network_info, network_file_path, True, active=True) punch.assert_called_with(userid, network_doscript, "X") update_guestdb.assert_called_with(userid) execute_cmd.assert_called_with(userid, active_cmds) @mock.patch('zvmsdk.dist.LinuxDistManager.get_linux_dist') @mock.patch.object(dist.rhel7, 'create_network_configuration_files') @mock.patch('zvmsdk.networkops.NetworkOPS._create_znetconfig') @mock.patch('zvmsdk.networkops.NetworkOPS._add_file') @mock.patch('zvmsdk.networkops.NetworkOPS._create_invokeScript') @mock.patch('zvmsdk.networkops.NetworkOPS._create_network_doscript') def test_generate_network_doscript_not_active(self, doscript, invokeScript, add_file, znetconfig, config, linux_dist): net_conf_files = [('target1', 'content1')] net_cmd_file = [('target2', 'content2')] net_conf_cmds = '' clean_cmd = '' net_enable = '' userid = 'fakeid' os_version = 'rhel7.2' network_info = [] first = False network_file_path = '/tmp' files_and_cmds = net_conf_files, net_conf_cmds, clean_cmd, net_enable files_map = [] files_map.append({'target_path': 'target1', 'source_file': "0000"}) files_map.append({'target_path': 'target2', 'source_file': "0001"}) linux_dist.return_value = dist.rhel7 config.return_value = files_and_cmds znetconfig.return_value = net_cmd_file add_file.return_value = None invokeScript.return_value = None doscript.return_value = 'result1' r1, r2 = self.networkops._generate_network_doscript(userid, os_version, network_info, network_file_path, first, active=False) linux_dist.assert_called_with(os_version) config.assert_called_with(network_file_path, network_info, first, active=False) invokeScript.assert_called_with(network_file_path, clean_cmd, files_map) doscript.assert_called_with(network_file_path) self.assertEqual(r1, 'result1') self.assertEqual(r2, '') @mock.patch('zvmsdk.dist.LinuxDistManager.get_linux_dist') @mock.patch.object(dist.rhel7, 'create_network_configuration_files') @mock.patch.object(dist.rhel7, 'create_active_net_interf_cmd') @mock.patch('zvmsdk.networkops.NetworkOPS._create_znetconfig') @mock.patch('zvmsdk.networkops.NetworkOPS._add_file') @mock.patch('zvmsdk.networkops.NetworkOPS._create_invokeScript') @mock.patch('zvmsdk.networkops.NetworkOPS._create_network_doscript') def test_generate_network_doscript_active(self, doscript, invokeScript, add_file, znetconfig, active_cmd, config, linux_dist): net_conf_files = [('target1', 'content1')] net_cmd_file = [('target2', 'content2')] active_net_cmd = 'create_active_net_interf_cmd' net_conf_cmds = '' clean_cmd = '' net_enable = '' userid = 'fakeid' os_version = 'rhel7.2' network_info = [] first = False network_file_path = '/tmp' files_and_cmds = net_conf_files, net_conf_cmds, clean_cmd, net_enable files_map = [] files_map.append({'target_path': 'target1', 'source_file': "0000"}) files_map.append({'target_path': 'target2', 'source_file': "0001"}) linux_dist.return_value = dist.rhel7 config.return_value = files_and_cmds active_cmd.return_value = active_net_cmd znetconfig.return_value = net_cmd_file add_file.return_value = None invokeScript.return_value = None doscript.return_value = 'result1' r1, r2 = self.networkops._generate_network_doscript(userid, os_version, network_info, network_file_path, first, active=True) linux_dist.assert_called_with(os_version) config.assert_called_with(network_file_path, network_info, first, active=True) invokeScript.assert_called_with(network_file_path, clean_cmd, files_map) doscript.assert_called_with(network_file_path) self.assertEqual(r1, 'result1') self.assertEqual(r2, active_net_cmd) @mock.patch('zvmsdk.smutclient.SMUTClient.query_vswitch') def test_vswitch_query(self, query_vswitch): self.networkops.vswitch_query("vswitch_name") query_vswitch.assert_called_with("vswitch_name") @mock.patch.object(shutil, 'rmtree') @mock.patch('zvmsdk.smutclient.SMUTClient.execute_cmd') @mock.patch('zvmsdk.smutclient.SMUTClient.punch_file') @mock.patch('zvmsdk.networkops.NetworkOPS._add_file') @mock.patch('zvmsdk.networkops.NetworkOPS._create_znetconfig') @mock.patch.object(dist.rhel7, 'get_network_configuration_files') @mock.patch.object(dist.rhel7, 'delete_vdev_info') @mock.patch.object(dist.rhel7, 'create_active_net_interf_cmd') @mock.patch('zvmsdk.dist.LinuxDistManager.get_linux_dist') @mock.patch('zvmsdk.smutclient.SMUTClient.get_guest_temp_path') def test_delete_network_configuration(self, temp_path, linux_dist, active_cmd, delete_vdev, get_netconf_files, znetconfig, add_file, punch, execute_cmd, rmtree): userid = 'fakeid' os_version = 'rhel7.2' vdev = '1000' net_cmd_file = [('target', 'content')] active_net_cmd = 'create_active_net_interf_cmd' delete_vdev_info = 'delete_vdev_info' get_network_configuration_files = 'network_conf_file' network_file_path = '/tmp' temp_path.return_value = network_file_path linux_dist.return_value = dist.rhel7 active_cmd.return_value = active_net_cmd delete_vdev.return_value = delete_vdev_info get_netconf_files.return_value = get_network_configuration_files znetconfig.return_value = net_cmd_file add_file.return_value = None rmtree.return_value = None self.networkops.delete_network_configuration(userid, os_version, vdev, active=True) temp_path.assert_called_with(userid) linux_dist.assert_called_with(os_version) get_netconf_files.assert_called_with(vdev) delete_vdev.assert_called_with(vdev) punch.assert_called_with(userid, '/tmp/DEL1000.sh', "X") execute_cmd.assert_called_with(userid, active_net_cmd) @mock.patch('zvmsdk.smutclient.SMUTClient.dedicate_OSA') def test_dedicate_OSA(self, dedicate_OSA): self.networkops.dedicate_OSA("fakeid", 'F000', vdev='1000', active=True) dedicate_OSA.assert_called_with("fakeid", 'F000', vdev='1000', active=True) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkclientcases/0000775000175000017510000000000013442723341023157 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkclientcases/__init__.py0000664000175000017510000000000013371225174025260 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmsdk/tests/unit/sdkclientcases/test_restclient.py0000664000175000017510000010526313442676324026763 0ustar ruirui00000000000000# 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. import json import mock import requests import unittest from zvmconnector import restclient from zvmsdk import config CONF = config.CONF class FakeResp(object): def __init__(self): self.content = '{"output": "{}"}' class RESTClientTestCase(unittest.TestCase): """Testcases for RESTClient.""" def setUp(self): self.client = restclient.RESTClient(ssl_enabled=False) self.fake_userid = 'userid01' self.base_url = 'http://127.0.0.1:8888' self.headers = {'Content-Type': 'application/json'} self.headers.update(self.headers or {}) self.response = FakeResp() self.client_ssl = restclient.RESTClient(ssl_enabled=True) self.base_url_ssl = 'https://127.0.0.1:8888' def test_init_ComputeAPI(self): self.assertTrue(isinstance(self.client, restclient.RESTClient)) def _tmp_token(self): token = '1234567890' return token @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_create(self, get_token, request): # method = 'POST' # url = '/guests' disks = [{'size': '3g', 'is_boot_disk': True}] # body = {'guest': {'userid': self.fake_userid, # 'vcpus': 1, # 'memory': 1024, # 'disk_list': disks, # 'user_profile': 'profile' # }} # body = json.dumps(body) # header = self.headers # full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_create", self.fake_userid, 1, 1024, disk_list=disks, user_profile='profile') # request.assert_called_with(method, full_uri, # data=body, headers=header, # verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_list(self, get_token, request): method = 'GET' url = '/guests' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_list") request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_inspect_stats(self, get_token, request): method = 'GET' url = '/guests/stats?userid=%s' % self.fake_userid body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_inspect_stats", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) fake_userid_string = 'userid1, userid2, userid3,userid4' url = '/guests/stats?userid=%s' % fake_userid_string full_uri = self.base_url + url self.client.call("guest_inspect_stats", fake_userid_string) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_inspect_vnics(self, get_token, request): method = 'GET' url = '/guests/interfacestats?userid=%s' % self.fake_userid body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_inspect_vnics", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) fake_userid_string = 'userid1, userid2, userid3,userid4' url = '/guests/interfacestats?userid=%s' % fake_userid_string full_uri = self.base_url + url self.client.call("guest_inspect_vnics", fake_userid_string) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guests_get_nic_info(self, get_token, request): method = 'GET' url = '/guests/nics?userid=%s&nic_id=%s&vswitch=%s' % ( self.fake_userid, '1000', 'xcatvsw1') body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guests_get_nic_info", userid=self.fake_userid, nic_id='1000', vswitch='xcatvsw1') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_delete(self, get_token, request): method = 'DELETE' url = '/guests/%s' % self.fake_userid body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_delete", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_get_definition_info(self, get_token, request): method = 'GET' url = '/guests/%s' % self.fake_userid body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_get_definition_info", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_start(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'start'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_start", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_stop(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'stop'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_stop", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_softstop(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'softstop'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_softstop", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_softstop_parameter_set_zero(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'softstop', 'timeout': 0, 'poll_interval': 0} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_softstop", self.fake_userid, timeout=0, poll_interval=0) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_pause(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'pause'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_pause", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_unpause(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'unpause'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_unpause", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_reboot(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'reboot'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_reboot", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_reset(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'reset'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_reset", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_get_console_output(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'get_console_output'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_get_console_output", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_capture(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'capture', 'image': 'image_captured'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_capture", self.fake_userid, 'image_captured') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_deploy(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'capture', 'image': 'image_captured'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_capture", self.fake_userid, 'image_captured') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_get_info(self, get_token, request): method = 'GET' url = '/guests/%s/info' % self.fake_userid body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_get_info", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_get_info_ssl(self, get_token, request): method = 'GET' url = '/guests/%s/info' % self.fake_userid body = None header = self.headers full_uri = self.base_url_ssl + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client_ssl.call("guest_get_info", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_create_nic(self, get_token, request): # method = 'POST' # url = '/guests/%s/nic' % self.fake_userid body = {'nic': {'vdev': '123', 'nic_id': '1234', 'mac_addr': 'xx:xx:xx:xx:xx:xx', 'active': False}} body = json.dumps(body) # header = self.headers # full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_create_nic", self.fake_userid, vdev='123', nic_id='1234', mac_addr='xx:xx:xx:xx:xx:xx', active=False) request.assert_called_once() # request.assert_called_with(method, full_uri, # data=body, headers=header, # verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_delete_nic(self, get_token, request): method = 'DELETE' url = '/guests/%s/nic/%s' % (self.fake_userid, '123') body = {'active': False} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_delete_nic", self.fake_userid, '123', active=False) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_nic_couple_to_vswitch(self, get_token, request): method = 'PUT' url = '/guests/%s/nic/%s' % (self.fake_userid, '123') body = {'info': {'couple': True, 'vswitch': 'vswitch1', 'active': False}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_nic_couple_to_vswitch", self.fake_userid, '123', 'vswitch1', active=False) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_nic_uncouple_from_vswitch(self, get_token, request): method = 'PUT' url = '/guests/%s/nic/%s' % (self.fake_userid, '123') body = {'info': {'couple': False, 'active': False}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_nic_uncouple_from_vswitch", self.fake_userid, '123', active=False) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_create_network_interface(self, get_token, request): method = 'POST' networks = [{'ip_addr': '12.12.12.12'}] url = '/guests/%s/interface' % self.fake_userid body = {'interface': {'os_version': 'rhel7.2', 'guest_networks': networks, 'active': False}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_create_network_interface", self.fake_userid, 'rhel7.2', networks, active=False) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_delete_network_interface(self, get_token, request): method = 'DELETE' url = '/guests/%s/interface' % self.fake_userid body = {'interface': {'os_version': 'rhel7.2', 'vdev': '123', 'active': False}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_delete_network_interface", self.fake_userid, 'rhel7.2', '123', active=False) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_get_power_state(self, get_token, request): method = 'GET' url = '/guests/%s/power_state' % self.fake_userid body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_get_power_state", self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_create_disks(self, get_token, request): method = 'POST' disks = [{'size': '3g'}] url = '/guests/%s/disks' % self.fake_userid body = {'disk_info': {'disk_list': disks}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_create_disks", self.fake_userid, disks) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_delete_disks(self, get_token, request): method = 'DELETE' vdevs = ['0101', '0102'] url = '/guests/%s/disks' % self.fake_userid body = {'vdev_info': {'vdev_list': vdevs}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_delete_disks", self.fake_userid, vdevs) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_config_minidisks(self, get_token, request): method = 'PUT' disks = [{'vdev': '0101', 'mntdir': '/mnt/0101'}] url = '/guests/%s/disks' % self.fake_userid body = {'disk_info': {'disk_list': disks}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_config_minidisks", self.fake_userid, disks) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_host_get_info(self, get_token, request): method = 'GET' url = '/host' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("host_get_info") request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_host_diskpool_get_info(self, get_token, request): # wait host_diskpool_get_info bug fixed pass @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_image_import(self, get_token, request): method = 'POST' image_uri = 'file:///tmp/100.img' image_meta = {'os_version': 'rhel7.2', 'md5sum': 'dummy'} url = '/images' body = {'image': {'image_name': '100.img', 'url': image_uri, 'image_meta': image_meta}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("image_import", '100.img', image_uri, image_meta) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_image_delete(self, get_token, request): method = 'DELETE' url = '/images/%s' % '100.img' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("image_delete", '100.img') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_image_export(self, get_token, request): method = 'PUT' destination = 'file:///tmp/export.img' url = '/images/%s' % '100.img' body = {'location': {'dest_url': destination}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("image_export", '100.img', destination) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_image_get_root_disk_size(self, get_token, request): method = 'GET' url = '/images/%s/root_disk_size' % '100.img' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("image_get_root_disk_size", '100.img') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_token_create(self, get_token, request): method = 'POST' url = '/token' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("token_create") request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_vswitch_get_list(self, get_token, request): method = 'GET' url = '/vswitches' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("vswitch_get_list") request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_vswitch_create(self, get_token, request): method = 'POST' url = '/vswitches' body = {'vswitch': {'name': 'dummy'}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("vswitch_create", 'dummy') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_vswitch_delete(self, get_token, request): method = 'DELETE' url = '/vswitches/%s' % 'dummy' body = None header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("vswitch_delete", 'dummy') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_vswitch_grant_user(self, get_token, request): method = 'PUT' url = '/vswitches/%s' % 'dummy' body = {'vswitch': {'grant_userid': self.fake_userid}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("vswitch_grant_user", 'dummy', self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_vswitch_revoke_user(self, get_token, request): method = 'PUT' url = '/vswitches/%s' % 'dummy' body = {'vswitch': {'revoke_userid': self.fake_userid}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("vswitch_revoke_user", 'dummy', self.fake_userid) request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_vswitch_set_vlan_id_for_user(self, get_token, request): method = 'PUT' url = '/vswitches/%s' % 'dummy' body = {'vswitch': {'user_vlan_id': {'userid': self.fake_userid, 'vlanid': 'vlan_id'}}} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("vswitch_set_vlan_id_for_user", 'dummy', self.fake_userid, 'vlan_id') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_resize_mem(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'resize_mem', 'size': '4g'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_resize_mem", self.fake_userid, '4g') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) @mock.patch.object(requests, 'request') @mock.patch('zvmconnector.restclient.RESTClient._get_token') def test_guest_live_resize_mem(self, get_token, request): method = 'POST' url = '/guests/%s/action' % self.fake_userid body = {'action': 'live_resize_mem', 'size': '4g'} body = json.dumps(body) header = self.headers full_uri = self.base_url + url request.return_value = self.response get_token.return_value = self._tmp_token() self.client.call("guest_live_resize_mem", self.fake_userid, '4g') request.assert_called_with(method, full_uri, data=body, headers=header, verify=False) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_volumeop.py0000664000175000017510000006143313442676317023460 0ustar ruirui00000000000000# 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. import mock import shutil from zvmsdk import database from zvmsdk import dist from zvmsdk import exception from zvmsdk import volumeop from zvmsdk.tests.unit import base class TestVolumeConfiguratorAPI(base.SDKTestCase): @classmethod def setUpClass(cls): super(TestVolumeConfiguratorAPI, cls).setUpClass() cls.configurator = volumeop.VolumeConfiguratorAPI() @mock.patch("zvmsdk.dist.LinuxDistManager.get_linux_dist") @mock.patch("zvmsdk.smutclient.SMUTClient.execute_cmd") @mock.patch.object(dist.rhel7, "create_active_net_interf_cmd") @mock.patch("zvmsdk.volumeop.VolumeConfiguratorAPI." "configure_volume_attach") @mock.patch("zvmsdk.vmops.VMOps.is_reachable") def test_config_attach_reachable(self, is_reachable, config_attach, restart_zvmguestconfigure, execute_cmd, get_dist): fcp = 'bfc3' assigner_id = 'userid1' target_wwpn = '1111' target_lun = '2222' multipath = True os_version = 'rhel7' mount_point = '/dev/sdz' get_dist.return_value = dist.rhel7 config_attach.return_value = None is_reachable.return_value = True active_cmds = 'systemctl start zvmguestconfigure.service' restart_zvmguestconfigure.return_value = active_cmds self.configurator.config_attach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) get_dist.assert_called_once_with(os_version) restart_zvmguestconfigure.assert_called_once_with() execute_cmd.assert_called_once_with(assigner_id, active_cmds) @mock.patch("zvmsdk.dist.LinuxDistManager.get_linux_dist") @mock.patch("zvmsdk.volumeop.VolumeConfiguratorAPI." "configure_volume_attach") @mock.patch("zvmsdk.vmops.VMOps.is_reachable") def test_config_attach_not_reachable(self, is_reachable, config_attach, get_dist): fcp = 'bfc3' assigner_id = 'userid1' target_wwpn = '1111' target_lun = '2222' multipath = True os_version = 'rhel7' mount_point = '/dev/sdz' is_reachable.return_value = False get_dist.return_value = dist.rhel7 config_attach.return_value = None self.configurator.config_attach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point) get_dist.assert_called_once_with(os_version) def test_config_force_attach(self): pass def test_config_force_detach(self): pass @mock.patch("zvmsdk.smutclient.SMUTClient.punch_file") @mock.patch.object(shutil, "rmtree") @mock.patch("zvmsdk.volumeop.VolumeConfiguratorAPI._create_file") @mock.patch("zvmsdk.dist.LinuxDistManager.get_linux_dist") @mock.patch("zvmsdk.dist.LinuxDist.get_volume_attach_configuration_cmds") def test_config_attach_active(self, get_attach_cmds, get_dist, create_file, rmtree, punch_file): fcp = 'bfc3' assigner_id = 'userid1' target_wwpn = '1111' target_lun = '2222' multipath = True os_version = 'rhel7' mount_point = '/dev/sdz' config_file = '/tm/userid1xxx/attach_volume.sh' config_file_path = '/tm/userid1xxx/' linuxdist = dist.rhel7 get_dist.return_value = linuxdist create_file.return_value = (config_file, config_file_path) rmtree.return_value = None self.configurator.configure_volume_attach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point, linuxdist) get_attach_cmds.assert_called_once_with(fcp, target_wwpn, target_lun, multipath, mount_point) punch_file.assert_called_once_with(assigner_id, config_file, 'X') @mock.patch("zvmsdk.smutclient.SMUTClient.punch_file") @mock.patch.object(shutil, "rmtree") @mock.patch("zvmsdk.volumeop.VolumeConfiguratorAPI._create_file") @mock.patch("zvmsdk.dist.LinuxDistManager.get_linux_dist") @mock.patch("zvmsdk.dist.LinuxDist.get_volume_detach_configuration_cmds") def test_config_detach_active(self, get_detach_cmds, get_dist, create_file, rmtree, punch_file): fcp = 'bfc3' assigner_id = 'userid1' target_wwpn = '1111' target_lun = '2222' multipath = True os_version = 'rhel7' mount_point = '/dev/sdz' config_file = '/tm/userid1xxx/attach_volume.sh' config_file_path = '/tm/userid1xxx/' linuxdist = dist.rhel7 get_dist.return_value = linuxdist create_file.return_value = (config_file, config_file_path) rmtree.return_value = None self.configurator.configure_volume_detach(fcp, assigner_id, target_wwpn, target_lun, multipath, os_version, mount_point, linuxdist) get_detach_cmds.assert_called_once_with(fcp, target_wwpn, target_lun, multipath, mount_point) punch_file.assert_called_once_with(assigner_id, config_file, 'X') class TestFCP(base.SDKTestCase): def test_parse_normal(self): info = ['opnstk1: FCP device number: B83D', 'opnstk1: Status: Free', 'opnstk1: NPIV world wide port number: NONE', 'opnstk1: Channel path ID: 59', 'opnstk1: Physical world wide port number: 20076D8500005181'] fcp = volumeop.FCP(info) self.assertEqual('B83D', fcp._dev_no.upper()) self.assertIsNone(fcp._npiv_port) self.assertEqual('59', fcp._chpid.upper()) self.assertEqual('20076D8500005181', fcp._physical_port.upper()) def test_parse_npiv(self): info = ['opnstk1: FCP device number: B83D', 'opnstk1: Status: Free', 'opnstk1: NPIV world wide port number: 20076D8500005182', 'opnstk1: Channel path ID: 59', 'opnstk1: Physical world wide port number: 20076D8500005181'] fcp = volumeop.FCP(info) self.assertEqual('B83D', fcp._dev_no.upper()) self.assertEqual('20076D8500005182', fcp._npiv_port.upper()) self.assertEqual('59', fcp._chpid.upper()) self.assertEqual('20076D8500005181', fcp._physical_port.upper()) class TestFCPManager(base.SDKTestCase): @classmethod def setUpClass(cls): super(TestFCPManager, cls).setUpClass() cls.fcpops = volumeop.FCPManager() cls.db_op = database.FCPDbOperator() def test_expand_fcp_list_normal(self): fcp_list = "1f10;1f11;1f12;1f13;1f14" expected = set(['1f10', '1f11', '1f12', '1f13', '1f14']) fcp_info = self.fcpops._expand_fcp_list(fcp_list) self.assertEqual(expected, fcp_info) def test_expand_fcp_list_with_dash(self): fcp_list = "1f10-1f14" expected = set(['1f10', '1f11', '1f12', '1f13', '1f14']) fcp_info = self.fcpops._expand_fcp_list(fcp_list) self.assertEqual(expected, fcp_info) def test_expand_fcp_list_with_normal_plus_dash(self): fcp_list = "1f10;1f11-1f13;1f17" expected = set(['1f10', '1f11', '1f12', '1f13', '1f17']) fcp_info = self.fcpops._expand_fcp_list(fcp_list) self.assertEqual(expected, fcp_info) def test_expand_fcp_list_with_normal_plus_2dash(self): fcp_list = "1f10;1f11-1f13;1f17-1f1a;1f02" expected = set(['1f10', '1f11', '1f12', '1f13', '1f17', '1f18', '1f19', '1f1a', '1f02']) fcp_info = self.fcpops._expand_fcp_list(fcp_list) self.assertEqual(expected, fcp_info) @mock.patch("zvmsdk.volumeop.FCPManager._get_all_fcp_info") def test_init_fcp_pool(self, mock_get): fcp_list = ['opnstk1: FCP device number: B83D', 'opnstk1: Status: Free', 'opnstk1: NPIV world wide port number: 20076D8500005182', 'opnstk1: Channel path ID: 59', 'opnstk1: Physical world wide port number: 20076D8500005181', 'opnstk1: FCP device number: B83E', 'opnstk1: Status: Active', 'opnstk1: NPIV world wide port number: 20076D8500005183', 'opnstk1: Channel path ID: 50', 'opnstk1: Physical world wide port number: 20076D8500005185'] mock_get.return_value = fcp_list fake_userid = 'fakeuser' self.fcpops._init_fcp_pool('b83d-b83f', fake_userid) self.assertEqual(2, len(self.fcpops._fcp_pool)) self.assertTrue('b83d' in self.fcpops._fcp_pool) self.assertTrue('b83e' in self.fcpops._fcp_pool) # note b83f is not in fcp_list self.assertFalse('b83f' in self.fcpops._fcp_pool) npiv = self.fcpops._fcp_pool['b83d']._npiv_port.upper() physical = self.fcpops._fcp_pool['b83d']._physical_port.upper() self.assertEqual('20076D8500005182', npiv) self.assertEqual('20076D8500005181', physical) self.assertEqual('59', self.fcpops._fcp_pool['b83d']._chpid.upper()) npiv = self.fcpops._fcp_pool['b83e']._npiv_port.upper() physical = self.fcpops._fcp_pool['b83e']._physical_port.upper() self.assertEqual('20076D8500005183', npiv) self.assertEqual('20076D8500005185', physical) self.assertEqual('50', self.fcpops._fcp_pool['b83e']._chpid.upper()) @mock.patch("zvmsdk.volumeop.FCPManager._get_all_fcp_info") @mock.patch("zvmsdk.volumeop.FCPManager._report_orphan_fcp") @mock.patch("zvmsdk.volumeop.FCPManager._add_fcp") def test_sync_db_fcp_list(self, mock_add, mock_report, mock_get): fcp_list = ['opnstk1: FCP device number: B83D', 'opnstk1: Status: Free', 'opnstk1: NPIV world wide port number: 20076D8500005182', 'opnstk1: Channel path ID: 59', 'opnstk1: Physical world wide port number: 20076D8500005181', 'opnstk1: FCP device number: B83E', 'opnstk1: Status: Active', 'opnstk1: NPIV world wide port number: 20076D8500005183', 'opnstk1: Channel path ID: 50', 'opnstk1: Physical world wide port number: 20076D8500005185', 'opnstk1: FCP device number: B83F', 'opnstk1: Status: Active', 'opnstk1: NPIV world wide port number: 20076D8500005187', 'opnstk1: Channel path ID: 50', 'opnstk1: Physical world wide port number: 20076D8500005188'] mock_get.return_value = fcp_list fake_userid = 'fakeuser' self.fcpops._init_fcp_pool('b83d-b83f', fake_userid) self.db_op.new('b83c') self.db_op.new('b83d') self.db_op.new('b83e') try: self.fcpops._sync_db_fcp_list() mock_add.assert_called_once_with('b83f') mock_report.assert_called_once_with('b83c') finally: self.db_op.delete('b83d') self.db_op.delete('b83e') self.db_op.delete('b83f') self.db_op.delete('b83c') def test_find_and_reserve_fcp_new(self): # create 2 FCP self.db_op.new('b83c') self.db_op.new('b83d') # find FCP for user and FCP not exist, should allocate them try: fcp1 = self.fcpops.find_and_reserve_fcp('dummy1') fcp2 = self.fcpops.find_and_reserve_fcp('dummy2') self.assertEqual('b83c', fcp1) self.assertEqual('b83d', fcp2) fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'', 0, 1, u'')] self.assertEqual(expected, fcp_list) fcp_list = self.db_op.get_from_fcp('b83d') expected = [(u'b83d', u'', 0, 1, u'')] self.assertEqual(expected, fcp_list) finally: self.db_op.delete('b83c') self.db_op.delete('b83d') def test_find_and_reserve_fcp_old(self): self.db_op = database.FCPDbOperator() # create 2 FCP self.db_op.new('b83c') self.db_op.new('b83d') # find FCP for user and FCP not exist, should allocate them try: fcp1 = self.fcpops.find_and_reserve_fcp('user1') self.assertEqual('b83c', fcp1) self.fcpops.increase_fcp_usage('b83c', 'user1') fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'user1', 1, 1, u'')] self.assertEqual(expected, fcp_list) # After usage, we need find b83d now fcp2 = self.fcpops.find_and_reserve_fcp('user2') self.assertEqual('b83d', fcp2) fcp_list = self.db_op.get_from_fcp('b83d') expected = [(u'b83d', u'', 0, 1, u'')] self.assertEqual(expected, fcp_list) self.fcpops.increase_fcp_usage('b83c', 'user1') fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'user1', 2, 1, u'')] self.assertEqual(expected, fcp_list) self.fcpops.decrease_fcp_usage('b83c', 'user1') fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'user1', 1, 1, u'')] self.assertEqual(expected, fcp_list) self.fcpops.decrease_fcp_usage('b83c') fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'user1', 0, 1, u'')] self.assertEqual(expected, fcp_list) # unreserve makes this fcp free self.fcpops.unreserve_fcp('b83c') fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'user1', 0, 0, u'')] self.assertEqual(expected, fcp_list) fcp3 = self.fcpops.find_and_reserve_fcp('user3') self.assertEqual('b83c', fcp3) fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'user1', 0, 1, u'')] self.assertEqual(expected, fcp_list) finally: self.db_op.delete('b83c') self.db_op.delete('b83d') def test_find_and_reserve_fcp_exception(self): # no FCP at all # find FCP for user and FCP not exist, should alloca fcp = self.fcpops.find_and_reserve_fcp('user1') self.assertIsNone(fcp) class TestFCPVolumeManager(base.SDKTestCase): @classmethod def setUpClass(cls): super(TestFCPVolumeManager, cls).setUpClass() cls.volumeops = volumeop.FCPVolumeManager() cls.db_op = database.FCPDbOperator() # tearDownClass deleted to work around bug of 'no such table:fcp' @mock.patch("zvmsdk.smutclient.SMUTClient.get_host_info") @mock.patch("zvmsdk.volumeop.FCPManager._get_all_fcp_info") def test_get_volume_connector(self, get_fcp_info, get_host_info): fcp_info = ['opnstk1: FCP device number: B83C', 'opnstk1: Status: Free', 'opnstk1: NPIV world wide port number: 2007123400001234', 'opnstk1: Channel path ID: 59', 'opnstk1: Physical world wide port number: 20076D8500005181'] get_fcp_info.return_value = fcp_info get_host_info.return_value = {'zvm_host': 'fakehost'} base.set_conf('volume', 'fcp_list', 'b83c') # assign FCP self.db_op.new('b83c') try: connections = self.volumeops.get_volume_connector('fakeuser') expected = {'zvm_fcp': ['b83c'], 'wwpns': ['2007123400001234'], 'host': 'fakehost'} self.assertEqual(expected, connections) fcp_list = self.db_op.get_from_fcp('b83c') expected = [(u'b83c', u'', 0, 0, u'')] self.assertEqual(expected, fcp_list) finally: self.db_op.delete('b83c') @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._add_disk") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._dedicate_fcp") def test_attach(self, mock_dedicate, mock_add_disk, mock_check): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'mount_point': '/dev/sdz', 'assigner_id': 'user1'} mock_check.return_value = True self.db_op = database.FCPDbOperator() self.db_op.new('b83c') try: self.volumeops.attach(connection_info) mock_dedicate.assert_called_once_with('b83c', 'USER1') mock_add_disk.assert_called_once_with('b83c', 'USER1', '1111', '2222', False, 'rhel7', '/dev/sdz') finally: self.db_op.delete('b83c') @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._add_disk") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._dedicate_fcp") def test_attach_no_dedicate(self, mock_dedicate, mock_add_disk, mock_check): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'mount_point': '/dev/sdz', 'assigner_id': 'user1'} mock_check.return_value = True self.db_op = database.FCPDbOperator() self.db_op.new('b83c') self.db_op.assign('b83c', 'USER1') self.db_op.increase_usage('b83c') try: self.volumeops.attach(connection_info) self.assertFalse(mock_dedicate.called) mock_add_disk.assert_called_once_with('b83c', 'USER1', '1111', '2222', False, 'rhel7', '/dev/sdz') finally: self.db_op.delete('b83c') @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._undedicate_fcp") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._dedicate_fcp") @mock.patch("zvmsdk.volumeop.FCPManager.decrease_fcp_usage") @mock.patch("zvmsdk.volumeop.FCPManager.increase_fcp_usage") def test_attach_rollback(self, mock_increase, mock_decrease, mock_dedicate, mock_undedicate, mock_check): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'mount_point': '/dev/sdz', 'assigner_id': 'user1'} mock_check.return_value = True mock_increase.return_value = True results = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 1, 'logEntries': [], 'rc': 0, 'response': ['fake response']} mock_dedicate.side_effect = exception.SDKSMUTRequestFailed( results, 'fake error') # return value of decreate must bigger than 1 mock_decrease.return_value = 0 self.assertRaises(exception.SDKBaseException, self.volumeops.attach, connection_info) mock_undedicate.assert_called_once_with('b83c', 'USER1') @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._add_disk") @mock.patch("zvmsdk.volumeop.FCPManager.increase_fcp_usage") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._remove_disk") @mock.patch("zvmsdk.volumeop.FCPManager.decrease_fcp_usage") def test_detach_rollback(self, mock_decrease, mock_remove_disk, mock_increase, mock_add_disk, mock_check): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'mount_point': '/dev/sdz', 'assigner_id': 'user1'} # this return does not matter mock_check.return_value = True mock_decrease.return_value = 0 results = {'rs': 0, 'errno': 0, 'strError': '', 'overallRC': 1, 'logEntries': [], 'rc': 0, 'response': ['fake response']} mock_remove_disk.side_effect = exception.SDKSMUTRequestFailed( results, 'fake error') self.assertRaises(exception.SDKBaseException, self.volumeops.detach, connection_info) mock_increase.assert_called_once_with('b83c', 'USER1') mock_add_disk.assert_called_once_with('b83c', 'USER1', '1111', '2222', False, 'rhel7', '/dev/sdz') @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._remove_disk") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._undedicate_fcp") def test_detach(self, mock_undedicate, mock_remove_disk, mock_check): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'mount_point': '/dev/sdz', 'assigner_id': 'user1'} mock_check.return_value = True self.db_op = database.FCPDbOperator() self.db_op.new('b83c') self.db_op.assign('b83c', 'user1') try: self.volumeops.detach(connection_info) mock_remove_disk.assert_called_once_with('b83c', 'USER1', '1111', '2222', False, 'rhel7', '/dev/sdz') mock_undedicate.assert_called_once_with('b83c', 'USER1') finally: self.db_op.delete('b83c') @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._remove_disk") @mock.patch("zvmsdk.volumeop.FCPVolumeManager._undedicate_fcp") def test_detach_no_undedicate(self, mock_undedicate, mock_remove_disk, mock_check): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'mount_point': '/dev/sdz', 'assigner_id': 'user1'} mock_check.return_value = True self.db_op = database.FCPDbOperator() self.db_op.new('b83c') self.db_op.assign('b83c', 'user1') self.db_op.increase_usage('b83c') try: self.volumeops.detach(connection_info) self.assertFalse(mock_undedicate.called) mock_remove_disk.assert_called_once_with('b83c', 'USER1', '1111', '2222', False, 'rhel7', '/dev/sdz') finally: self.db_op.delete('b83c') zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_dist.py0000664000175000017510000005350713401106372022540 0ustar ruirui00000000000000# 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. import mock from zvmsdk import dist from zvmsdk.tests.unit import base class RHEL6TestCase(base.SDKTestCase): """Testcases for different distros.""" @classmethod def setUpClass(cls): super(RHEL6TestCase, cls).setUpClass() cls.os_version = 'redhat6.7' def setUp(self): super(RHEL6TestCase, self).setUp() self.dist_manager = dist.LinuxDistManager() self.linux_dist = self.dist_manager.get_linux_dist(self.os_version)() def test_create_network_configuration_files(self): pass @mock.patch('zvmsdk.dist.LinuxDist._reload_rules_config_file') @mock.patch('zvmsdk.dist.LinuxDist._add_udev_rules') @mock.patch('zvmsdk.dist.LinuxDist._get_wwid') @mock.patch.object(dist.rhel, '_get_source_device_path') def test_create_mount_point_multipath(self, get_source_path, get_wwid, add_rules, reload_rules): fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' mount_point = '/dev/sde' multipath = True get_source_path.return_value = 'fake_ret' get_wwid.return_value = 'fake_ret' add_rules.return_value = 'fake_ret' reload_rules.return_value = 'fake_ret' self.linux_dist.create_mount_point(fcp, wwpn, lun, mount_point, multipath) get_source_path.assert_called_once_with(fcp, wwpn, lun) get_wwid.assert_called_once_with() add_rules.assert_called_once_with(mount_point, wwpn, lun, multipath) reload_rules.assert_called_once_with(multipath) def test_get_source_device_path(self): fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' device = '0.0.%s' % fcp data = {'device': device, 'wwpn': wwpn, 'lun': lun} expect = ('SourceDevice="/dev/disk/by-path/ccw-%(device)s-' 'zfcp-%(wwpn)s:%(lun)s"\n' % data) ret = self.linux_dist._get_source_device_path(fcp, wwpn, lun) self.assertEqual(ret, expect) def test_get_wwid(self): var_wwid_line = ('wwid_line=`/sbin/udevadm info --query=all ' '--name=$SourceDevice | egrep -a -i \"ID_SERIAL=\"`') var_wwid = 'WWID=${wwid_line##*=}\n' wwid = '\n'.join((var_wwid_line, var_wwid)) ret = self.linux_dist._get_wwid() self.assertEqual(ret, wwid) def test_add_udev_rules_multipath(self): wwpn = '0x5005076812341234' lun = '0x0026000000000000' multipath = True mount_point = '/dev/sde' var_target_file = 'TargetFile="sde"\n' var_wwpn = 'WWPN="%s"\n' % wwpn var_lun = 'LUN="%s"\n' % lun config_file_lib = '/lib/udev/rules.d/56-zfcp.rules' find_config = 'ConfigLib="%s"\n' % config_file_lib find_config += 'if [ -e "$ConfigLib" ]\n' find_config += 'then\n' find_config += ' ConfigFile="/lib/udev/rules.d/56-zfcp.rules"\n' find_config += 'else\n' find_config += ' ConfigFile="/etc/udev/rules.d/56-zfcp.rules"\n' find_config += 'fi\n' var_config_file = find_config link_item = ('KERNEL==\\"dm-*\\", ' 'ENV{DM_UUID}==\\"mpath-$WWID\\", ' 'SYMLINK+=\\"$TargetFile\\"') var_link_item = 'LinkItem="%s"\n' % link_item add_rules_cmd = ''.join((var_target_file, var_wwpn, var_lun, var_config_file, var_link_item, 'echo -e $LinkItem >> $ConfigFile\n')) ret = self.linux_dist._add_udev_rules(mount_point, wwpn, lun, multipath) self.assertEqual(ret, add_rules_cmd) def test_reload_udev_rules_multipath(self): multipath = True reload_cmd = 'udevadm control --reload' create_symlink_cmd = 'udevadm trigger --sysname-match=dm-*' expect = '\n'.join((reload_cmd, create_symlink_cmd)) ret = self.linux_dist._reload_rules_config_file(multipath) self.assertEqual(ret, expect) @mock.patch('zvmsdk.dist.LinuxDist.create_mount_point') @mock.patch('zvmsdk.dist.rhel._set_zfcp_multipath') @mock.patch('zvmsdk.dist.rhel6._set_zfcp_config_files') @mock.patch('zvmsdk.dist.rhel._online_fcp_device') @mock.patch('zvmsdk.dist.LinuxDist.modprobe_zfcp_module') def test_get_volume_attach_configuration_cmds(self, check_module, online_device, zfcp_config, zfcp_multipath, create_mount_point): fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' multipath = True mount_point = '/dev/sdz' check_module.return_value = 'fake_ret' online_device.return_value = 'fake_ret' zfcp_config.return_value = 'fake_ret' zfcp_multipath.return_value = 'fake_ret' create_mount_point.return_value = 'fake_ret' self.linux_dist.get_volume_attach_configuration_cmds(fcp, wwpn, lun, multipath, mount_point) check_module.assert_called_once_with() online_device.assert_called_once_with(fcp) zfcp_config.assert_called_once_with(fcp, wwpn, lun) zfcp_multipath.assert_called_once_with() create_mount_point.assert_called_once_with(fcp, wwpn, lun, mount_point, multipath) @mock.patch('zvmsdk.dist.LinuxDist.remove_mount_point') @mock.patch('zvmsdk.dist.rhel6._restart_multipath') @mock.patch('zvmsdk.dist.rhel._offline_fcp_device') def test_get_volume_detach_configuration_cmds(self, offline_device, restart_multipath, remove_mount_point): """ RHEL6 """ fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' multipath = True mount_point = '/dev/sdz' offline_device.return_value = 'fake_ret' restart_multipath.return_value = 'fake_ret' remove_mount_point.return_value = 'fake_ret' self.linux_dist.get_volume_detach_configuration_cmds(fcp, wwpn, lun, multipath, mount_point) offline_device.assert_called_once_with(fcp, wwpn, lun, multipath) restart_multipath.assert_called_once_with() remove_mount_point.assert_called_once_with(mount_point, wwpn, lun, multipath) def test_online_fcp_device(self): fcp = '1fc5' cmd_cio_ignore = '/sbin/cio_ignore -r %s > /dev/null\n' % fcp cmd_online_dev = '/sbin/chccwdev -e %s > /dev/null\n' % fcp ret = self.linux_dist._online_fcp_device(fcp) expect = cmd_cio_ignore + cmd_online_dev self.assertEqual(ret, expect) def test_offline_fcp_device(self): fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' multipath = True device = '0.0.%s' % fcp cmd_offline_dev = 'chccwdev -d %s' % fcp data = {'wwpn': wwpn, 'lun': lun, 'device': device, 'zfcpConf': '/etc/zfcp.conf'} cmd_delete_records = ('sed -i -e ' '\"/%(device)s %(wwpn)s %(lun)s/d\" ' '%(zfcpConf)s\n' % data) ret = self.linux_dist._offline_fcp_device(fcp, wwpn, lun, multipath) expect = '\n'.join((cmd_offline_dev, cmd_delete_records)) self.assertEqual(ret, expect) def test_set_zfcp_config_files(self): """ RHEL6 """ fcp = '1fc5' target_wwpn = '0x5005076812341234' target_lun = '0x0026000000000000' device = '0.0.%s' % fcp # add to port(WWPN) set_zfcp_conf = 'echo "%(device)s %(wwpn)s %(lun)s" >> /etc/zfcp.conf'\ % {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} trigger_uevent = 'echo "add" >> /sys/bus/ccw/devices/%s/uevent\n'\ % device ret = self.linux_dist._set_zfcp_config_files(fcp, target_wwpn, target_lun) expect = '\n'.join((set_zfcp_conf, trigger_uevent)) self.assertEqual(ret, expect) def test_restart_multipath(self): ret = self.linux_dist._restart_multipath() expect = 'service multipathd restart\n' self.assertEqual(ret, expect) def test_set_zfcp_multipath(self): modprobe = 'modprobe dm-multipath' conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' cmd_conf = 'echo -e "%s" > /etc/multipath.conf' % conf_file cmd_mpathconf = 'mpathconf' restart = 'service multipathd restart\n' expect = '\n'.join((modprobe, cmd_conf, cmd_mpathconf, 'sleep 2', restart, 'sleep 2\n')) ret = self.linux_dist._set_zfcp_multipath() self.assertEqual(ret, expect) class RHEL7TestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(RHEL7TestCase, cls).setUpClass() cls.os_version = 'redhat7.2' def setUp(self): super(RHEL7TestCase, self).setUp() self.dist_manager = dist.LinuxDistManager() self.linux_dist = self.dist_manager.get_linux_dist(self.os_version)() @mock.patch('zvmsdk.dist.LinuxDist.create_mount_point') @mock.patch('zvmsdk.dist.rhel._set_zfcp_multipath') @mock.patch('zvmsdk.dist.rhel7._set_zfcp_config_files') @mock.patch('zvmsdk.dist.LinuxDist.wait_for_file_ready') @mock.patch('zvmsdk.dist.LinuxDist.settle_file_system') @mock.patch('zvmsdk.dist.rhel7._set_sysfs') @mock.patch('zvmsdk.dist.rhel._online_fcp_device') @mock.patch('zvmsdk.dist.LinuxDist.modprobe_zfcp_module') def test_get_volume_attach_configuration_cmds(self, check_module, online_device, set_sysfs, settle_file, wait_file, zfcp_config, zfcp_multipath, create_mount_point): """ RHEL7 """ fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' multipath = True mount_point = '/dev/sdz' check_module.return_value = 'fake_ret' online_device.return_value = 'fake_ret' set_sysfs.return_value = '' settle_file.return_value = '' wait_file.return_value = '' zfcp_config.return_value = 'fake_ret' zfcp_multipath.return_value = 'fake_ret' create_mount_point.return_value = 'fake_ret' self.linux_dist.get_volume_attach_configuration_cmds(fcp, wwpn, lun, multipath, mount_point) check_module.assert_called_once_with() online_device.assert_called_once_with(fcp) set_sysfs.assert_called_once_with(fcp, wwpn, lun) settle_file.assert_called_once_with() wait_file.assert_called_once_with(fcp, wwpn, lun) zfcp_config.assert_called_once_with(fcp, wwpn, lun) zfcp_multipath.assert_called_once_with() create_mount_point.assert_called_once_with(fcp, wwpn, lun, mount_point, multipath) @mock.patch('zvmsdk.dist.LinuxDist.remove_mount_point') @mock.patch('zvmsdk.dist.rhel7._restart_multipath') @mock.patch('zvmsdk.dist.rhel._offline_fcp_device') def test_get_volume_detach_configuration_cmds(self, offline_device, restart_multipath, remove_mount_point): fcp = '1fc5' wwpn = '0x5005076812341234' lun = '0x0026000000000000' multipath = True mount_point = '/dev/sdz' offline_device.return_value = 'fake_ret' restart_multipath.return_value = 'fake_ret' remove_mount_point.return_value = 'fake_ret' self.linux_dist.get_volume_detach_configuration_cmds(fcp, wwpn, lun, multipath, mount_point) offline_device.assert_called_once_with(fcp, wwpn, lun, multipath) restart_multipath.assert_called_once_with() remove_mount_point.assert_called_once_with(mount_point, wwpn, lun, multipath) def test_set_zfcp_config_files(self): """ RHEL7 """ fcp = '1fc5' target_wwpn = '0x5005076812341234' target_lun = '0x0026000000000000' device = '0.0.%s' % fcp # add to port(WWPN) set_zfcp_conf = 'echo "%(device)s %(wwpn)s %(lun)s" >> '\ '/etc/zfcp.conf' % {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} trigger_uevent = 'echo "add" >> /sys/bus/ccw/devices/%s/uevent\n'\ % device ret = self.linux_dist._set_zfcp_config_files(fcp, target_wwpn, target_lun) expect = '\n'.join((set_zfcp_conf, trigger_uevent)) self.assertEqual(ret, expect) def test_restart_multipath(self): ret = self.linux_dist._restart_multipath() expect = 'systemctl restart multipathd.service\n' self.assertEqual(ret, expect) def test_set_zfcp_multipath(self): modprobe = 'modprobe dm-multipath' conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' cmd_conf = 'echo -e "%s" > /etc/multipath.conf' % conf_file cmd_mpathconf = 'mpathconf' restart = 'systemctl restart multipathd.service\n' expect = '\n'.join((modprobe, cmd_conf, cmd_mpathconf, 'sleep 2', restart, 'sleep 2\n')) ret = self.linux_dist._set_zfcp_multipath() self.assertEqual(ret, expect) class SLESTestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(SLESTestCase, cls).setUpClass() def setUp(self): super(SLESTestCase, self).setUp() self.dist_manager = dist.LinuxDistManager() self.sles11_dist = self.dist_manager.get_linux_dist('sles11')() self.sles12_dist = self.dist_manager.get_linux_dist('sles12')() def test_set_zfcp_config_files(self): """ sles """ fcp = '1fc5' target_wwpn = '0x5005076812341234' target_lun = '0x0026000000000000' device = '0.0.%s' % fcp host_config = '/sbin/zfcp_host_configure %s 1' % device disk_config = '/sbin/zfcp_disk_configure ' +\ '%(device)s %(wwpn)s %(lun)s 1' %\ {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} create_config = 'touch /etc/udev/rules.d/51-zfcp-%s.rules' % device check_channel = ('out=`cat "/etc/udev/rules.d/51-zfcp-%s.rules" ' '| egrep -i "ccw/%s]online"`\n' % (device, device)) check_channel += 'if [[ ! $out ]]; then\n' check_channel += (' echo "ACTION==\\"add\\", SUBSYSTEM==\\"ccw\\", ' 'KERNEL==\\"%(device)s\\", IMPORT{program}=\\"' 'collect %(device)s %%k %(device)s zfcp\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device}) check_channel += (' echo "ACTION==\\"add\\", SUBSYSTEM==\\"drivers\\"' ', KERNEL==\\"zfcp\\", IMPORT{program}=\\"' 'collect %(device)s %%k %(device)s zfcp\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device}) check_channel += (' echo "ACTION==\\"add\\", ' 'ENV{COLLECT_%(device)s}==\\"0\\", ' 'ATTR{[ccw/%(device)s]online}=\\"1\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device}) check_channel += 'fi\n' check_channel += ('echo "ACTION==\\"add\\", KERNEL==\\"rport-*\\", ' 'ATTR{port_name}==\\"%(wwpn)s\\", ' 'SUBSYSTEMS==\\"ccw\\", KERNELS==\\"%(device)s\\",' 'ATTR{[ccw/%(device)s]%(wwpn)s/unit_add}=' '\\"%(lun)s\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device, 'wwpn': target_wwpn, 'lun': target_lun}) expect = '\n'.join((host_config, 'sleep 2', disk_config, 'sleep 2', create_config, check_channel)) ret = self.sles11_dist._set_zfcp_config_files(fcp, target_wwpn, target_lun) self.assertEqual(ret, expect) ret = self.sles12_dist._set_zfcp_config_files(fcp, target_wwpn, target_lun) self.assertEqual(ret, expect) def test_restart_multipath(self): ret = self.sles11_dist._restart_multipath() start_multipathd = 'systemctl restart multipathd\n' self.assertEqual(ret, start_multipathd) ret = self.sles12_dist._restart_multipath() start_multipathd = 'systemctl restart multipathd\n' self.assertEqual(ret, start_multipathd) def test_set_zfcp_multipath(self): modprobe = 'modprobe dm_multipath' conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' cmd_conf = 'echo -e "%s" > /etc/multipath.conf' % conf_file restart = 'systemctl restart multipathd\n' expect = '\n'.join((modprobe, cmd_conf, restart)) ret = self.sles11_dist._set_zfcp_multipath() self.assertEqual(ret, expect) ret = self.sles12_dist._set_zfcp_multipath() self.assertEqual(ret, expect) class UBUNTUTestCase(base.SDKTestCase): @classmethod def setUpClass(cls): super(UBUNTUTestCase, cls).setUpClass() def setUp(self): super(UBUNTUTestCase, self).setUp() self.dist_manager = dist.LinuxDistManager() self.linux_dist = self.dist_manager.get_linux_dist('ubuntu16')() def test_check_multipath_tools(self): multipath = 'multipath' ret = self.linux_dist._check_multipath_tools() self.assertEqual(ret, multipath) def test_restart_multipath(self): reload_map = 'systemctl restart multipath-tools.service\n' ret = self.linux_dist._restart_multipath() self.assertEqual(ret, reload_map) def test_set_zfcp_multipath(self): modprobe = 'multipath' conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' conf_file = 'defaults {\n' conf_file += '\tfind_multipaths no\n' conf_file += '}\n' cmd_conf = 'echo -e "%s" > /etc/multipath.conf' % conf_file restart = 'systemctl restart multipath-tools.service\n' expect = '\n'.join((modprobe, cmd_conf, restart)) ret = self.linux_dist._set_zfcp_multipath() self.assertEqual(ret, expect) def test_offline_fcp_device(self): fcp = '1fc5' target_wwpn = '0x5005076812341234' target_lun = '0x0026000000000000' multipath = True device = '0.0.%s' % fcp target = '%s:%s:%s' % (device, target_wwpn, target_lun) disk_offline = '/sbin/chzdev zfcp-lun %s -d' % target host_offline = '/sbin/chzdev zfcp-host %s -d' % fcp offline_dev = 'chccwdev -d %s' % fcp ret = self.linux_dist._offline_fcp_device(fcp, target_wwpn, target_lun, multipath) expect = '\n'.join((disk_offline, host_offline, offline_dev)) self.assertEqual(ret, expect) zVMCloudConnector-1.4.1/zvmsdk/tests/unit/test_api.py0000664000175000017510000002661213375735014022356 0ustar ruirui00000000000000# 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. import mock from zvmsdk import api from zvmsdk import exception from zvmsdk.tests.unit import base from zvmsdk import vmops class SDKAPITestCase(base.SDKTestCase): """Testcases for compute APIs.""" @classmethod def setUpClass(cls): super(SDKAPITestCase, cls).setUpClass() cls.userid = 'TESTUID' cls.userid_list = ["USERID1", "USERID2"] def setUp(self): super(SDKAPITestCase, self).setUp() vmops.VMOps.check_guests_exist_in_db = mock.MagicMock() self.api = api.SDKAPI() def test_init_ComputeAPI(self): self.assertTrue(isinstance(self.api, api.SDKAPI)) @mock.patch("zvmsdk.vmops.VMOps.get_info") def test_guest_get_info(self, ginfo): self.api.guest_get_info(self.userid) ginfo.assert_called_once_with(self.userid) @mock.patch("zvmsdk.vmops.VMOps.guest_deploy") def test_guest_deploy(self, guest_deploy): user_id = 'fakevm' image_name = 'fakeimg' transportfiles = '/tmp/transport.tgz' vdev = '0100' self.api.guest_deploy(user_id, image_name, transportfiles=transportfiles, vdev=vdev) guest_deploy.assert_called_with(user_id.upper(), image_name, transportfiles, None, vdev, None) @mock.patch("zvmsdk.imageops.ImageOps.image_import") def test_image_import(self, image_import): image_name = '95a4da37-9f9b-4fb2-841f-f0bb441b7544' url = "file:///install/temp/test.img" image_meta = {'os_version': "rhel6.7"} self.api.image_import(image_name, url, image_meta) image_import.assert_called_once_with(image_name, url, image_meta, remote_host=None) @mock.patch("zvmsdk.imageops.ImageOps.image_export") def test_image_export(self, image_export): image_name = '95a4da37-9f9b-4fb2-841f-f0bb441b7544' dest_url = "file:///install/temp/test.img" self.api.image_export(image_name, dest_url) image_export.assert_called_once_with(image_name, dest_url, None) @mock.patch("zvmsdk.vmops.VMOps.create_vm") def test_guest_create(self, create_vm): vcpus = 1 memory = 1024 disk_list = [] user_profile = 'profile' max_cpu = 10 max_mem = '4G' self.api.guest_create(self.userid, vcpus, memory, disk_list, user_profile, max_cpu, max_mem) create_vm.assert_called_once_with(self.userid, vcpus, memory, disk_list, user_profile, max_cpu, max_mem) @mock.patch("zvmsdk.vmops.VMOps.create_vm") def test_guest_create_with_default_max_cpu_memory(self, create_vm): vcpus = 1 memory = 1024 disk_list = [] user_profile = 'profile' self.api.guest_create(self.userid, vcpus, memory, disk_list, user_profile) create_vm.assert_called_once_with(self.userid, vcpus, memory, disk_list, user_profile, 32, '64G') @mock.patch("zvmsdk.imageops.ImageOps.image_query") def test_image_query(self, image_query): imagekeyword = 'eae09a9f_7958_4024_a58c_83d3b2fc0aab' self.api.image_query(imagekeyword) image_query.assert_called_once_with(imagekeyword) @mock.patch("zvmsdk.vmops.VMOps.delete_vm") @mock.patch("zvmsdk.vmops.VMOps.check_guests_exist_in_db") def test_guest_delete(self, cge, delete_vm): cge.return_value = True self.api.guest_delete(self.userid) cge.assert_called_once_with(self.userid, raise_exc=False) delete_vm.assert_called_once_with(self.userid) @mock.patch("zvmsdk.vmops.VMOps.delete_vm") @mock.patch("zvmsdk.vmops.VMOps.check_guests_exist_in_db") def test_guest_delete_userid_in_lower_case(self, cge, delete_vm): cge.return_value = True self.api.guest_delete('testuid') cge.assert_called_once_with(self.userid, raise_exc=False) delete_vm.assert_called_once_with(self.userid) @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.vmops.VMOps.check_guests_exist_in_db") def test_guest_delete_not_exist(self, cge, cue): cge.return_value = False cue.return_value = False self.api.guest_delete(self.userid) cge.assert_called_once_with(self.userid, raise_exc=False) cue.assert_called_once_with(self.userid) @mock.patch("zvmsdk.utils.check_userid_exist") @mock.patch("zvmsdk.vmops.VMOps.check_guests_exist_in_db") def test_guest_delete_not_exist_in_db(self, cge, cue): cge.return_value = False cue.return_value = True self.assertRaises(exception.SDKObjectNotExistError, self.api.guest_delete, self.userid) cge.assert_called_once_with(self.userid, raise_exc=False) cue.assert_called_once_with(self.userid) @mock.patch("zvmsdk.monitor.ZVMMonitor.inspect_stats") def test_guest_inspect_cpus_list(self, inspect_stats): self.api.guest_inspect_stats(self.userid_list) inspect_stats.assert_called_once_with(self.userid_list) @mock.patch("zvmsdk.monitor.ZVMMonitor.inspect_stats") def test_guest_inspect_cpus_single(self, inspect_stats): self.api.guest_inspect_stats(self.userid) inspect_stats.assert_called_once_with([self.userid]) @mock.patch("zvmsdk.monitor.ZVMMonitor.inspect_vnics") def test_guest_inspect_vnics_list(self, inspect_vnics): self.api.guest_inspect_vnics(self.userid_list) inspect_vnics.assert_called_once_with(self.userid_list) @mock.patch("zvmsdk.monitor.ZVMMonitor.inspect_vnics") def test_guest_inspect_vnics_single(self, inspect_vnics): self.api.guest_inspect_vnics(self.userid) inspect_vnics.assert_called_once_with([self.userid]) @mock.patch("zvmsdk.vmops.VMOps.guest_stop") def test_guest_stop(self, gs): self.api.guest_stop(self.userid) gs.assert_called_once_with(self.userid) @mock.patch("zvmsdk.vmops.VMOps.guest_stop") def test_guest_stop_with_timeout(self, gs): self.api.guest_stop(self.userid, timeout=300) gs.assert_called_once_with(self.userid, timeout=300) @mock.patch("zvmsdk.vmops.VMOps.guest_softstop") def test_guest_softstop(self, gss): self.api.guest_softstop(self.userid, timeout=300) gss.assert_called_once_with(self.userid, timeout=300) @mock.patch("zvmsdk.vmops.VMOps.guest_pause") def test_guest_pause(self, gp): self.api.guest_pause(self.userid) gp.assert_called_once_with(self.userid) @mock.patch("zvmsdk.vmops.VMOps.guest_unpause") def test_guest_unpause(self, gup): self.api.guest_unpause(self.userid) gup.assert_called_once_with(self.userid) @mock.patch("zvmsdk.vmops.VMOps.guest_config_minidisks") def test_guest_process_additional_disks(self, config_disks): disk_list = [{'vdev': '0101', 'format': 'ext3', 'mntdir': '/mnt/0101'}] self.api.guest_config_minidisks(self.userid, disk_list) config_disks.assert_called_once_with(self.userid, disk_list) @mock.patch("zvmsdk.imageops.ImageOps.image_delete") def test_image_delete(self, image_delete): image_name = 'eae09a9f_7958_4024_a58c_83d3b2fc0aab' self.api.image_delete(image_name) image_delete.assert_called_once_with(image_name) def test_set_vswitch(self): self.assertRaises(exception.SDKInvalidInputFormat, self.api.vswitch_set, "vswitch_name", unknown='fake_id') @mock.patch("zvmsdk.vmops.VMOps.create_disks") def test_guest_add_disks(self, cds): disk_list = [{'size': '1g'}] self.api.guest_create_disks(self.userid, disk_list) cds.assert_called_once_with(self.userid, disk_list) @mock.patch("zvmsdk.vmops.VMOps.create_disks") def test_guest_add_disks_nothing_to_do(self, cds): self.api.guest_create_disks('userid', []) cds.assert_not_called() @mock.patch("zvmsdk.vmops.VMOps.delete_disks") def test_guest_delete_disks(self, dds): vdev_list = ['0102', '0103'] self.api.guest_delete_disks(self.userid, vdev_list) dds.assert_called_once_with(self.userid, vdev_list) @mock.patch("zvmsdk.vmops.VMOps.live_resize_cpus") def test_guest_live_resize_cpus(self, live_resize_cpus): cpu_cnt = 3 self.api.guest_live_resize_cpus(self.userid, cpu_cnt) live_resize_cpus.assert_called_once_with(self.userid, cpu_cnt) @mock.patch("zvmsdk.vmops.VMOps.resize_cpus") def test_guest_resize_cpus(self, resize_cpus): cpu_cnt = 3 self.api.guest_resize_cpus(self.userid, cpu_cnt) resize_cpus.assert_called_once_with(self.userid, cpu_cnt) @mock.patch("zvmsdk.vmops.VMOps.live_resize_memory") def test_guest_live_resize_mem(self, live_resize_memory): size = "1024m" self.api.guest_live_resize_mem(self.userid, size) live_resize_memory.assert_called_once_with(self.userid, size) @mock.patch("zvmsdk.vmops.VMOps.resize_memory") def test_guest_resize_mem(self, resize_memory): size = "2g" self.api.guest_resize_mem(self.userid, size) resize_memory.assert_called_once_with(self.userid, size) @mock.patch("zvmsdk.networkops.NetworkOPS.grant_user_to_vswitch") def test_vswitch_grant_user(self, guv): self.api.vswitch_grant_user("testvsw", self.userid) guv.assert_called_once_with("testvsw", self.userid) @mock.patch("zvmsdk.volumeop.VolumeOperatorAPI.attach_volume_to_instance") def test_volume_attach(self, mock_attach): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'assigner_id': 'user1'} self.api.volume_attach(connection_info) mock_attach.assert_called_once_with(connection_info) @mock.patch("zvmsdk.volumeop.VolumeOperatorAPI." "detach_volume_from_instance") def test_volume_detach(self, mock_detach): connection_info = {'platform': 'x86_64', 'ip': '1.2.3.4', 'os_version': 'rhel7', 'multipath': False, 'target_wwpn': '1111', 'target_lun': '2222', 'zvm_fcp': 'b83c', 'assigner_id': 'user1'} self.api.volume_detach(connection_info) mock_detach.assert_called_once_with(connection_info) zVMCloudConnector-1.4.1/zvmsdk/dist.py0000775000175000017510000016342213442676324017377 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import netaddr import os import six from zvmsdk import config from zvmsdk import exception from zvmsdk import log from zvmsdk import smutclient CONF = config.CONF LOG = log.LOG @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 RHEL6, RHEL7, SLES11, SLES12 and UBUNTU16 are supported. """ def __init__(self): self._smutclient = smutclient.get_smutclient() def create_network_configuration_files(self, file_path, guest_networks, first, active=False): """Generate network configuration files for guest vm :param list guest_networks: a list of network info for the guest. It has one dictionary that contain some of the below keys for each network, the format is: {'ip_addr': (str) IP address, 'dns_addr': (list) dns addresses, 'gateway_addr': (str) gateway address, 'cidr': (str) cidr format 'nic_vdev': (str) VDEV of the nic} Example for guest_networks: [{'ip_addr': '192.168.95.10', 'dns_addr': ['9.0.2.1', '9.0.3.1'], 'gateway_addr': '192.168.95.1', 'cidr': "192.168.95.0/24", 'nic_vdev': '1000'}, {'ip_addr': '192.168.96.10', 'dns_addr': ['9.0.2.1', '9.0.3.1'], 'gateway_addr': '192.168.96.1', 'cidr': "192.168.96.0/24", 'nic_vdev': '1003}] :returns cfg_files: the network interface configuration file name and file content cmd_strings: shell command, helps to enable the network interface, will be put into znetconfig file clean_cmd: if first is true, it is used to erase the previous network interface configuration, will be put into invokeScript file net_enable_cmd: 'ip addr' and 'ip link' command to enable the new network interface """ cfg_files = [] cmd_strings = '' udev_cfg_str = '' dns_cfg_str = '' route_cfg_str = '' net_enable_cmd = '' cmd_str = None file_path = self._get_network_file_path() file_name_route = file_path + 'routes' if first: clean_cmd = self._get_clean_command() else: clean_cmd = '' file_name_dns = self._get_dns_filename() for network in guest_networks: base_vdev = network['nic_vdev'].lower() file_name = self._get_device_filename(base_vdev) (cfg_str, cmd_str, dns_str, route_str, net_cmd) = self._generate_network_configuration( network, base_vdev, active=active) LOG.debug('Network configure file content is: %s', cfg_str) target_net_conf_file_name = file_path + file_name cfg_files.append((target_net_conf_file_name, cfg_str)) udev_cfg_str += self._get_udev_configuration(base_vdev, '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 net_cmd is not None: net_enable_cmd += net_cmd if len(dns_str) > 0: dns_cfg_str += dns_str if len(route_str) > 0: route_cfg_str += route_str if len(dns_cfg_str) > 0: cfg_files.append((file_name_dns, dns_cfg_str)) cmd_strings = self._append_udev_info(cmd_strings, cfg_files, file_name_route, route_cfg_str, udev_cfg_str, first) return cfg_files, cmd_strings, clean_cmd, net_enable_cmd def _generate_network_configuration(self, network, vdev, active=False): ip_v4 = dns_str = gateway_v4 = '' ip_cidr = netmask_v4 = broadcast_v4 = '' net_cmd = '' if (('ip_addr' in network.keys()) and (network['ip_addr'] is not None)): ip_v4 = network['ip_addr'] if (('gateway_addr' in network.keys()) and (network['gateway_addr'] is not None)): gateway_v4 = network['gateway_addr'] if (('dns_addr' in network.keys()) and (network['dns_addr'] is not None) and (len(network['dns_addr']) > 0)): for dns in network['dns_addr']: dns_str += 'nameserver ' + dns + '\n' if (('cidr' in network.keys()) and (network['cidr'] is not None)): ip_cidr = network['cidr'] netmask_v4 = str(netaddr.IPNetwork(ip_cidr).netmask) broadcast_v4 = str(netaddr.IPNetwork(ip_cidr).broadcast) if broadcast_v4 == 'None': broadcast_v4 = '' device = self._get_device_name(vdev) 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) if active and ip_v4 != '': if ip_cidr != '': mask = ip_cidr.rpartition('/')[2] else: mask = '32' full_ip = '%s/%s' % (ip_v4, mask) net_cmd = self._enable_network_interface(device, full_ip, broadcast_v4) return cfg_str, cmd_str, dns_str, route_str, net_cmd def get_simple_znetconfig_contents(self): return '\n'.join(('cio_ignore -R', 'znetconf -A', 'cio_ignore -u')) def get_device_name(self, vdev): return self._get_device_name(vdev) def get_network_configuration_files(self, vdev): vdev = vdev.lower() file_path = self._get_network_file_path() device = self._get_device_filename(vdev) target_net_conf_file_name = os.path.join(file_path, device) return target_net_conf_file_name def delete_vdev_info(self, vdev): cmd = self._delete_vdev_info(vdev) return cmd @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 def modprobe_zfcp_module(self): # modprobe zfcp module modprobe = 'modprobe zfcp\n' return modprobe @abc.abstractmethod def _get_source_device_path(self, fcp, target_wwpn, target_lun): # A sample path for rhel/sles is like: # '/dev/disk/by-path/ccw-0.0.1fb3-zfcp-0x5005076801102991:0x0021000000000000'. # On Ubuntu it's like: # '/dev/disk/by-path/ccw-0.0.1fb3-fc-0x5005076801102991-lun-33' pass def _get_wwid(self): var_wwid_line = ('wwid_line=`/sbin/udevadm info --query=all ' '--name=$SourceDevice | egrep -a -i \"ID_SERIAL=\"`') var_wwid = 'WWID=${wwid_line##*=}\n' wwid = '\n'.join((var_wwid_line, var_wwid)) return wwid def _get_rules_config_file_path(self): config_file_lib = '/lib/udev/rules.d/56-zfcp.rules' find_config = 'ConfigLib="%s"\n' % config_file_lib find_config += 'if [ -e "$ConfigLib" ]\n' find_config += 'then\n' find_config += ' ConfigFile="/lib/udev/rules.d/56-zfcp.rules"\n' find_config += 'else\n' find_config += ' ConfigFile="/etc/udev/rules.d/56-zfcp.rules"\n' find_config += 'fi\n' # TODO: if /etc/xxx not exist? return find_config def _add_udev_rules(self, mount_point, target_wwpn, target_lun, multipath): # TODO: mount_point format check? # get var value of TargetFile, WWPN, LUN target_filename = mount_point.replace('/dev/', '') var_target_file = 'TargetFile="%s"\n' % target_filename var_wwpn = 'WWPN="%s"\n' % target_wwpn var_lun = 'LUN="%s"\n' % target_lun # find the right path of config file var_config_file = self._get_rules_config_file_path() # add rules if multipath: # KERNEL: device name in kernel # ENV: environment variable # SYMLINK: create symbol link for device under /dev link_item = ('KERNEL==\\"dm-*\\", ' 'ENV{DM_UUID}==\\"mpath-$WWID\\", ' 'SYMLINK+=\\"$TargetFile\\"') else: link_item = ('KERNEL==\\"sd*\\", ATTRS{wwpn}==\\"$WWPN\\", ' 'ATTRS{fcp_lun}==\\"$LUN\\", ' 'SYMLINK+=\\"$TargetFile%n\\"') var_link_item = 'LinkItem="%s"\n' % link_item add_rules_cmd = ''.join((var_target_file, var_wwpn, var_lun, var_config_file, var_link_item, 'echo -e $LinkItem >> $ConfigFile\n')) return add_rules_cmd def _remove_udev_rules(self, mount_point, target_wwpn, target_lun, multipath): # TODO: mount_point format check? # get file name target_filename = mount_point.replace('/dev/', '') var_target_file = 'TargetFile="%s"\n' % target_filename var_wwpn = 'WWPN="%s"\n' % target_wwpn var_lun = 'LUN="%s"\n' % target_lun # find the right path of config file var_config_file = self._get_rules_config_file_path() # remove rules if multipath: remove_rules_cmd = ('sed -i -e /SYMLINK+=\\"$TargetFile\\"/d ' '$ConfigFile\n') else: remove_rules_cmd = ('sed -i -e ' '/SYMLINK+=\\"$TargetFile%n\\"/d ' '$ConfigFile\n') cmds = ''.join((var_target_file, var_wwpn, var_lun, var_config_file, remove_rules_cmd)) return cmds def _reload_rules_config_file(self, multipath): # reload the rules by sending reload signal to systemd-udevd reload_cmd = 'udevadm control --reload' # trigger uevent with the device path in /sys if multipath: create_symlink_cmd = 'udevadm trigger --sysname-match=dm-*' else: create_symlink_cmd = 'udevadm trigger --sysname-match=sd*' return '\n'.join((reload_cmd, create_symlink_cmd)) def create_mount_point(self, fcp, target_wwpn, target_lun, mount_point, multipath): # get path of source device var_source_device = self._get_source_device_path(fcp, target_wwpn, target_lun) # get WWID var_wwid = self._get_wwid() # add rules into config file add_rules = self._add_udev_rules(mount_point, target_wwpn, target_lun, multipath) # reload the rules reload_rules = self._reload_rules_config_file(multipath) return ''.join((var_source_device, var_wwid, add_rules, reload_rules)) def remove_mount_point(self, mount_point, target_wwpn, target_lun, multipath): # remove rules remove_rules = self._remove_udev_rules(mount_point, target_wwpn, target_lun, multipath) # reload the rules reload_rules = self._reload_rules_config_file(multipath) return '\n'.join((remove_rules, reload_rules)) def settle_file_system(self): # Settle the file system so when we are done # the device is fully available settle = 'if [[ $(which udevadm 2> /dev/null) != \'\' ]]; then\n' settle += ' udevadm settle\n' settle += 'else\n' settle += ' udevsettle\n' settle += 'fi\n' return settle def wait_for_file_ready(self, fcp, target_wwpn, target_lun): # Sometimes the file takes longer to appear. # We will wait up to 3 minutes. var_src_dev = self._get_source_device_path(fcp, target_wwpn, target_lun) scripts = 'if [[ ! -e $SourceDevice ]]; then\n' scripts += ' maxTime=0\n' scripts += ' for time in 1 2 2 5 10 10 30 60 60\n' scripts += ' do\n' scripts += ' if [[ -e $SourceDevice ]]; then\n' scripts += ' break\n' scripts += ' fi\n' scripts += ' maxTime=$maxTime+$time\n' scripts += ' sleep $time\n' scripts += ' done\n' scripts += 'fi\n' return '\n'.join((var_src_dev, scripts)) def get_volume_attach_configuration_cmds(self, fcp, target_wwpn, target_lun, multipath, mount_point): shell_cmds = '' shell_cmds += self.modprobe_zfcp_module() shell_cmds += self._online_fcp_device(fcp) shell_cmds += self._set_sysfs(fcp, target_wwpn, target_lun) shell_cmds += self._set_zfcp_config_files(fcp, target_wwpn, target_lun) shell_cmds += self.settle_file_system() shell_cmds += self.wait_for_file_ready(fcp, target_wwpn, target_lun) # TODO:rollback?? if multipath: shell_cmds += self._set_zfcp_multipath() if mount_point != '': shell_cmds += self.create_mount_point(fcp, target_wwpn, target_lun, mount_point, multipath) return '\n'.join(('#!/bin/bash', shell_cmds)) def get_volume_detach_configuration_cmds(self, fcp, target_wwpn, target_lun, multipath, mount_point): shell_cmds = '' shell_cmds += self._offline_fcp_device(fcp, target_wwpn, target_lun, multipath) if multipath: shell_cmds += self._restart_multipath() if mount_point != '': shell_cmds += self.remove_mount_point(mount_point, target_wwpn, target_lun, multipath) return '\n'.join(('#!/bin/bash', shell_cmds)) @abc.abstractmethod def _online_fcp_device(self, fcp): pass @abc.abstractmethod def _offline_fcp_device(self, fcp, target_wwpn, target_lun, multipath): pass @abc.abstractmethod def _set_sysfs(self, fcp, target_wwpn, target_lun): pass @abc.abstractmethod def _set_zfcp_config_files(self, fcp, target_wwpn, target_lun): pass @abc.abstractmethod def _restart_multipath(self): pass @abc.abstractmethod def _set_zfcp_multipath(self): pass @abc.abstractmethod def _config_to_persistent(self, multipath): pass @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, vdev): """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 _enable_network_interface(self, device, ip, broadcast): """construct a router string.""" pass @abc.abstractmethod def _get_clean_command(self): """construct a clean command to remove.""" 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, vdev): """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, cmd_str, cfg_files, file_name_route, route_cfg_str, udev_cfg_str, first=False): return cmd_str @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 @abc.abstractmethod def create_active_net_interf_cmd(self): """construct active command which will initialize and configure vm.""" pass @abc.abstractmethod def _delete_vdev_info(self, vdev): """delete udev rules file.""" pass def generate_set_hostname_script(self, hostname): lines = ['#!/bin/bash\n', 'echo -n %s > /etc/hostname\n' % hostname, '/bin/hostname %s\n' % hostname] return lines class rhel(LinuxDist): def _get_network_file_path(self): return '/etc/sysconfig/network-scripts/' 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 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, vdev): return 'eth' + str(vdev).zfill(4) def _get_udev_configuration(self, device, dev_channel): return '' def _append_udev_info(self, cmd_str, cfg_files, file_name_route, route_cfg_str, udev_cfg_str, first=False): return cmd_str 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 def _enable_network_interface(self, device, ip, broadcast): return '' def _delete_vdev_info(self, vdev): return '' def _online_fcp_device(self, fcp): """rhel online zfcp. sampe to all rhel distro.""" # cio_ignore cio_ignore = '/sbin/cio_ignore -r %s > /dev/null\n' % fcp # set the fcp online online_dev = '/sbin/chccwdev -e %s > /dev/null\n' % fcp return cio_ignore + online_dev def _offline_fcp_device(self, fcp, target_wwpn, target_lun, multipath): """rhel offline zfcp. sampe to all rhel distro.""" offline_dev = 'chccwdev -d %s' % fcp delete_records = self._delete_zfcp_config_records(fcp, target_wwpn, target_lun) return '\n'.join((offline_dev, delete_records)) def _set_zfcp_multipath(self): """sampe to all rhel distro""" # update multipath configuration modprobe = 'modprobe dm-multipath' conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' conf_cmd = 'echo -e "%s" > /etc/multipath.conf' % conf_file mpathconf = 'mpathconf' restart = self._restart_multipath() return '\n'.join((modprobe, conf_cmd, mpathconf, 'sleep 2', restart, 'sleep 2\n')) def _config_to_persistent(self): """rhel""" pass def _delete_zfcp_config_records(self, fcp, target_wwpn, target_lun): """rhel""" device = '0.0.%s' % fcp data = {'wwpn': target_wwpn, 'lun': target_lun, 'device': device, 'zfcpConf': '/etc/zfcp.conf'} delete_records_cmd = ('sed -i -e ' '\"/%(device)s %(wwpn)s %(lun)s/d\" ' '%(zfcpConf)s\n' % data) return delete_records_cmd def _get_source_device_path(self, fcp, target_wwpn, target_lun): """rhel""" device = '0.0.%s' % fcp data = {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} var_source_device = ('SourceDevice="/dev/disk/by-path/ccw-%(device)s-' 'zfcp-%(wwpn)s:%(lun)s"\n' % data) return var_source_device 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, vdev): return 'ifcfg-eth' + str(vdev).zfill(4) def _get_all_device_filename(self): return 'ifcfg-eth*' def _get_device_name(self, vdev): return 'eth' + str(vdev).zfill(4) def get_scp_string(self, root, fcp, wwpn, lun): return ("=root=%(root)s selinux=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 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}] def create_active_net_interf_cmd(self): return 'service zvmguestconfigure start' def _get_clean_command(self): files = os.path.join(self._get_network_file_path(), self._get_all_device_filename()) return '\nrm -f %s\n' % files def _restart_multipath(self): """rhel6""" restart_multipathd = 'service multipathd restart\n' return restart_multipathd def _set_sysfs(self, fcp, target_wwpn, target_lun): """rhel6 set WWPN and LUN in sysfs""" device = '0.0.%s' % fcp port_add = "echo '%s' > " % target_wwpn port_add += "/sys/bus/ccw/drivers/zfcp/%s/port_add" % device unit_add = "echo '%s' > " % target_lun unit_add += "/sys/bus/ccw/drivers/zfcp/%(device)s/%(wwpn)s/unit_add\n"\ % {'device': device, 'wwpn': target_wwpn} return '\n'.join((port_add, unit_add)) def _set_zfcp_config_files(self, fcp, target_wwpn, target_lun): """rhel6 set WWPN and LUN in configuration files""" device = '0.0.%s' % fcp set_zfcp_conf = 'echo "%(device)s %(wwpn)s %(lun)s" >> /etc/zfcp.conf'\ % {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} trigger_uevent = 'echo "add" >> /sys/bus/ccw/devices/%s/uevent\n'\ % device return '\n'.join((set_zfcp_conf, trigger_uevent)) def generate_set_hostname_script(self, hostname): lines = ['#!/bin/bash\n', 'sed -i "s/^HOSTNAME=.*/HOSTNAME=%s/" ' '/etc/sysconfig/network\n' % hostname, '/bin/hostname %s\n' % hostname] return lines 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, vdev): # Construct a device like ifcfg-enccw0.0.1000, ifcfg-enccw0.0.1003 return 'ifcfg-enccw0.0.' + str(vdev).zfill(4) def _get_all_device_filename(self): return 'ifcfg-enccw0.0.*' def _get_device_name(self, vdev): # Construct a device like enccw0.0.1000, enccw0.0.1003 return 'enccw0.0.' + str(vdev).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}] def _enable_network_interface(self, device, ip, broadcast): if len(broadcast) > 0: activeIP_str = 'ip addr add %s broadcast %s dev %s\n' % (ip, broadcast, device) else: activeIP_str = 'ip addr add %s dev %s\n' % (ip, device) activeIP_str += 'ip link set dev %s up\n' % device return activeIP_str def create_active_net_interf_cmd(self): return 'systemctl start zvmguestconfigure.service' def _get_clean_command(self): files = os.path.join(self._get_network_file_path(), self._get_all_device_filename()) return '\nrm -f %s\n' % files def _set_sysfs(self, fcp, target_wwpn, target_lun): """rhel7 set WWPN and LUN in sysfs""" device = '0.0.%s' % fcp unit_add = "echo '%s' > " % target_lun unit_add += "/sys/bus/ccw/drivers/zfcp/%(device)s/%(wwpn)s/unit_add\n"\ % {'device': device, 'wwpn': target_wwpn} return unit_add def _set_zfcp_config_files(self, fcp, target_wwpn, target_lun): """rhel7 set WWPN and LUN in configuration files""" device = '0.0.%s' % fcp set_zfcp_conf = 'echo "%(device)s %(wwpn)s %(lun)s" >> /etc/zfcp.conf'\ % {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} trigger_uevent = 'echo "add" >> /sys/bus/ccw/devices/%s/uevent\n'\ % device return '\n'.join((set_zfcp_conf, trigger_uevent)) def _restart_multipath(self): """rhel7""" restart_multipathd = 'systemctl restart multipathd.service\n' return restart_multipathd class sles(LinuxDist): def _get_network_file_path(self): return '/etc/sysconfig/network/' def _get_cfg_str(self, device, broadcast_v4, gateway_v4, ip_v4, netmask_v4, address_read, subchannels): cfg_str = "BOOTPROTO=\'static\'\n" cfg_str += "IPADDR=\'%s\'\n" % ip_v4 cfg_str += "NETMASK=\'%s\'\n" % netmask_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, vdev): return 'ifcfg-eth' + str(vdev).zfill(4) def _get_all_device_filename(self): return 'ifcfg-eth*' def _get_device_name(self, vdev): return 'eth' + str(vdev).zfill(4) def _append_udev_info(self, cmd_str, cfg_files, file_name_route, route_cfg_str, udev_cfg_str, first=False): udev_file_name = '/etc/udev/rules.d/70-persistent-net.rules' if first: cfg_files.append((udev_file_name, udev_cfg_str)) if len(route_cfg_str) > 0: cfg_files.append((file_name_route, route_cfg_str)) else: cmd_str += ("echo '%s'" ' >>%s\n' % (udev_cfg_str, udev_file_name)) if len(route_cfg_str) > 0: cmd_str += ('echo "%s"' ' >>%s\n' % (route_cfg_str, file_name_route)) return cmd_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.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\\""' '>/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-zfcp-0x%(wwpn)s:0x%(lun)s' srcdev = path % {'fcp': fcp, 'wwpn': wwpn, 'lun': lun} return srcdev def _enable_network_interface(self, device, ip, broadcast): return '' def _get_clean_command(self): files = os.path.join(self._get_network_file_path(), self._get_all_device_filename()) cmd = '\nrm -f %s\n' % files all_udev_rules_files = '/etc/udev/rules.d/51-qeth-0.0.*' cmd += 'rm -f %s\n' % all_udev_rules_files cmd += '> /boot/zipl/active_devices.txt\n' return cmd def _delete_vdev_info(self, vdev): """handle udev rules file.""" vdev = vdev.lower() rules_file_name = '/etc/udev/rules.d/51-qeth-0.0.%s.rules' % vdev cmd = 'rm -f %s\n' % rules_file_name address = '0.0.%s' % str(vdev).zfill(4) udev_file_name = '/etc/udev/rules.d/70-persistent-net.rules' cmd += "sed -i '/%s/d' %s\n" % (address, udev_file_name) cmd += "sed -i '/%s/d' %s\n" % (address, '/boot/zipl/active_devices.txt') return cmd def _online_fcp_device(self, fcp): """sles online fcp""" # cio_ignore cio_ignore = '/sbin/cio_ignore -r %s > /dev/null\n' % fcp # set the fcp online online_dev = '/sbin/chccwdev -e %s > /dev/null\n' % fcp return cio_ignore + online_dev def _set_sysfs(self, fcp, target_wwpn, target_lun): """sles set WWPN and LUN in sysfs""" device = '0.0.%s' % fcp unit_add = "echo '%s' > " % target_lun unit_add += "/sys/bus/ccw/drivers/zfcp/%(device)s/%(wwpn)s/unit_add\n"\ % {'device': device, 'wwpn': target_wwpn} return unit_add def _set_zfcp_config_files(self, fcp, target_wwpn, target_lun): """sles set WWPN and LUN in configuration files""" device = '0.0.%s' % fcp # host config host_config = '/sbin/zfcp_host_configure %s 1' % device # disk config disk_config = '/sbin/zfcp_disk_configure ' +\ '%(device)s %(wwpn)s %(lun)s 1' %\ {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} create_config = 'touch /etc/udev/rules.d/51-zfcp-%s.rules' % device # check if the file already contains the zFCP channel check_channel = ('out=`cat "/etc/udev/rules.d/51-zfcp-%s.rules" ' '| egrep -i "ccw/%s]online"`\n' % (device, device)) check_channel += 'if [[ ! $out ]]; then\n' check_channel += (' echo "ACTION==\\"add\\", SUBSYSTEM==\\"ccw\\", ' 'KERNEL==\\"%(device)s\\", IMPORT{program}=\\"' 'collect %(device)s %%k %(device)s zfcp\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device}) check_channel += (' echo "ACTION==\\"add\\", SUBSYSTEM==\\"drivers\\"' ', KERNEL==\\"zfcp\\", IMPORT{program}=\\"' 'collect %(device)s %%k %(device)s zfcp\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device}) check_channel += (' echo "ACTION==\\"add\\", ' 'ENV{COLLECT_%(device)s}==\\"0\\", ' 'ATTR{[ccw/%(device)s]online}=\\"1\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device}) check_channel += 'fi\n' check_channel += ('echo "ACTION==\\"add\\", KERNEL==\\"rport-*\\", ' 'ATTR{port_name}==\\"%(wwpn)s\\", ' 'SUBSYSTEMS==\\"ccw\\", KERNELS==\\"%(device)s\\",' 'ATTR{[ccw/%(device)s]%(wwpn)s/unit_add}=' '\\"%(lun)s\\""' '| tee -a /etc/udev/rules.d/51-zfcp-%(device)s.rules' '\n' % {'device': device, 'wwpn': target_wwpn, 'lun': target_lun}) return '\n'.join((host_config, 'sleep 2', disk_config, 'sleep 2', create_config, check_channel)) def _restart_multipath(self): """sles restart multipath""" # reload device mapper reload_map = 'systemctl restart multipathd\n' return reload_map def _set_zfcp_multipath(self): """sles""" # modprobe DM multipath kernel module modprobe = 'modprobe dm_multipath' conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' conf_cmd = 'echo -e "%s" > /etc/multipath.conf' % conf_file restart = self._restart_multipath() return '\n'.join((modprobe, conf_cmd, restart)) def _offline_fcp_device(self, fcp, target_wwpn, target_lun, multipath): """sles offline zfcp. sampe to all rhel distro.""" device = '0.0.%s' % fcp # disk config disk_config = '/sbin/zfcp_disk_configure ' +\ '%(device)s %(wwpn)s %(lun)s 0' %\ {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} # host config host_config = '/sbin/zfcp_host_configure %s 0' % device return '\n'.join((disk_config, host_config)) def _config_to_persistent(self): """sles""" pass def _get_source_device_path(self, fcp, target_wwpn, target_lun): """sles""" device = '0.0.%s' % fcp data = {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} var_source_device = ('SourceDevice="/dev/disk/by-path/ccw-%(device)s-' 'zfcp-%(wwpn)s:%(lun)s"\n' % data) return var_source_device 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')) def create_active_net_interf_cmd(self): return 'service zvmguestconfigure start' 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', 'wicked ifreload all')) 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}] def create_active_net_interf_cmd(self): return 'systemctl start zvmguestconfigure.service' def _enable_network_interface(self, device, ip, broadcast): if len(broadcast) > 0: activeIP_str = 'ip addr add %s broadcast %s dev %s\n' % (ip, broadcast, device) else: activeIP_str = 'ip addr add %s dev %s\n' % (ip, device) activeIP_str += 'ip link set dev %s up\n' % device return activeIP_str class ubuntu(LinuxDist): def create_network_configuration_files(self, file_path, guest_networks, first, active=False): """Generate network configuration files for guest vm :param list guest_networks: a list of network info for the guest. It has one dictionary that contain some of the below keys for each network, the format is: {'ip_addr': (str) IP address, 'dns_addr': (list) dns addresses, 'gateway_addr': (str) gateway address, 'cidr': (str) cidr format 'nic_vdev': (str) VDEV of the nic} Example for guest_networks: [{'ip_addr': '192.168.95.10', 'dns_addr': ['9.0.2.1', '9.0.3.1'], 'gateway_addr': '192.168.95.1', 'cidr': "192.168.95.0/24", 'nic_vdev': '1000'}, {'ip_addr': '192.168.96.10', 'dns_addr': ['9.0.2.1', '9.0.3.1'], 'gateway_addr': '192.168.96.1', 'cidr': "192.168.96.0/24", 'nic_vdev': '1003}] """ 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' net_enable_cmd = '' if first: clean_cmd = self._get_clean_command() else: clean_cmd = '' network_cfg_str = '' for network in guest_networks: base_vdev = network['nic_vdev'].lower() 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)) (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 if first: cfg_files.append((network_config_file_name, network_cfg_str)) else: cmd_strings = ('echo "%s" >>%s\n' % (network_cfg_str, network_config_file_name)) return cfg_files, cmd_strings, clean_cmd, net_enable_cmd def get_network_configuration_files(self, vdev): vdev = vdev.lower() network_hw_config_fname = self._get_device_filename(vdev) return network_hw_config_fname def delete_vdev_info(self, vdev): cmd = self._delete_vdev_info(vdev) return cmd def _delete_vdev_info(self, vdev): """handle vdev related info.""" vdev = vdev.lower() network_config_file_name = self._get_network_file() device = self._get_device_name(vdev) cmd = '\n'.join(("num=$(sed -n '/auto %s/=' %s)" % (device, network_config_file_name), "dns=$(awk 'NR==(\"\'$num\'\"+6)&&" "/dns-nameservers/' %s)" % network_config_file_name, "if [[ -n $dns ]]; then", " sed -i '/auto %s/,+6d' %s" % (device, network_config_file_name), "else", " sed -i '/auto %s/,+5d' %s" % (device, network_config_file_name), "fi")) return cmd 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 = gateway_v4 = '' netmask_v4 = broadcast_v4 = '' if (('ip_addr' in network.keys()) and (network['ip_addr'] is not None)): ip_v4 = network['ip_addr'] if (('gateway_addr' in network.keys()) and (network['gateway_addr'] is not None)): gateway_v4 = network['gateway_addr'] if (('dns_addr' in network.keys()) and (network['dns_addr'] is not None) and (len(network['dns_addr']) > 0)): for dns in network['dns_addr']: dns_str += 'dns-nameservers ' + dns + '\n' if (('cidr' in network.keys()) and (network['cidr'] is not None)): ip_cidr = network['cidr'] netmask_v4 = str(netaddr.IPNetwork(ip_cidr).netmask) broadcast_v4 = str(netaddr.IPNetwork(ip_cidr).broadcast) if broadcast_v4 == 'None': broadcast_v4 = '' 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 _enable_network_interface(self, device, ip): 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, cmd_str, cfg_files, file_name_route, route_cfg_str, udev_cfg_str, first=False): return cmd_str def get_scp_string(self, root, fcp, wwpn, lun): pass def get_zipl_script_lines(self, image, ramdisk, root, fcp, wwpn, lun): 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 def create_active_net_interf_cmd(self): return "systemctl start zvmguestconfigure.service" def _get_clean_command(self): files = self._get_device_filename('*') cmd = '\nrm -f %s\n' % files return cmd def _online_fcp_device(self, fcp): """ubuntu online fcp""" return '' def _set_sysfs(self, fcp, target_wwpn, target_lun): """ubuntu set WWPN and LUN in sysfs""" return '' def _set_zfcp_config_files(self, fcp, target_wwpn, target_lun): """ubuntu zfcp configuration """ host_config = '/sbin/chzdev zfcp-host %s -e' % fcp device = '0.0.%s' % fcp target = '%s:%s:%s' % (device, target_wwpn, target_lun) disk_config = '/sbin/chzdev zfcp-lun %s -e\n' % target return '\n'.join((host_config, disk_config)) def _check_multipath_tools(self): multipath = 'multipath' return multipath def _restart_multipath(self): """ubuntu""" # restart multipathd reload_map = 'systemctl restart multipath-tools.service\n' return reload_map def _set_zfcp_multipath(self): """ubuntu multipath setup multipath-tools and multipath-tools-boot must be set. Now only when the find_multpaths set to 'no', can the multipath command has output. maybe a bug? """ modprobe = self._check_multipath_tools() conf_file = '#blacklist {\n' conf_file += '#\tdevnode \\"*\\"\n' conf_file += '#}\n' conf_file = 'defaults {\n' conf_file += '\tfind_multipaths no\n' conf_file += '}\n' conf_cmd = 'echo -e "%s" > /etc/multipath.conf' % conf_file restart = self._restart_multipath() return '\n'.join((modprobe, conf_cmd, restart)) def _offline_fcp_device(self, fcp, target_wwpn, target_lun, multipath): """ubuntu offline zfcp.""" device = '0.0.%s' % fcp target = '%s:%s:%s' % (device, target_wwpn, target_lun) disk_offline = '/sbin/chzdev zfcp-lun %s -d' % target host_offline = '/sbin/chzdev zfcp-host %s -d' % fcp offline_dev = 'chccwdev -d %s' % fcp return '\n'.join((disk_offline, host_offline, offline_dev)) def _config_to_persistent(self): """ubuntu""" pass def _format_lun(self, lun): """ubuntu""" target_lun = int(lun[2:6], 16) return target_lun def _get_source_device_path(self, fcp, target_wwpn, target_lun): """ubuntu""" target_lun = self._format_lun(target_lun) device = '0.0.%s' % fcp data = {'device': device, 'wwpn': target_wwpn, 'lun': target_lun} var_source_device = ('SourceDevice="/dev/disk/by-path/ccw-%(device)s-' 'fc-%(wwpn)s-lun-%(lun)s"\n' % data) return var_source_device class ubuntu16(ubuntu): pass class LinuxDistManager(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.ZVMException(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.ZVMException(msg=msg) zVMCloudConnector-1.4.1/zvmsdk/monitor.py0000775000175000017510000002021613442676317020116 0ustar ruirui00000000000000# 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. import threading import time from zvmsdk import config from zvmsdk import log from zvmsdk import smutclient from zvmsdk import utils as zvmutils _MONITOR = None CONF = config.CONF LOG = log.LOG def get_monitor(): global _MONITOR if _MONITOR is None: _MONITOR = ZVMMonitor() return _MONITOR class ZVMMonitor(object): """Monitor support for ZVM""" _TYPES = ('cpumem', 'vnics') def __init__(self): self._cache = MeteringCache(self._TYPES) self._smutclient = smutclient.get_smutclient() self._namelist = zvmutils.get_namelist() def inspect_stats(self, uid_list): cpumem_data = self._get_inspect_data('cpumem', uid_list) # construct and return final result stats_data = {} for uid in uid_list: if uid in cpumem_data: with zvmutils.expect_invalid_resp_data(): user_data = cpumem_data[uid] guest_cpus = int(user_data['guest_cpus']) used_cpu_time = user_data['used_cpu_time'] used_cpu_time = int(used_cpu_time.partition(' ')[0]) elapsed_cpu_time = int( user_data['elapsed_cpu_time'].partition(' ')[0]) used_mem = int(user_data['used_memory'].partition(' ')[0]) max_mem = int(user_data['max_memory'].partition(' ')[0]) min_mem = int(user_data['min_memory'].partition(' ')[0]) shared_mem = int( user_data['shared_memory'].partition(' ')[0]) stats_data[uid] = { 'guest_cpus': guest_cpus, 'used_cpu_time_us': used_cpu_time, 'elapsed_cpu_time_us': elapsed_cpu_time, 'min_cpu_count': int(user_data['min_cpu_count']), 'max_cpu_limit': int(user_data['max_cpu_limit']), 'samples_cpu_in_use': int(user_data['samples_cpu_in_use']), 'samples_cpu_delay': int(user_data['samples_cpu_delay']), 'used_mem_kb': used_mem, 'max_mem_kb': max_mem, 'min_mem_kb': min_mem, 'shared_mem_kb': shared_mem } return stats_data def inspect_vnics(self, uid_list): vnics = self._get_inspect_data('vnics', uid_list) # construct and return final result target_vnics = {} for uid in uid_list: if uid in vnics: with zvmutils.expect_invalid_resp_data(): target_vnics[uid] = vnics[uid] return target_vnics def _cache_enabled(self): return CONF.monitor.cache_interval > 0 def _get_inspect_data(self, type, uid_list): inspect_data = {} update_needed = False for uid in uid_list: if not zvmutils.valid_userid(uid): continue cache_data = self._cache.get(type, uid) if cache_data is not None: inspect_data[uid] = cache_data else: if self._smutclient.get_power_state(uid) == 'on': update_needed = True inspect_data = {} break # If all data are found in cache, just return if not update_needed: return inspect_data # Call client to query latest data rdata = {} if type == 'cpumem': rdata = self._update_cpumem_data(uid_list) elif type == 'vnics': rdata = self._update_nic_data() return rdata def _update_cpumem_data(self, uid_list): namelist_uids = self._smutclient.namelist_query(self._namelist) sdk_managed_uids = self._smutclient.get_vm_list() mis_uids = list((set(uid_list) - set(namelist_uids)).intersection(set(sdk_managed_uids))) for muid in mis_uids: self._smutclient.namelist_add(self._namelist, muid) rdata = {} if self._cache_enabled(): rdata = self._smutclient.system_image_performance_query( self._namelist) self._cache.refresh('cpumem', rdata) else: rdata = self._smutclient.system_image_performance_query( self._namelist) return rdata def _update_nic_data(self): nics = {} vsw_dict = self._smutclient.virtual_network_vswitch_query_byte_stats() with zvmutils.expect_invalid_resp_data(): for vsw in vsw_dict['vswitches']: for nic in vsw['nics']: userid = nic['userid'] nic_entry = { 'vswitch_name': vsw['vswitch_name'], 'nic_vdev': nic['vdev'], 'nic_fr_rx': int(nic['nic_fr_rx']), 'nic_fr_tx': int(nic['nic_fr_tx']), 'nic_fr_rx_dsc': int(nic['nic_fr_rx_dsc']), 'nic_fr_tx_dsc': int(nic['nic_fr_tx_dsc']), 'nic_fr_rx_err': int(nic['nic_fr_rx_err']), 'nic_fr_tx_err': int(nic['nic_fr_tx_err']), 'nic_rx': int(nic['nic_rx']), 'nic_tx': int(nic['nic_tx'])} if nics.get(userid, None) is None: nics[userid] = [nic_entry] else: nics[userid].append(nic_entry) # Update cache if enabled if self._cache_enabled(): self._cache.refresh('vnics', nics) return nics class MeteringCache(object): """Cache for metering data.""" def __init__(self, types): self._cache = {} self._types = types self._lock = threading.RLock() self._reset(types) def _reset(self, types): with zvmutils.acquire_lock(self._lock): for type in types: self._cache[type] = {'expiration': time.time(), 'data': {}, } def _get_ctype_cache(self, ctype): return self._cache[ctype] def set(self, ctype, key, data): """Set or update cache content. :param ctype: cache type :param key: the key to be set value :param data: cache data """ with zvmutils.acquire_lock(self._lock): target_cache = self._get_ctype_cache(ctype) target_cache['data'][key] = data def get(self, ctype, key): with zvmutils.acquire_lock(self._lock): target_cache = self._get_ctype_cache(ctype) if(time.time() > target_cache['expiration']): return None else: return target_cache['data'].get(key, None) def delete(self, ctype, key): with zvmutils.acquire_lock(self._lock): target_cache = self._get_ctype_cache(ctype) if key in target_cache['data']: del target_cache['data'][key] def clear(self, ctype='all'): with zvmutils.acquire_lock(self._lock): if ctype == 'all': self._reset() else: target_cache = self._get_ctype_cache(ctype) target_cache['data'] = {} def refresh(self, ctype, data): with zvmutils.acquire_lock(self._lock): self.clear(ctype) target_cache = self._get_ctype_cache(ctype) target_cache['expiration'] = (time.time() + float(CONF.monitor.cache_interval)) for (k, v) in data.items(): self.set(ctype, k, v) zVMCloudConnector-1.4.1/zvmsdk/configdrive.py0000775000175000017510000001232113371225174020714 0ustar ruirui00000000000000# 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. import os import dist import tarfile import shutil import stat from zvmsdk import config CONF = config.CONF _DEFAULT_MODE = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO def _generate_vdev(base, offset): """Generate virtual device number based on base vdev :param base: base virtual device number, string of 4 bit hex. :param offset: offset to base, integer. """ vdev = hex(int(base, 16) + offset)[2:] return vdev.rjust(4, '0') def get_cfg_str(network_interface_info, os_version): ip_v4 = network_interface_info['ip_addr'] address_read = network_interface_info['nic_vdev'] broadcast_v4 = network_interface_info['broadcast_v4'] gateway_v4 = network_interface_info['gateway_v4'] netmask_v4 = network_interface_info['netmask_v4'] nic_vdev = network_interface_info['nic_vdev'] subchannels = ','.join(('0.0.' + nic_vdev, '0.0.' + _generate_vdev(nic_vdev, 1), '0.0.' + _generate_vdev(nic_vdev, 2))) linuxdist = dist.LinuxDistManager().get_linux_dist(os_version)() device_num = 0 device_name = linuxdist.get_device_name(device_num) cfg_str = 'DEVICE=' + device_name + '\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 generate_net_file(network_interface_info, net_file_path, os_version): cfg_str = get_cfg_str(network_interface_info, os_version) generate_file(cfg_str, net_file_path) def get_znetconfig_str(os_version): linuxdist = dist.LinuxDistManager().get_linux_dist(os_version)() udev_settle = linuxdist.get_znetconfig_contents() znetconfig = '\n'.join(('# !/bin/sh', udev_settle)) znetconfig += '\nrm -rf /tmp/znetconfig.sh\n' return znetconfig def generate_znetconfig_file(znetconfig_path, os_version): znetconfig = get_znetconfig_str(os_version) generate_file(znetconfig, znetconfig_path) def get_meta_data_str(): meta_data = '{\"files\":[{\"path\":' +\ '\"/etc/sysconfig/network-scripts/ifcfg-enccw0.0.1000\", ' meta_data += '\"content_path\": \"/content/0000\"},' +\ '{\"path\": \"/tmp/znetconfig.sh\", \"content_path\":' +\ ' \"/content/0001\"}], ' meta_data += '\"uuid\": \"4ec7a80d-201a-4c17-afbc-b0a93b66133b\", ' meta_data += '\"availability_zone\": \"nova\", ' meta_data += '\"hostname\": \"eckdrh72.5.novalocal\", ' meta_data += '\"launch_index\": 0, ' meta_data += '\"project_id\": \"94f8dc6644f24785a1383959dbba3f9e\", ' meta_data += '\"name\": \"eckdrh72.5\"}' return meta_data def generate_meta_data(meta_data_path): meta_data = get_meta_data_str() generate_file(meta_data, meta_data_path) def generate_file(file_content, path): f = open(path, 'w') f.write(file_content) f.close() def create_config_drive(network_interface_info, os_version): """Generate config driver for zVM guest vm. :param dict network_interface_info: Required keys: ip_addr - (str) IP address nic_vdev - (str) VDEV of the nic gateway_v4 - IPV4 gateway broadcast_v4 - IPV4 broadcast address netmask_v4 - IPV4 netmask :param str os_version: operating system version of the guest """ temp_path = CONF.guest.temp_path if not os.path.exists(temp_path): os.mkdir(temp_path) cfg_dir = os.path.join(temp_path, 'openstack') if os.path.exists(cfg_dir): shutil.rmtree(cfg_dir) content_dir = os.path.join(cfg_dir, 'content') latest_dir = os.path.join(cfg_dir, 'latest') os.mkdir(cfg_dir) os.mkdir(content_dir) os.mkdir(latest_dir) net_file = os.path.join(content_dir, '0000') generate_net_file(network_interface_info, net_file, os_version) znetconfig_file = os.path.join(content_dir, '0001') generate_znetconfig_file(znetconfig_file, os_version) meta_data_path = os.path.join(latest_dir, 'meta_data.json') generate_meta_data(meta_data_path) network_data_path = os.path.join(latest_dir, 'network_data.json') generate_file('{}', network_data_path) vendor_data_path = os.path.join(latest_dir, 'vendor_data.json') generate_file('{}', vendor_data_path) tar_path = os.path.join(temp_path, 'cfgdrive.tgz') tar = tarfile.open(tar_path, "w:gz") os.chdir(temp_path) tar.add('openstack') tar.close() return tar_path zVMCloudConnector-1.4.1/zvmsdk/constants.py0000775000175000017510000001226713371225174020442 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. HYPERVISOR_TYPE = 'zvm' ARCHITECTURE = 's390x' ALLOWED_VM_TYPE = 'zLinux' PROV_METHOD = 'netboot' ZVM_USER_DEFAULT_PRIVILEGE = 'G' CONFIG_DRIVE_FORMAT = 'tgz' DEFAULT_EPH_DISK_FMT = 'ext3' DISK_FUNC_NAME = 'setupDisk' RINV_HOST_KEYWORDS = { "zcc_userid": "ZCC USERID:", "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:", } DISKPOOL_KEYWORDS = { "disk_total": "Total:", "disk_used": "Used:", "disk_available": "Free:", } SET_VSWITCH_KEYWORDS = ["grant_userid", "user_vlan_id", "revoke_userid", "real_device_address", "port_name", "controller_name", "connection_value", "queue_memory_limit", "routing_value", "port_type", "persist", "gvrp_value", "mac_id", "uplink", "nic_userid", "nic_vdev", "lacp", "interval", "group_rdev", "iptimeout", "port_isolation", "promiscuous", "MAC_protect", "VLAN_counters"] DEV_STATUS = {'0': 'Device is not active.', '1': 'Device is active.', '2': 'Device is a backup device'} DEV_ERROR = {'0': 'No error.', '1': 'Port name conflict.', '2': 'No layer 2 support.', '3': 'Real device does not exist.', '4': 'Real device is attached elsewhere.', '5': 'Real device is not compatible type.', '6': 'Initialization error.', '7': 'Stalled OSA.', '8': 'Stalled controller.', '9': 'Controller connection severed.', '10': 'Primary or secondary routing conflict.', '11': 'Device is offline.', '12': 'Device was detached.', '13': 'IP/Ethernet type mismatch.', '14': 'Insufficient memory in controller ' 'virtual machine.', '15': 'TCP/IP configuration conflict.', '16': 'No link aggregation support.', '17': 'OSA-E attribute mismatch.', '18': 'Reserved for future use.', '19': 'OSA-E is not ready.', '20': 'Reserved for future use.', '21': 'Attempting restart for device.', '22': 'Exclusive user error.', '23': 'Device state is invalid.', '24': 'Port number is invalid for device.', '25': 'No OSA connection isolation.', '26': 'EQID mismatch.', '27': 'Incompatible controller.', '28': 'BACKUP detached.', '29': 'BACKUP not ready.', '30': 'BACKUP attempting restart.', '31': 'EQID mismatch.', '32': 'No HiperSockets bridge support.', '33': 'HiperSockets bridge error.'} SWITCH_STATUS = {'1': 'Virtual switch defined.', '2': 'Controller not available.', '3': 'Operator intervention required.', '4': 'Disconnected.', '5': 'Virtual devices attached to controller. ' 'Normally a transient state.', '6': 'OSA initialization in progress. ' 'Normally a transient state.', '7': 'OSA device not ready', '8': 'OSA device ready.', '9': 'OSA devices being detached. ' 'Normally a transient state.', '10': 'Virtual switch delete pending. ' 'Normally a transient state.', '11': 'Virtual switch failover recovering. ' 'Normally a transient state.', '12': 'Autorestart in progress. ' 'Normally a transient state.'} ZVM_VOLUMES_FILE = 'zvm_volumes' ZVM_VOLUME_STATUS = ['free', 'in-use'] VOLUME_MULTI_PASS = 'MULTI' POWER_STATE_ON = u'on' POWER_STATE_OFF = u'off' DATABASE_VOLUME = 'sdk_volume.sqlite' DATABASE_NETWORK = 'sdk_network.sqlite' DATABASE_GUEST = 'sdk_guest.sqlite' DATABASE_IMAGE = 'sdk_image.sqlite' DATABASE_FCP = 'sdk_fcp.sqlite' IMAGE_TYPE = { 'DEPLOY': 'netboot', 'CAPTURE': 'staging'} FILE_TYPE = { 'IMPORT': 'imported', 'EXPORT': 'exported'} SDK_DATA_PATH = '/var/lib/zvmsdk/' zVMCloudConnector-1.4.1/zvmsdk/log.py0000775000175000017510000000510413371225174017177 0ustar ruirui00000000000000# Copyright 2017,2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging import os from zvmsdk import config class Logger(): def __init__(self, logger): # create a logger self.logger = logging.getLogger(logger) self.log_level = logging.INFO def getlog(self): return self.logger def setup(self, log_dir, log_level, log_file_name='zvmsdk.log'): # make sure target directory exists if not os.path.exists(log_dir): if os.access(log_dir, os.W_OK): os.makedirs(log_dir) else: log_dir = '/tmp/' # Setup log level self.updateloglevel(log_level) self.logger.setLevel(self.log_level) # create a handler for the file log_file = os.path.join(log_dir, log_file_name) fh = logging.FileHandler(log_file) fh.setLevel(self.log_level) # set the formate of the handler formatter = logging.Formatter( '[%(asctime)s] [%(levelname)s] %(message)s', '%Y-%m-%d %H:%M:%S') fh.setFormatter(formatter) # add handler in the logger self.logger.addHandler(fh) def updateloglevel(self, level): log_level = level.upper() if log_level in ('LOGGING.INFO', 'INFO'): log_level = logging.INFO elif log_level in ('LOGGING.DEBUG', 'DEBUG'): log_level = logging.DEBUG elif log_level in ('LOGGING.WARN', 'WARN'): log_level = logging.WARN elif log_level in ('LOGGING.ERROR', 'ERROR'): log_level = logging.ERROR elif log_level in ('LOGGING.CRITICAL', 'CRITICAL'): log_level = logging.CRITICAL else: # default to logging.INFO log_level = logging.INFO self.log_level = log_level def getloglevel(self): return self.log_level def setup_log(): global LOGGER LOGGER.setup(log_dir=config.CONF.logging.log_dir, log_level=config.CONF.logging.log_level) LOGGER = Logger('ZVMSDK') LOG = LOGGER.getlog() zVMCloudConnector-1.4.1/setup.py0000664000175000017510000000420513442676317016246 0ustar ruirui00000000000000# 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. import setuptools from zvmsdk import version as sdkversion setuptools.setup( name='zVMCloudConnector', version=sdkversion.__version__, license='ASL 2.0', author='IBM', description='z/VM cloud management library in Python', long_description=open('README.rst').read(), url='https://github.com/mfcloud/python-zvm-sdk', keywords='zvm cloud library', install_requires=open('requirements.txt').read(), packages=setuptools.find_packages(exclude=["zvmsdk.tests.fvt*"]), classifiers=[ "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved :: Apache Software License", "Operating System :: POSIX :: Linux", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", ], entry_points={ 'wsgi_scripts': [ 'sdk_api = zvmsdk.sdkwsgi.wsgi:init_application', ] }, scripts=['scripts/sdkserver', 'zvmsdk/sdkwsgi/zvmsdk-wsgi', 'scripts/zvmsdk-gentoken'], data_files=[('/lib/systemd/system', ['data/sdkserver.service']), ('/var/lib/zvmsdk', ['data/setupDisk']), ('/etc/sudoers.d', ['data/sudoers-zvmsdk']), ('/etc/zvmsdk', ['data/uwsgi-zvmsdk.conf']), ('/etc/zvmsdk', ['doc/source/zvmsdk.conf.sample'])], ) zVMCloudConnector-1.4.1/data/0000775000175000017510000000000013442723341015432 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/data/sudoers-zvmsdk0000664000175000017510000000056713371225174020367 0ustar ruirui00000000000000zvmsdk ALL = (ALL) NOPASSWD:/sbin/vmcp, /opt/zthin/bin/smcli, /sbin/chccwdev, /sbin/cio_ignore, /sbin/fdasd, /sbin/fdisk, /usr/sbin/vmur, /bin/mount, /bin/umount, /sbin/mkfs, /sbin/mkfs.xfs, /sbin/dasdfmt, /opt/zthin/bin/unpackdiskimage, /opt/zthin/bin/creatediskimage, /opt/zthin/bin/linkdiskandbringonline, /opt/zthin/bin/offlinediskanddetach, /opt/zthin/bin/IUCV/iucvclnt zVMCloudConnector-1.4.1/data/sdkserver.service0000664000175000017510000000027313371225174021030 0ustar ruirui00000000000000[Unit] Description=zVM SDK API server After=network.target syslog.target [Service] Type=simple User=zvmsdk Group=zvmsdk ExecStart=/usr/bin/sdkserver [Install] WantedBy=multi-user.targetzVMCloudConnector-1.4.1/data/uwsgi-zvmsdk.conf0000664000175000017510000000065013371225174020756 0ustar ruirui00000000000000[uwsgi] chmod-socket = 666 uwsgi-socket = 127.0.0.1:35000 lazy-apps = true add-header = Connection: close buffer-size = 65535 thunder-lock = true plugins = python enable-threads = true exit-on-reload = true die-on-term = true master = true processes = 2 threads = 32 wsgi-file = /usr/bin/zvmsdk-wsgi pidfile = /tmp/zvmsdk-wsgi.pid socket = /tmp/zvmsdk-wsgi.socket uid = zvmsdk gid = zvmsdk logto = /var/log/zvmsdk/uwsgi.log zVMCloudConnector-1.4.1/data/setupDisk0000775000175000017510000005143113371225174017341 0ustar ruirui00000000000000#!/bin/bash ############################################################################### # 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. # ############################################################################### # This script is used to handle xCAT disk initialization and configuration(eg. # attach/detach a SCSI volume, add an additional ephemeral disk when vm is in # inactive status). It will be invoked and executed when vm start up. ############################################################################### version=3.0 function getOsVersion { # @Description: # Returns the Linux distro version in an easy to parse format. # @Input: # None # @Output: # os - Variable set with OS and version information. For example: # "rhel62" or "sles11sp2" # @Code: if [[ -e "/etc/os-release" ]]; then os=`cat /etc/os-release | grep "^ID=" | sed \ -e 's/ID=//' \ -e 's/"//g'` version=`cat /etc/os-release | grep "^VERSION_ID=" | sed \ -e 's/VERSION_ID=//' \ -e 's/"//g' \ -e 's/\.//'` os=$os$version #The /etc/SuSE-release file will be deprecated in sles11.4 and later release elif [[ -e "/etc/SuSE-release" ]]; then os='sles' version=`cat /etc/SuSE-release | grep "VERSION =" | sed \ -e 's/^.*VERSION =//' \ -e 's/\s*$//' \ -e 's/.//' \ -e 's/[^0-9]*([0-9]+).*/$1/'` os=$os$version # Append service level level=`echo "/etc/SuSE-release" | grep "LEVEL =" | sed \ -e 's/^.*LEVEL =//' \ -e 's/\s*$//' \ -e 's/.//' \ -e 's/[^0-9]*([0-9]+).*/$1/'` os=$os'sp'$level #The /etc/redhat-release file will be deprecated in rhel7 and later release elif [[ -e "/etc/redhat-release" ]]; then os='rhel' version=`cat /etc/redhat-release | grep -i "Red Hat Enterprise Linux Server" | sed \ -e 's/[A-Za-z\/\.\(\)]//g' \ -e 's/^ *//g' \ -e 's/ *$//g' \ -e 's/\s.*$//'` os=$os$version fi return } function onlineDevice { # @Description: # Brings a Linux device online. # @Input: # Device number, e.g. "0.0.000c" # @Output: # Return code indicates success or failure # @Code: device=$1 local funcName="onlineDevice" rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) if (( rc != 0 )); then if [[ -e /sbin/cio_ignore ]]; then rc=$(/sbin/cio_ignore -r 0.0.$device > /dev/null; echo $?) which udevadm &> /dev/null && udevadm settle || udevsettle fi rc=$(/sbin/chccwdev -e $device > /dev/null; echo $?) if (( rc != 0 )); then echo "$funcName (Error) Could not activate the virtual device $device" return 1 fi fi which udevadm &> /dev/null && udevadm settle || udevsettle return 0 } function setupDisk { # @Description: # Processes a disk file for the following functions: # Create a file system node # Remove a file system node # Setup a SCSI volume # Removes a SCSI volume # Add a mdisk based ephemeral disk # @Input: # Disk handling parameters # @Output: # None # @Code: local funcName="setupDisk" # Parse the parameter from parameter list for parameter in $@; do keyName=${parameter%\=*} value=${parameter#*\=} value=$(echo ${value} | sed -e 's/^ *//g') newKey='xcat_'$keyName eval $newKey=$value done # Remove the invokeScript.sh file after we have read it rm -f invokeScript.sh ########################################################################## # Handle creating a file system node # Disk handler input parameters: # action - "createfilesysnode" # srcFile - location/name of the source file for the mknod command # xcat_tgtFile - location/name of the target file for the mknod command ########################################################################## if [[ $xcat_action == "createfilesysnode" ]]; then echo "Creating a file system node, source: $xcat_srcFile, target: $xcat_tgtFile" if [[ ! -n $xcat_srcFile ]]; then echo "$funcName (Error) Source file for creating a file system node was not specified" return fi if [[ ! -n $xcat_tgtFile ]]; then echo "$funcName (Error) Target file for creating a file system node was not specified" return fi if [[ -e $xcat_tgtFile ]]; then echo "$funcName (Error) Target file for creating a file system node already exists" return fi configFile='/etc/udev/rules.d/56-zfcp.rules' # Create udev config file if not exist if [[ ! -e $configFile ]]; then touch $configFile if [[ $os == rhel* ]]; then echo "KERNEL==\"zfcp\", RUN+=\"/sbin/zfcpconf.sh\"" >> ${configFile} echo "KERNEL==\"zfcp\", RUN+=\"/sbin/multipath -r\"" >> ${configFile} fi fi tgtNode=$(echo ${xcat_tgtFile} | sed -e 's/^\/dev\///') if [[ $os == sles* || $os == rhel* ]]; then wwpn_lun=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*-zfcp-//') wwpn=$(echo ${wwpn_lun} | sed -e 's/:0x.*//') lun=$(echo ${wwpn_lun} | sed -e 's/^0x.*://') else wwpn_lun=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*-fc-//') wwpn=$(echo ${wwpn_lun} | sed -e 's/-lun-.*//') lun=$(echo ${wwpn_lun} | sed -e 's/^0x.*-lun-//') fi multipath=0 out=`echo $wwpn | grep ","` if [[ -n "$out" ]]; then multipath=1 fi if [[ $os == sles* || $os == rhel* ]]; then fcp=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*ccw-0.0.//' | sed -e 's/-zfcp-.*$//') else fcp=$(echo ${xcat_srcFile} | sed -e 's/^\/dev.*ccw-0.0.//' | sed -e 's/-fc-.*$//') fi oldIFS=$IFS IFS="," fcpList=($fcp) for fcp in ${fcpList[@]} do if [[ $multipath == 1 ]]; then # Find the name of the multipath device by arbitrary one path in the set wwpnList=($wwpn) for wwpn in ${wwpnList[@]} do if [[ ${wwpn:0:2} -ne "0x" ]]; then wwpn="0x$wwpn" fi if [[ $os == sles* || $os == rhel* ]]; then cur_wwpn_lun=${wwpn}:${lun} srcFile=$(echo ${xcat_srcFile} | sed -e 's/-zfcp-.*//')"-zfcp-"$cur_wwpn_lun srcFile=$(echo ${srcFile} | sed -e 's/ccw-.*-zfcp/ccw-0.0.'$fcp'-zfcp/') else cur_wwpn_lun=${wwpn}-lun-${lun} srcFile=$(echo ${xcat_srcFile} | sed -e 's/-fc-.*//')"-fc-"$cur_wwpn_lun srcFile=$(echo ${srcFile} | sed -e 's/ccw-.*-fc/ccw-0.0.'$fcp'-fc/') fi out=`/usr/bin/stat --printf=%n ${srcFile}` if (( $? != 0 )); then echo "$funcName (Error) Unable to stat the source file: $srcFile" continue fi out=`/sbin/udevadm info --query=all --name=$srcFile | grep ID_SERIAL=` devName=$(echo ${out} | sed -e 's/^E:\s//') multipathUuid=$(echo $devName | sed -e 's/ID_SERIAL=//') if [[ -n "$multipathUuid" ]]; then break fi done if [[ -z "$multipathUuid" ]]; then echo "$funcName (Error) Building up multipath failed!" return fi else if [[ $os == sles* || $os == rhel* ]]; then srcFile=$(echo ${xcat_srcFile} | sed -e 's/ccw-.*-zfcp/ccw-0.0.'$fcp'-zfcp/') else srcFile=$(echo ${xcat_srcFile} | sed -e 's/ccw-.*-zfcp/ccw-0.0.'$fcp'-fc/') fi out=`/usr/bin/stat --printf=%n ${srcFile}` if (( $? != 0 )); then echo "$funcName (Error) Unable to stat the source file: $xcat_srcFile" return fi fi done IFS=$oldIFS # Add the entry into udev config file if [[ $multipath == 1 ]]; then echo "KERNEL==\"dm*\", ENV{DM_UUID}==\"mpath-${multipathUuid}\", SYMLINK+=\"${tgtNode}\"" >> ${configFile} udevadm control --reload udevadm trigger --sysname-match=dm-* else echo "KERNEL==\"sd*\", ATTRS{wwpn}==\"${wwpn}\", ATTRS{fcp_lun}==\"${lun}\", SYMLINK+=\"${tgtNode}%n\"" >> ${configFile} udevadm control --reload udevadm trigger --sysname-match=sd* fi echo "$funcName successfully create the file system node ${xcat_tgtFile}" ########################################################################## # Handle removing a file system node # Disk file input parameters: # action - "removefilesysnode" # tgtFile - location/name of the target file for the mknod command ########################################################################## elif [[ $xcat_action == "removefilesysnode" ]]; then echo "Removing a file system node, target: $xcat_tgtFile" if [[ ! -n $xcat_tgtFile ]]; then echo "$funcName (Error) Target file for creating a file system node was not specified" return fi configFile='/etc/udev/rules.d/56-zfcp.rules' tgtNode=$(echo ${xcat_tgtFile} | sed -e 's/^\/dev\///') sed -i -e /SYMLINK+=\"${tgtNode}%n\"/d ${configFile} sed -i -e /SYMLINK+=\"${tgtNode}\"/d ${configFile} udevadm control --reload udevadm trigger --sysname-match=sd* udevadm trigger --sysname-match=dm-* echo "$funcName successfully remove the file system node ${xcat_tgtFile}" ########################################################################## # Handle adding a SCSI volume # Disk file input parameters: # action - "addScsiVolume" # fcpAddr - FCP device address # wwpn - WWPN number # lun - LUN number ########################################################################## elif [[ $xcat_action == "addScsiVolume" ]]; then echo "Adding a SCSI Volume, FCP addr: $xcat_fcpAddr, WWPN: $xcat_wwpn, LUN: $xcat_lun" # Validate the input if [[ ! -n $xcat_fcpAddr ]]; then echo "$funcName (Error) FCP address was not specified" return fi xcat_fcpAddr=`echo $xcat_fcpAddr | tr '[A-Z]' '[a-z]'` if [[ ! -n $xcat_wwpn ]]; then echo "$funcName (Error) WWPN was not specified" return fi xcat_wwpn=`echo $xcat_wwpn | tr '[A-Z]' '[a-z]'` if [[ ! -n $xcat_lun ]]; then echo "$funcName (Error) LUN was not specified" return fi xcat_lun=`echo $xcat_lun | tr '[A-Z]' '[a-z]'` decimal_lun=$((16#${xcat_lun:0:4})) # Online the device oldIFS=$IFS IFS="," fcp_list=($xcat_fcpAddr) for fcp in ${fcp_list[@]} do rc= onlineDevice $fcp if (( rc != 0 )); then return fi if [[ $os == sles12* ]]; then out=`cat /boot/zipl/active_devices.txt | grep -i "0.0.$fcp"` if [[ -z $out ]]; then /sbin/zfcp_host_configure 0.0.$fcp 1 fi elif [[ $os == sles11* ]]; then /sbin/zfcp_host_configure 0.0.$fcp 1 elif [[ $os == ubuntu* ]]; then /sbin/chzdev zfcp-host $fcp -e fi done multipath=0 out=`echo $xcat_wwpn | grep ","` if [[ -n "$out" ]]; then multipath=1 fi # Start multipathd service if [[ $multipath == 1 ]]; then if [[ $os == sles* ]]; then insserv multipathd elif [[ $os == rhel6* ]]; then chkconfig multipathd on else systemctl enable multipathd fi modprobe dm-multipath fi for fcp in ${fcp_list[@]} do wwpn_list=($xcat_wwpn) for wwpn in ${wwpn_list[@]} do # Set WWPN and LUN in sysfs echo 0x$xcat_lun > /sys/bus/ccw/drivers/zfcp/0.0.$fcp/0x$wwpn/unit_add # Set WWPN and LUN in configuration files if [[ $os == sles* ]]; then # SLES: /etc/udev/rules.d/51-zfcp* /sbin/zfcp_disk_configure 0.0.$fcp $wwpn $xcat_lun 1 # Configure zFCP device to be persistent touch /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules # Check if the file already contains the zFCP channel out=`cat "/etc/udev/rules.d/51-zfcp-0.0.$fcp.rules" | egrep -i "ccw/0.0.$fcp]online"` if [[ ! $out ]]; then echo "ACTION==\"add\", SUBSYSTEM==\"ccw\", KERNEL==\"0.0.$fcp\", IMPORT{program}=\"collect 0.0.$fcp %k 0.0.$fcp zfcp\"" \ | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules echo "ACTION==\"add\", SUBSYSTEM==\"drivers\", KERNEL==\"zfcp\", IMPORT{program}=\"collect 0.0.$fcp %k 0.0.$fcp zfcp\"" \ | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules echo "ACTION==\"add\", ENV{COLLECT_0.0.$fcp}==\"0\", ATTR{[ccw/0.0.$fcp]online}=\"1\"" \ | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules fi echo "ACTION==\"add\", KERNEL==\"rport-*\", ATTR{port_name}==\"0x$wwpn\", SUBSYSTEMS==\"ccw\", KERNELS==\"0.0.$fcp\", ATTR{[ccw/0.0.$fcp]0x$wwpn/unit_add}=\"0x$xcat_lun\"" \ | tee -a /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules elif [[ $os == rhel* ]]; then # RHEL: /etc/zfcp.conf echo "0.0.$fcp 0x$wwpn 0x$xcat_lun" >> /etc/zfcp.conf echo "add" > /sys/bus/ccw/devices/0.0.$fcp/uevent elif [[ $os == ubuntu* ]]; then # Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -e /sbin/chzdev zfcp-lun 0.0.$fcp:0x$wwpn:0x$xcat_lun -e fi # Settle the file system so when we are done the device is fully available if [[ $(which udevadm 2> /dev/null) != '' ]]; then udevadm settle else udevsettle fi if [[ $os == rhel* || $os == sles* ]]; then if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun}" ]]; then # Sometimes the file takes longer to appear. We will wait up to 3 minutes. maxTime=0 for time in 1 2 2 5 10 10 30 60 60 do if [[ -e "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun}" ]]; then # Leave the loop now that the file exists break fi maxTime=$maxTime+$time echo "Sleeping for $time seconds to allow /dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun} to be created" sleep $time done fi if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun}" ]]; then echo "/dev/disk/by-path/ccw-0.0.${fcp}-zfcp-0x${wwpn}:0x${xcat_lun} did not appear in $maxTime seconds, continuing." fi elif [[ $os == ubuntu* ]]; then if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun}" ]]; then # Sometimes the file takes longer to appear. We will wait up to 3 minutes. maxTime=0 for time in 1 2 2 5 10 10 30 60 60 do if [[ -e "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun}" ]]; then # Leave the loop now that the file exists break fi maxTime=$maxTime+$time echo "Sleeping for $time seconds to allow /dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun} to be created" sleep $time done fi if [[ ! -e "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun}" ]]; then echo "/dev/disk/by-path/ccw-0.0.${fcp}-fc-0x${wwpn}-lun-${decimal_lun} did not appear in $maxTime seconds, continuing." fi fi done done IFS=$oldIFS /sbin/multipath -r echo "$funcName successfully create the SCSI volume" ########################################################################## # Handle removing a SCSI volume # Disk file input parameters: # action - "removeScsiVolume" # fcpAddr - FCP device address # wwpn - WWPN number # lun - LUN number ########################################################################## elif [[ $xcat_action == "removeScsiVolume" ]]; then echo "Removing a SCSI Volume, FCP addr: $xcat_fcpAddr, WWPN: $xcat_wwpn, LUN: $xcat_lun" # Validate the input if [[ ! -n $xcat_fcpAddr ]]; then echo "$funcName (Error) FCP address was not specified" return fi xcat_fcpAddr=`echo $xcat_fcpAddr | tr '[A-Z]' '[a-z]'` if [[ ! -n $xcat_wwpn ]]; then echo "$funcName (Error) WWPN was not specified" return fi xcat_wwpn=`echo $xcat_wwpn | tr '[A-Z]' '[a-z]'` if [[ ! -n $xcat_lun ]]; then echo "$funcName (Error) LUN was not specified" return fi xcat_lun=`echo $xcat_lun | tr '[A-Z]' '[a-z]'` oldIFS=$IFS IFS="," fcp_list=($xcat_fcpAddr) for fcp in ${fcp_list[@]} do wwpn_list=($xcat_wwpn) for wwpn in ${wwpn_list[@]} do # Delete the SCSI device scsiDevice=`lszfcp -l 0x$xcat_lun | grep 0x$wwpn | cut -d " " -f2` if [[ -n $scsiDevice ]]; then echo 1 > "/sys/bus/scsi/devices/$scsiDevice/delete" fi # Delete WWPN and LUN from sysfs if [[ -e /sys/bus/ccw/drivers/zfcp/0.0.$fcp/0x$wwpn/unit_remove ]]; then if [[ $(which udevadm 2> /dev/null) != '' ]]; then udevadm settle else udevsettle fi echo 0x$xcat_lun > /sys/bus/ccw/drivers/zfcp/0.0.$fcp/0x$wwpn/unit_remove fi # Delete WWPN and LUN from configuration files if [[ $os == sles11* || $os == sles12* ]]; then # SLES: /etc/udev/rules.d/51-zfcp* expression="/$xcat_lun/d" sed --in-place -e $expression /etc/udev/rules.d/51-zfcp-0.0.$fcp.rules elif [[ $os == rhel* ]]; then # RHEL: /etc/zfcp.conf expression="/$xcat_lun/d" sed --in-place -e $expression /etc/zfcp.conf elif [[ $os == ubuntu* ]]; then # Ubuntu: chzdev zfcp-lun 0.0.$device:0x$wwpn:0x$lun -d /sbin/chzdev zfcp-lun 0.0.$fcp:0x$wwpn:0x$xcat_lun -d fi done done IFS=$oldIFS /sbin/multipath -W /sbin/multipath -r echo "$funcName successfully remove the SCSI volume" ########################################################################### # Handle adding a mdisk based ephemeral disk. # Disk file input parameters: # action - "addMdisk" # vaddr - virtual address of the minidisk # filesys - Filesystem type # mntdir - The directory that mount the mdisk to ########################################################################## elif [[ $xcat_action == "addMdisk" ]]; then echo "Adding a minidisk based ephemeral disk, Vaddr: $xcat_vaddr, Filesystem: $xcat_filesys mountpoint:$xcat_mntdir" # Validate the input if [[ ! -n $xcat_vaddr ]]; then echo "$funcName (Error) Virtual address was not specified" return fi xcat_vaddr=`echo $xcat_vaddr | tr '[A-Z]' '[a-z]'` # Online the device rc= onlineDevice $xcat_vaddr if (( rc != 0 )); then echo "$funcName (Error) fail to online the disk $xcat_vaddr" return fi # Configure the added dasd to be persistent echo "Permanently online the ephemeral disk" if [[ $os == rhel* ]]; then out=`cat "/etc/dasd.conf" | egrep -i $xcat_vaddr` if [[ ! $out ]]; then echo "0.0.$xcat_vaddr" >> /etc/dasd.conf fi elif [[ $os == sles* ]]; then /sbin/dasd_configure 0.0.$xcat_vaddr 1 elif [[ $os == ubuntu16* ]]; then touch /etc/sysconfig/hardware/config-ccw-0.0.$xcat_vaddr else echo "$funcName (Error) failed to permanently online the disk:$xcat_vaddr on os: $os, please check if $os is in the supported distribution list" return fi # Mount the mdisk to the specified mount point echo "Mounting the ephemeral disk $xcat_vaddr to directory $xcat_mntdir" if [[ -d $xcat_mntdir ]]; then rm -rf $xcat_mntdir fi mkdir -p $xcat_mntdir cp /etc/fstab /etc/fstab.bak out=`cat "/etc/fstab" | egrep -i "ccw-0.0.$xcat_vaddr"` if [[ $out ]]; then sed -i '/ccw-0.0.'"$xcat_vaddr"'/d' /etc/fstab fi if [[ $os == sles12* ]]; then echo "/dev/disk/by-path/ccw-0.0.${xcat_vaddr}-part1 $xcat_mntdir $xcat_filesys defaults,nofail 0 0" >> /etc/fstab else echo "/dev/disk/by-path/ccw-0.0.${xcat_vaddr}-part1 $xcat_mntdir $xcat_filesys defaults 0 0" >> /etc/fstab fi out=`mount -a 2>&1` if [[ "$out" ]]; then echo "Fail to mount the disk $xcat_vaddr with reason $out" mv /etc/fstab.bak /etc/fstab mount -a else echo "The disk $xcat_vaddr has been mounted to $xcat_mntdir in format $xcat_filesys successfully" fi fi return } ############################################################################ # Main Code Section ############################################################################ # Get Linux version getOsVersion setupDisk $@ rm -f setupDisk zVMCloudConnector-1.4.1/test-requirements.txt0000664000175000017510000000015013371225174020760 0ustar ruirui00000000000000mock>=2.0.0 # BSD python-subunit>=1.0.0 # Apache-2.0/BSD PyYAML>=3.10 # MIT sphinx>=1.6.2,!=1.8.0 # BSD zVMCloudConnector-1.4.1/PKG-INFO0000664000175000017510000000410013442723341015611 0ustar ruirui00000000000000Metadata-Version: 1.1 Name: zVMCloudConnector Version: 1.4.1 Summary: z/VM cloud management library in Python Home-page: https://github.com/mfcloud/python-zvm-sdk Author: IBM Author-email: UNKNOWN License: ASL 2.0 Description: z/VM Cloud Connector ******************** Description =========== z/VM cloud connector is a development sdk for manage z/VM. It provides a set of APIs to operate z/VM including guest, image, network, volume etc. Just like os-win for nova hyperv driver and oslo.vmware for nova vmware driver, z/VM cloud connector (zVMCloudConnector) is for nova z/vm driver and other z/VM related openstack driver such as neutron, ceilometer. Quickstart ========== Please refer to `Quick Start Guide `_. Documentation ============= Please refer to `Documentation of z/VM Cloud Connector `_. License ======= This package is licensed under the `Apache 2.0 License`_. .. _Apache 2.0 License: https://raw.githubusercontent.com/zhmcclient/python-zhmcclient/master/LICENSE Bug reporting ============= If you encounter any problem with this package, please open a bug against `cloud connector issue tracker`_ .. _cloud connector issue tracker: https://bugs.launchpad.net/python-zvm-sdk/+bug Keywords: zvm cloud library Platform: UNKNOWN Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 zVMCloudConnector-1.4.1/zvmconnector/0000775000175000017510000000000013442723341017250 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmconnector/__init__.py0000664000175000017510000000000013371225174021351 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/zvmconnector/socketclient.py0000664000175000017510000001324613371225174022321 0ustar ruirui00000000000000# 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. import json import six import socket SDKCLIENT_MODID = 110 SOCKET_ERROR = [{'overallRC': 101, 'modID': SDKCLIENT_MODID, 'rc': 101}, {1: "Failed to create client socket, error: %(error)s", 2: ("Failed to connect SDK server %(addr)s:%(port)s, " "error: %(error)s"), 3: ("Failed to send all API call data to SDK server, " "only %(sent)d bytes sent. API call: %(api)s"), 4: "Client receive empty data from SDK server", 5: ("Client got socket error when sending API call to " "SDK server, error: %(error)s"), 6: ("Client got socket error when receiving response " "from SDK server, error: %(error)s")}, "SDK client or server get socket error", ] INVALID_API_ERROR = [{'overallRC': 400, 'modID': SDKCLIENT_MODID, 'rc': 400}, {1: "Invalid API name, '%(msg)s'"}, "Invalid API name" ] class SDKSocketClient(object): def __init__(self, addr='127.0.0.1', port=2000, request_timeout=3600): self.addr = addr self.port = port # request_timeout is used to set the client socket timeout when # waiting results returned from server. self.timeout = request_timeout def _construct_api_name_error(self, msg): results = INVALID_API_ERROR[0] results.update({'rs': 1, 'errmsg': INVALID_API_ERROR[1][1] % {'msg': msg}, 'output': ''}) return results def _construct_socket_error(self, rs, **kwargs): results = SOCKET_ERROR[0] results.update({'rs': rs, 'errmsg': SOCKET_ERROR[1][rs] % kwargs, 'output': ''}) return results def call(self, func, *api_args, **api_kwargs): """Send API call to SDK server and return results""" if not isinstance(func, str) or (func == ''): msg = ('Invalid input for API name, should be a' 'string, type: %s specified.') % type(func) return self._construct_api_name_error(msg) # Create client socket try: cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error as err: return self._construct_socket_error(1, error=six.text_type(err)) try: # Set socket timeout cs.settimeout(self.timeout) # Connect SDK server try: cs.connect((self.addr, self.port)) except socket.error as err: return self._construct_socket_error(2, addr=self.addr, port=self.port, error=six.text_type(err)) # Prepare the data to be sent and switch to bytes if needed api_data = json.dumps((func, api_args, api_kwargs)) api_data = api_data.encode() # Send the API call data to SDK server sent = 0 total_len = len(api_data) got_error = False try: while (sent < total_len): this_sent = cs.send(api_data[sent:]) if this_sent == 0: got_error = True break sent += this_sent except socket.error as err: return self._construct_socket_error(5, error=six.text_type(err)) if got_error or sent != total_len: return self._construct_socket_error(3, sent=sent, api=api_data) # Receive data from server return_blocks = [] try: while True: block = cs.recv(4096) if not block: break block = bytes.decode(block) return_blocks.append(block) except socket.error as err: # When the sdkserver cann't handle all the client request, # some client request would be rejected. # Under this case, the client socket can successfully # connect/send, but would get exception in recv with error: # "error: [Errno 104] Connection reset by peer" return self._construct_socket_error(6, error=six.text_type(err)) finally: # Always close the client socket to avoid too many hanging # socket left. cs.close() # Transform the received stream to standard result form # This client assumes that the server would return result in # the standard result form, so client just return the received # data if return_blocks: results = json.loads(''.join(return_blocks)) else: results = self._construct_socket_error(4) return results zVMCloudConnector-1.4.1/zvmconnector/restclient.py0000664000175000017510000007440413442676324022017 0ustar ruirui00000000000000# 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. import json import os import requests import six import tempfile import threading import uuid # TODO:set up configuration file only for RESTClient and configure this value TOKEN_LOCK = threading.Lock() CHUNKSIZE = 4096 REST_REQUEST_ERROR = [{'overallRC': 101, 'modID': 110, 'rc': 101}, {1: "Request to zVM Cloud Connector failed: %(error)s", 2: "Token file not found: %(error)s", 3: "Request to url: %(url)s got unexpected response: " "status_code: %(status)s, reason: %(reason)s, " "text: %(text)s", 4: "Get Token failed: %(error)s"}, "zVM Cloud Connector request failed", ] SERVICE_UNAVAILABLE_ERROR = [{'overallRC': 503, 'modID': 110, 'rc': 503}, {2: "Service is unavailable. reason: %(reason)s," " text: %(text)s"}, "Service is unavailable", ] INVALID_API_ERROR = [{'overallRC': 400, 'modID': 110, 'rc': 400}, {1: "Invalid API name, '%(msg)s'"}, "Invalid API name", ] class UnexpectedResponse(Exception): def __init__(self, resp): self.resp = resp class ServiceUnavailable(Exception): def __init__(self, resp): self.resp = resp class TokenNotFound(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return repr(self.msg) class TokenFileOpenError(Exception): def __init__(self, msg): self.msg = msg class CACertNotFound(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return repr(self.msg) class APINameNotFound(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return repr(self.msg) class ArgsFormatError(Exception): def __init__(self, msg): self.msg = msg def __str__(self): return repr(self.msg) def fill_kwargs_in_body(body, **kwargs): for key in kwargs.keys(): body[key] = kwargs.get(key) def req_version(start_index, *args, **kwargs): url = '/' body = None return url, body def req_guest_list(start_index, *args, **kwargs): url = '/guests' body = None return url, body def req_guest_delete(start_index, *args, **kwargs): url = '/guests/%s' body = None return url, body def req_guest_get_definition_info(start_index, *args, **kwargs): url = '/guests/%s' body = None return url, body def req_guest_create(start_index, *args, **kwargs): url = '/guests' body = {'guest': {'userid': args[start_index], 'vcpus': args[start_index + 1], 'memory': args[start_index + 2]}} fill_kwargs_in_body(body['guest'], **kwargs) return url, body def req_guest_inspect_stats(start_index, *args, **kwargs): if type(args[start_index]) is str: url = '/guests/stats?userid=%s' % args[start_index] else: userids = ','.join(args[start_index]) url = '/guests/stats?userid=%s' % userids body = None return url, body def req_guest_inspect_vnics(start_index, *args, **kwargs): if type(args[start_index]) is str: url = '/guests/interfacestats?userid=%s' % args[start_index] else: userids = ','.join(args[start_index]) url = '/guests/interfacestats?userid=%s' % userids body = None return url, body def req_guests_get_nic_info(start_index, *args, **kwargs): url = '/guests/nics' # process appends in GET method userid = kwargs.get('userid', None) nic_id = kwargs.get('nic_id', None) vswitch = kwargs.get('vswitch', None) if ((userid is None) and (nic_id is None) and (vswitch is None)): append = '' else: append = "?" if userid is not None: append += 'userid=%s&' % userid if nic_id is not None: append += 'nic_id=%s&' % nic_id if vswitch is not None: append += 'vswitch=%s&' % vswitch append = append.strip('&') url = url + append body = None return url, body # FIXME: the order of args need adjust def req_guest_start(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'start'} return url, body def req_guest_stop(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'stop'} fill_kwargs_in_body(body, **kwargs) return url, body def req_guest_softstop(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'softstop'} fill_kwargs_in_body(body, **kwargs) return url, body def req_guest_pause(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'pause'} return url, body def req_guest_unpause(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'unpause'} return url, body def req_guest_reboot(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'reboot'} return url, body def req_guest_reset(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'reset'} return url, body def req_guest_get_console_output(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'get_console_output'} return url, body def req_guest_live_migrate(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'live_migrate_vm', 'dest_zcc_userid': args[start_index], 'destination': args[start_index + 1], 'parms': args[start_index + 2], 'operation': args[start_index + 3]} return url, body def req_guest_pre_migrate(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'register_vm', 'meta': args[start_index], 'net_set': args[start_index + 1]} return url, body def req_guest_live_resize_cpus(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'live_resize_cpus', 'cpu_cnt': args[start_index]} return url, body def req_guest_resize_cpus(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'resize_cpus', 'cpu_cnt': args[start_index]} return url, body def req_guest_resize_mem(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'resize_mem', 'size': args[start_index]} return url, body def req_guest_live_resize_mem(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'live_resize_mem', 'size': args[start_index]} return url, body def req_guest_capture(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'capture', 'image': args[start_index]} fill_kwargs_in_body(body, **kwargs) return url, body def req_guest_deploy(start_index, *args, **kwargs): url = '/guests/%s/action' body = {'action': 'deploy', 'image': args[start_index]} fill_kwargs_in_body(body, **kwargs) return url, body def req_guest_get_info(start_index, *args, **kwargs): url = '/guests/%s/info' body = None return url, body def req_guest_create_nic(start_index, *args, **kwargs): url = '/guests/%s/nic' body = {'nic': {}} fill_kwargs_in_body(body['nic'], **kwargs) return url, body def req_guest_delete_nic(start_index, *args, **kwargs): url = '/guests/%s/nic/%s' body = {} fill_kwargs_in_body(body, **kwargs) return url, body def req_guest_nic_couple_to_vswitch(start_index, *args, **kwargs): url = '/guests/%s/nic/%s' body = {'info': {'couple': True, 'vswitch': args[start_index]}} fill_kwargs_in_body(body['info'], **kwargs) return url, body def req_guest_nic_uncouple_from_vswitch(start_index, *args, **kwargs): url = '/guests/%s/nic/%s' body = {'info': {'couple': False}} fill_kwargs_in_body(body['info'], **kwargs) return url, body def req_guest_create_network_interface(start_index, *args, **kwargs): url = '/guests/%s/interface' body = {'interface': {'os_version': args[start_index], 'guest_networks': args[start_index + 1]}} fill_kwargs_in_body(body['interface'], **kwargs) return url, body def req_guest_delete_network_interface(start_index, *args, **kwargs): url = '/guests/%s/interface' body = {'interface': {'os_version': args[start_index], 'vdev': args[start_index + 1]}} fill_kwargs_in_body(body['interface'], **kwargs) return url, body def req_guest_get_power_state(start_index, *args, **kwargs): url = '/guests/%s/power_state' body = None return url, body def req_guest_create_disks(start_index, *args, **kwargs): url = '/guests/%s/disks' body = {'disk_info': {'disk_list': args[start_index]}} return url, body def req_guest_delete_disks(start_index, *args, **kwargs): url = '/guests/%s/disks' body = {'vdev_info': {'vdev_list': args[start_index]}} return url, body def req_guest_config_minidisks(start_index, *args, **kwargs): url = '/guests/%s/disks' body = {'disk_info': {'disk_list': args[start_index]}} fill_kwargs_in_body(body['disk_info'], **kwargs) return url, body def req_volume_attach(start_index, *args, **kwargs): url = '/guests/volumes' body = {'info': {'connection': args[start_index]}} return url, body def req_volume_detach(start_index, *args, **kwargs): url = '/guests/volumes' body = {'info': {'connection': args[start_index]}} return url, body def req_get_volume_connector(start_index, *args, **kwargs): url = '/volumes/conn/%s' body = None return url, body def req_host_get_info(start_index, *args, **kwargs): url = '/host' body = None return url, body def req_host_diskpool_get_info(start_index, *args, **kwargs): url = '/host/diskpool' poolname = kwargs.get('disk_pool', None) append = '' if poolname is not None: append += "?poolname=%s" % poolname url += append body = None return url, body def req_image_import(start_index, *args, **kwargs): url = '/images' body = {'image': {'image_name': args[start_index], 'url': args[start_index + 1], 'image_meta': args[start_index + 2]}} fill_kwargs_in_body(body['image'], **kwargs) return url, body def req_image_query(start_index, *args, **kwargs): url = '/images' image_name = kwargs.get('imagename', None) if image_name is None: append = '' else: append = "?" append += "imagename=%s" % image_name url += append body = None return url, body def req_image_delete(start_index, *args, **kwargs): url = '/images/%s' body = None return url, body def req_image_export(start_index, *args, **kwargs): url = '/images/%s' body = {'location': {'dest_url': args[start_index]}} fill_kwargs_in_body(body['location'], **kwargs) return url, body def req_image_get_root_disk_size(start_index, *args, **kwargs): url = '/images/%s/root_disk_size' body = None return url, body def req_file_import(start_index, *args, **kwargs): url = '/files' file_spath = args[start_index] body = get_data_file(file_spath) return url, body def req_file_export(start_index, *args, **kwargs): url = '/files' body = {'source_file': args[start_index]} return url, body def req_token_create(start_index, *args, **kwargs): url = '/token' body = None return url, body def req_vswitch_get_list(start_index, *args, **kwargs): url = '/vswitches' body = None return url, body def req_vswitch_create(start_index, *args, **kwargs): url = '/vswitches' body = {'vswitch': {'name': args[start_index]}} fill_kwargs_in_body(body['vswitch'], **kwargs) return url, body def req_vswitch_delete(start_index, *args, **kwargs): url = '/vswitches/%s' body = None return url, body def req_vswitch_query(start_index, *args, **kwargs): url = '/vswitches/%s' body = None return url, body def req_vswitch_grant_user(start_index, *args, **kwargs): url = '/vswitches/%s' body = {'vswitch': {'grant_userid': args[start_index]}} fill_kwargs_in_body(body['vswitch'], **kwargs) return url, body def req_vswitch_revoke_user(start_index, *args, **kwargs): url = '/vswitches/%s' body = {'vswitch': {'revoke_userid': args[start_index]}} fill_kwargs_in_body(body['vswitch'], **kwargs) return url, body def req_vswitch_set_vlan_id_for_user(start_index, *args, **kwargs): url = '/vswitches/%s' body = {'vswitch': {'user_vlan_id': {'userid': args[start_index], 'vlanid': args[start_index + 1]}}} fill_kwargs_in_body(body['vswitch'], **kwargs) return url, body # Save data used for comprsing RESTful request # method: request type # args_required: arguments in args are required, record the count here. # if len(args) not equal to this number, raise exception # params_path: parameters amount in url path # request: function that provide url and body for comprosing a request DATABASE = { 'version': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_version}, 'guest_create': { 'method': 'POST', 'args_required': 3, 'params_path': 0, 'request': req_guest_create}, 'guest_list': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_guest_list}, 'guest_inspect_stats': { 'method': 'GET', 'args_required': 1, 'params_path': 0, 'request': req_guest_inspect_stats}, 'guest_inspect_vnics': { 'method': 'GET', 'args_required': 1, 'params_path': 0, 'request': req_guest_inspect_vnics}, 'guests_get_nic_info': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_guests_get_nic_info}, 'guest_delete': { 'method': 'DELETE', 'args_required': 1, 'params_path': 1, 'request': req_guest_delete}, 'guest_get_definition_info': { 'method': 'GET', 'args_required': 1, 'params_path': 1, 'request': req_guest_get_definition_info}, 'guest_start': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_start}, 'guest_stop': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_stop}, 'guest_softstop': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_softstop}, 'guest_pause': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_pause}, 'guest_unpause': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_unpause}, 'guest_reboot': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_reboot}, 'guest_reset': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_reset}, 'guest_get_console_output': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_get_console_output}, 'guest_register': { 'method': 'POST', 'args_required': 3, 'params_path': 1, 'request': req_guest_pre_migrate}, 'guest_live_migrate': { 'method': 'POST', 'args_required': 5, 'params_path': 1, 'request': req_guest_live_migrate}, 'guest_live_resize_cpus': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_live_resize_cpus}, 'guest_resize_cpus': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_resize_cpus}, 'guest_live_resize_mem': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_live_resize_mem}, 'guest_resize_mem': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_resize_mem}, 'guest_capture': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_capture}, 'guest_deploy': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_deploy}, 'guest_get_info': { 'method': 'GET', 'args_required': 1, 'params_path': 1, 'request': req_guest_get_info}, 'guest_create_nic': { 'method': 'POST', 'args_required': 1, 'params_path': 1, 'request': req_guest_create_nic}, 'guest_delete_nic': { 'method': 'DELETE', 'args_required': 2, 'params_path': 2, 'request': req_guest_delete_nic}, 'guest_nic_couple_to_vswitch': { 'method': 'PUT', 'args_required': 3, 'params_path': 2, 'request': req_guest_nic_couple_to_vswitch}, 'guest_nic_uncouple_from_vswitch': { 'method': 'PUT', 'args_required': 2, 'params_path': 2, 'request': req_guest_nic_uncouple_from_vswitch}, 'guest_create_network_interface': { 'method': 'POST', 'args_required': 3, 'params_path': 1, 'request': req_guest_create_network_interface}, 'guest_delete_network_interface': { 'method': 'DELETE', 'args_required': 3, 'params_path': 1, 'request': req_guest_delete_network_interface}, 'guest_get_power_state': { 'method': 'GET', 'args_required': 1, 'params_path': 1, 'request': req_guest_get_power_state}, 'guest_create_disks': { 'method': 'POST', 'args_required': 2, 'params_path': 1, 'request': req_guest_create_disks}, 'guest_delete_disks': { 'method': 'DELETE', 'args_required': 2, 'params_path': 1, 'request': req_guest_delete_disks}, 'guest_config_minidisks': { 'method': 'PUT', 'args_required': 2, 'params_path': 1, 'request': req_guest_config_minidisks}, 'volume_attach': { 'method': 'POST', 'args_required': 1, 'params_path': 0, 'request': req_volume_attach}, 'volume_detach': { 'method': 'DELETE', 'args_required': 1, 'params_path': 0, 'request': req_volume_detach}, 'get_volume_connector': { 'method': 'GET', 'args_required': 1, 'params_path': 1, 'request': req_get_volume_connector}, 'host_get_info': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_host_get_info}, 'host_diskpool_get_info': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_host_diskpool_get_info}, 'image_import': { 'method': 'POST', 'args_required': 3, 'params_path': 0, 'request': req_image_import}, 'image_query': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_image_query}, 'image_delete': { 'method': 'DELETE', 'args_required': 1, 'params_path': 1, 'request': req_image_delete}, 'image_export': { 'method': 'PUT', 'args_required': 2, 'params_path': 1, 'request': req_image_export}, 'image_get_root_disk_size': { 'method': 'GET', 'args_required': 1, 'params_path': 1, 'request': req_image_get_root_disk_size}, 'file_import': { 'method': 'PUT', 'args_required': 1, 'params_path': 0, 'request': req_file_import}, 'file_export': { 'method': 'POST', 'args_required': 1, 'params_path': 0, 'request': req_file_export}, 'token_create': { 'method': 'POST', 'args_required': 0, 'params_path': 0, 'request': req_token_create}, 'vswitch_get_list': { 'method': 'GET', 'args_required': 0, 'params_path': 0, 'request': req_vswitch_get_list}, 'vswitch_create': { 'method': 'POST', 'args_required': 1, 'params_path': 0, 'request': req_vswitch_create}, 'vswitch_delete': { 'method': 'DELETE', 'args_required': 1, 'params_path': 1, 'request': req_vswitch_delete}, 'vswitch_grant_user': { 'method': 'PUT', 'args_required': 2, 'params_path': 1, 'request': req_vswitch_grant_user}, 'vswitch_query': { 'method': 'GET', 'args_required': 1, 'params_path': 1, 'request': req_vswitch_query}, 'vswitch_revoke_user': { 'method': 'PUT', 'args_required': 2, 'params_path': 1, 'request': req_vswitch_revoke_user}, 'vswitch_set_vlan_id_for_user': { 'method': 'PUT', 'args_required': 3, 'params_path': 1, 'request': req_vswitch_set_vlan_id_for_user}, } def get_data_file(fpath): if fpath: return open(fpath, 'rb') class RESTClient(object): def __init__(self, ip='127.0.0.1', port=8888, ssl_enabled=False, verify=False, token_path=None): # SSL enable or not if ssl_enabled: self.base_url = "https://" + ip + ":" + str(port) else: self.base_url = "http://" + ip + ":" + str(port) # if value of verify is str, means its value is # the path of CA certificate if type(verify) == str: if not os.path.exists(verify): raise CACertNotFound('CA certificate file not found.') self.verify = verify self.token_path = token_path def _check_arguments(self, api_name, *args, **kwargs): # check api_name exist or not if api_name not in DATABASE.keys(): msg = "API name %s not exist." % api_name raise APINameNotFound(msg) # check args count is valid count = DATABASE[api_name]['args_required'] if len(args) < count: msg = "Missing some args,please check:%s." % args raise ArgsFormatError(msg) if len(args) > count: msg = "Too many args,please check:%s." % args raise ArgsFormatError(msg) def _get_admin_token(self, path): if os.path.exists(path): TOKEN_LOCK.acquire() try: with open(path, 'r') as fd: token = fd.read().strip() except Exception: raise TokenFileOpenError('token file open failed.') finally: TOKEN_LOCK.release() else: raise TokenNotFound('token file not found.') return token def _get_token(self): _headers = {'Content-Type': 'application/json'} admin_token = self._get_admin_token(self.token_path) _headers['X-Admin-Token'] = admin_token url = self.base_url + '/token' method = 'POST' response = requests.request(method, url, headers=_headers, verify=self.verify) if response.status_code == 503: # service unavailable raise ServiceUnavailable(response) else: try: token = response.headers['X-Auth-Token'] except KeyError: raise UnexpectedResponse(response) return token def _get_url_body_headers(self, api_name, *args, **kwargs): headers = {} headers['Content-Type'] = 'application/json' count_params_in_path = DATABASE[api_name]['params_path'] func = DATABASE[api_name]['request'] url, body = func(count_params_in_path, *args, **kwargs) if api_name in ['file_import']: headers['Content-Type'] = 'application/octet-stream' if count_params_in_path > 0: url = url % tuple(args[0:count_params_in_path]) full_url = '%s%s' % (self.base_url, url) return full_url, body, headers def _process_rest_response(self, response): content_type = response.headers.get('Content-Type') if ('application/json' not in content_type) and ( 'application/octet-stream' not in content_type): # Currently, all the response content from zvmsdk wsgi are # 'application/json' or application/octet-stream type. # If it is not, the response may be sent by HTTP server due # to internal server error or time out, # it is an unexpected response to the rest client. # If new content-type is added to the response by sdkwsgi, the # parsing function here is also required to change. raise UnexpectedResponse(response) # Read body into string if it isn't obviously image data if 'application/octet-stream' in content_type: # Do not read all response in memory when downloading an file body_iter = self._close_after_stream(response, CHUNKSIZE) else: body_iter = None return response, body_iter def api_request(self, url, method='GET', body=None, headers=None, **kwargs): _headers = {} _headers.update(headers or {}) if body is not None and not isinstance(body, six.string_types): try: body = json.dumps(body) except TypeError: # if data is a file-like object body = body if self.token_path is not None: _headers['X-Auth-Token'] = self._get_token() content_type = headers['Content-Type'] stream = content_type == 'application/octet-stream' if stream: response = requests.request(method, url, data=body, headers=_headers, verify=self.verify, stream=stream) else: response = requests.request(method, url, data=body, headers=_headers, verify=self.verify) return response def call(self, api_name, *args, **kwargs): try: # check validation of arguments self._check_arguments(api_name, *args, **kwargs) # get method by api_name method = DATABASE[api_name]['method'] # get url,body with api_name and method url, body, headers = self._get_url_body_headers(api_name, *args, **kwargs) response = self.api_request(url, method, body=body, headers=headers) # change response to SDK format resp, body_iter = self._process_rest_response(response) if api_name == 'file_export' and resp.status_code == 200: # Save the file in an temporary path return self._save_exported_file(body_iter) results = json.loads(resp.content) except TokenFileOpenError as err: errmsg = REST_REQUEST_ERROR[1][4] % {'error': err.msg} results = REST_REQUEST_ERROR[0] results.update({'rs': 4, 'errmsg': errmsg, 'output': ''}) except TokenNotFound as err: errmsg = REST_REQUEST_ERROR[1][2] % {'error': err.msg} results = REST_REQUEST_ERROR[0] results.update({'rs': 2, 'errmsg': errmsg, 'output': ''}) except UnexpectedResponse as err: errmsg = REST_REQUEST_ERROR[1][3] % ({ 'url': err.resp.url, 'status': err.resp.status_code, 'reason': err.resp.reason, 'text': err.resp.text}) results = REST_REQUEST_ERROR[0] results.update({'rs': 3, 'errmsg': errmsg, 'output': ''}) except ServiceUnavailable as err: errmsg = SERVICE_UNAVAILABLE_ERROR[1][2] % { 'reason': err.resp.reason, 'text': err.resp.text} results = SERVICE_UNAVAILABLE_ERROR[0] results.update({'rs': 2, 'errmsg': errmsg, 'output': ''}) except Exception as err: errmsg = REST_REQUEST_ERROR[1][1] % {'error': six.text_type(err)} results = REST_REQUEST_ERROR[0] results.update({'rs': 1, 'errmsg': errmsg, 'output': ''}) return results def _save_exported_file(self, body_iter): fname = str(uuid.uuid1()) tempDir = tempfile.mkdtemp() os.chmod(tempDir, 0o777) target_file = '/'.join([tempDir, fname]) self._save_file(body_iter, target_file) file_size = os.path.getsize(target_file) output = {'filesize_in_bytes': file_size, 'dest_url': target_file} results = {'overallRC': 0, 'modID': None, 'rc': 0, 'output': output, 'rs': 0, 'errmsg': ''} return results def _close_after_stream(self, response, chunk_size): """Iterate over the content and ensure the response is closed after.""" # Yield each chunk in the response body for chunk in response.iter_content(chunk_size=chunk_size): yield chunk # Once we're done streaming the body, ensure everything is closed. response.close() def _save_file(self, data, path): """Save an file to the specified path. :param data: binary data of the file :param path: path to save the file to """ with open(path, 'wb') as tfile: for chunk in data: tfile.write(chunk) zVMCloudConnector-1.4.1/zvmconnector/connector.py0000664000175000017510000000765113407042050021615 0ustar ruirui00000000000000# 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. from zvmconnector import socketclient from zvmconnector import restclient CONN_TYPE_SOCKET = 'socket' CONN_TYPE_REST = 'rest' class baseConnection(object): def request(self, api_name, *api_args, **api_kwargs): pass class socketConnection(baseConnection): def __init__(self, ip_addr='127.0.0.1', port=2000, timeout=3600): self.client = socketclient.SDKSocketClient(ip_addr, port, timeout) def request(self, api_name, *api_args, **api_kwargs): return self.client.call(api_name, *api_args, **api_kwargs) class restConnection(baseConnection): def __init__(self, ip_addr='127.0.0.1', port=8080, ssl_enabled=False, verify=False, token_path=None): self.client = restclient.RESTClient(ip_addr, port, ssl_enabled, verify, token_path) def request(self, api_name, *api_args, **api_kwargs): return self.client.call(api_name, *api_args, **api_kwargs) class ZVMConnector(object): def __init__(self, ip_addr=None, port=None, timeout=3600, connection_type=None, ssl_enabled=False, verify=False, token_path=None): """ :param str ip_addr: IP address of SDK server :param int port: Port of SDK server daemon :param int timeout: Wait timeout if request no response :param str connection_type: The value should be 'socket' or 'rest' :param boolean ssl_enabled: Whether SSL enabled or not. If enabled, use HTTPS instead of HTTP. The httpd server should enable SSL to support this. :param boolean/str verify: Either a boolean, in which case it controls whether we verify the server's TLS certificate, or a string, in which case it must be a path to a CA bundle to use. Default to False. :param str token_path: The path of token file. """ if (connection_type is not None and connection_type.lower() == CONN_TYPE_SOCKET): connection_type = CONN_TYPE_SOCKET else: connection_type = CONN_TYPE_REST self.conn = self._get_connection(ip_addr, port, timeout, connection_type, ssl_enabled, verify, token_path) def _get_connection(self, ip_addr, port, timeout, connection_type, ssl_enabled, verify, token_path): if connection_type == CONN_TYPE_SOCKET: return socketConnection(ip_addr or '127.0.0.1', port or 2000, timeout) else: return restConnection(ip_addr or '127.0.0.1', port or 8080, ssl_enabled=ssl_enabled, verify=verify, token_path=token_path) def send_request(self, api_name, *api_args, **api_kwargs): """Refer to SDK API documentation. :param api_name: SDK API name :param *api_args: SDK API sequence parameters :param **api_kwargs: SDK API keyword parameters """ return self.conn.request(api_name, *api_args, **api_kwargs) zVMCloudConnector-1.4.1/smutLayer/0000775000175000017510000000000013442723341016506 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/smutLayer/__init__.py0000664000175000017510000000000013442676317020617 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/smutLayer/smutTest.py0000664000175000017510000016663213442676317020740 0ustar ruirui00000000000000#!/usr/bin/env python # Test logic for Systems Management Ultra Thin Layer # # 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. import argparse import datetime import os import re from six import string_types import sys import subprocess from subprocess import CalledProcessError from tempfile import NamedTemporaryFile from smutLayer.smut import SMUT from smutLayer.ReqHandle import ReqHandle version = '1.0.0' # Version of this script longstring = '1' * 4096 """ The following dictionary contains keys and values used as substitution in the requests that are processed. Replaceable values are identified in the requests by '<<<' and '>>>'. The key within the '<<<' and '>>>' is in the subs dictionary. """ subs = { '<<>>': 'someid', # An existing userid that can be # started and stopped '<<>>': 'STUS', # A prefix for a userid that gets # created and destroyed. Tests # add to the prefix to get an id. '<<>>': 'g[][325$$$', # A userid that makes SMAPI cry # and beg for a swift death '<<>>': '', # An existing userid that can be # migrated or empty to bypass tests. '<<>>': '', # An existing userid that cannot be # migrated or empty to bypass tests. '<<>>': 'zvmhost', # A z/VM host for migration into it '<<>>': 'password', # password '<<>>': '2G', # Virtual machine size '<<>>': 'POOL1', # 3390 disk pool (keep this in # uppercase for smutTest ease of use) '<<>>': '1100', # Size of a 3390 for system deploys '<<>>': 'POOL4', # 9336 disk pool (keep this in # uppercase for smutTest ease of use) '<<>>': '/opt/xcat/share/xcat/scripts/setupDisk', # SetupDisk '<<>>': '/install/zvm/POC/testImages/cfgdrive.tgz', # Simple tar file for the config drive '<<>>': '/install/zvm/POC/testImages/' + 'rhel67eckd_small_1100cyl.img', # Small image file '<<>>': '/opt/zthin/bin/unpackdiskimage', # Location of unpackdiskimage '<<>>': longstring, '<<>>': '0', # Wait time for makeVM to fully # complete } # Add a substitution key for the userid of this system. cmd = ["sudo", "/sbin/vmcp", "query userid"] try: subs['<<>>'] = subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) subs['<<>>'] = bytes.decode(subs['<<>>']) subs['<<>>'].split()[0] except Exception: print("Could not find the userid of this system.") subs['<<>>'] = 'unknownUserid' # Add a substitution key for the name of the aemod script that # set the /etc/iucv_authorized_userid file to use our userid # and create the script. modFile = NamedTemporaryFile(delete=False) subs['<<>>'] = modFile.name file = open(modFile.name, 'w') file.write("#!/usr/bin/env bash\n") file.write("echo -n $1 > /etc/iucv_authorized_userid\n") file.close() """ A dictionary contains the elements needed to process a test. This includes the following keys: description - Discriptive information to show when running the test. request - Request to be passed to SMUT. out - Input to grep to validate the output from a test. Normally, this is a reqular expression. The regular expression is input to grep which scans and validates the output. If output is an empty string then the test is assumed to have passed the output check. overallRC - A single return code or a list of return codes to compare against the overallRC property in the results. If the test returns an overallRC value that matches one of the specified values then it has passed the overallRC check. rc - A single return code or a list of return codes. If the test returns a return code that matches one of the specified return codes then it has passed the return code check. rs - A single return code or a list of return codes. If the test returns a return code that matches one of the specified return codes then it has passed the return code check. Note: A test must pass all specified tests (e.g. output, rc, etc.) in order for the test to pass. """ deployTests = [ { 'description': "Create a simple system: <<>>1", 'request': "MakeVM <<>>1 directory <<>> " + "<<>> g --ipl 100 --profile OSDFLT", 'out': "", 'overallRC': [0], }, { 'description': "Purge the reader", 'request': "ChangeVM <<>>1 purgerdr", 'out': "", 'overallRC': [0], }, { 'description': "Add a 3390 disk to <<>>1 as 100", 'request': "ChangeVM <<>>1 add3390 <<>> 100 " + "<<>>", 'out': "", 'overallRC': [0], }, { 'description': "Check out the user entry", 'request': "GetVM <<>>1 directory", 'out': "", 'overallRC': [0], }, { 'description': "Unpack the image into the disk.", 'request': "SHELL_TEST <<>> <<>>1 100 " + "<<>>", 'out': "", 'overallRC': [0], }, { 'description': "Punch the config drive tar file to the system.", 'request': "ChangeVM <<>>1 punchfile " + "<<>> --class x", 'out': "", 'overallRC': [0], }, { 'description': "Send an aemod to allow IUCV access by this system.", 'request': "ChangeVM <<>>1 aemod <<>> " + "--invparms <<>>", 'out': "", 'overallRC': [0], }, { 'description': "Power on the system and wait for to OS to come up.", 'request': "PowerVM <<>>1 on --wait --state up", 'out': "", 'overallRC': [0], }, { 'description': "Send a commmand to a system.", 'request': "CmdVM <<>>1 cmd pwd", 'out': "", 'overallRC': [0], }, { 'description': "Delete a system: <<>>1", 'request': "DeleteVM <<>>1 directory", 'out': "", 'overallRC': [0], }, ] generalTests = [ { 'description': "Test Help Function", 'request': "help", 'overallRC': [0], }, { 'description': "Test no operands => error", 'request': "", # Request with no parms 'overallRC': [4], 'rc': [4], 'rs': [9], }, { 'description': "Test Version", 'request': "version", 'out': "^Version:", 'overallRC': [0], }, { 'description': "Test unrecognized operands", 'request': "Steve is great", 'overallRC': [4], 'rc': [4], 'rs': [7], }, ] guestTests = [ { 'description': "Power on a system: <<>>", 'request': "PowerVM <<>> on", 'out': "", 'overallRC': [0], }, { 'description': "Get the status of the system: <<>>", 'request': "getvm <<>> status --all", 'out': "CPU Used Time:", 'overallRC': [0], }, { 'description': "Get the power status of the system: <<>>", 'request': "getvm <<>> status --power", 'out': "Power state: on", 'overallRC': [0], }, { 'description': "Get the memory status of the system: <<>>", 'request': "getvm <<>> status --memory", 'out': "Total Memory:", 'overallRC': [0], }, { 'description': "Get the cpu status of the system: <<>>", 'request': "getvm <<>> status --cpu", 'out': "Processors:", 'overallRC': [0], }, { 'description': "Power off the system: <<>>", 'request': "PowerVM <<>> off", 'out': "", 'overallRC': [0], }, { 'description': "Get the status of the system: <<>>", 'request': "getvm <<>> status", 'out': "CPU Used Time: 0 sec", 'overallRC': [0], }, { 'description': "Get the power status of the system: <<>>", 'request': "getvm <<>> status --power", 'out': "Power state: off", 'overallRC': [0], }, { 'description': "Get the memory status of the system: <<>>", 'request': "getvm <<>> status --memory", 'out': "Total Memory: 0M", 'overallRC': [0], }, { 'description': "Get the cpu status of the system: <<>>", 'request': "getvm <<>> status --cpu", 'out': "Processors: 0", 'overallRC': [0], }, ] hostTests = [ { 'description': "Get the list of disk pools.", 'request': "GetHost diskpoolnames", 'overallRC': [0], }, { 'description': "Get the space for all disk pools.", 'request': "GetHost diskpoolspace", 'out': "Total", 'overallRC': [0], }, { 'description': "Get the space for a specific 3390 disk pool: " + "<<>>", 'request': "GetHost diskpoolspace <<>>", 'out': "^<<>> Total", 'overallRC': [0], }, { 'description': "Get the space for a specific 9336 disk pool: " + "<<>>", 'request': "GetHost diskpoolspace <<>>", 'out': "^<<>> Total", 'overallRC': [0], }, { 'description': "Get the FCP Device information.", 'request': "GetHost fcpdevices", 'out': "^FCP device number", 'overallRC': [0], }, { 'description': "Get the general information.", 'request': "GetHost general", 'out': "", 'overallRC': [0], }, ] iucvTests = [ { 'description': "Power on a system: <<>>", 'request': "PowerVM <<>> on --wait --state up", 'out': "", 'overallRC': [0], }, { 'description': "Send a commmand to a system.", 'request': "CmdVM <<>> cmd pwd", 'out': "", 'overallRC': [0], }, { 'description': "Send an failing commmand to a system.", 'request': "CmdVM <<>> cmd \"echo 'bob'|grep /john/\"", 'out': "", 'overallRC': [2], 'rc': [8], 'rs': [1], }, { 'description': "Send an unknown commmand to a system.", 'request': "CmdVM <<>> cmd SteveIsGreat", 'out': "", 'overallRC': [2], 'rc': [8], 'rs': [127], }, ] lifecycleTests = [ { 'description': "Create a simple system: <<>>2", 'request': "makevm <<>>2 directory smapi 2g g", 'out': "", 'overallRC': [0], }, { 'description': "Verify system exists: <<>>2", 'request': "smapi <<>>2 api Image_Query_DM", 'out': "", 'overallRC': [0], }, { 'description': "Delete a system: <<>>2", 'request': "deletevm <<>>2 directory", 'out': "", 'overallRC': [0], }, # We used to verify that system no longer exists but dirmaint was slower # and test case sometimes fails. ] migrateTests = [ { 'description': "Get status for a specific userid that " + "cannot be migrated: <<>>", 'doIf': "'<<>>' != ''", 'request': "migrateVM <<>> status", 'overallRC': [99], 'rc': [99], 'rs': [419], }, { 'description': "Get all migration status for a host with " + "no active migrations.", 'doIf': "'<<>>' != ''", 'request': "migrateVM <<>> status --all", 'overallRC': [99], 'rc': [99], 'rs': [419], }, { 'description': ("Get incoming migration status for a host " + "with no active migrations."), 'doIf': "'<<>>' != ''", 'request': "migrateVM <<>> status --incoming", 'overallRC': [99], 'rc': [99], 'rs': [419], }, { 'description': "Get outgoing migration status for a host " + "with no active migrations.", 'doIf': "'<<>>' != ''", 'request': "migrateVM <<>> status --outgoing", 'overallRC': [99], 'rc': [99], 'rs': [419], }, { 'description': "Test a system for migration: <<>>", 'doIf': "'<<>>' != ''", 'request': "migrateVM <<>> test --destination " + "<<>>", 'overallRC': [99], 'rc': [99], 'rs': [418], }, { 'description': "Cancel a migration", 'doIf': "'<<>>' != ''", 'request': "migrateVM <<>> cancel", 'overallRC': [99], 'rc': [99], 'rs': [419], }, ] modifyTests = [ # >>>>>>>>> Create a simple system for logged off tests. { 'description': "Create a simple system: <<>>3", 'request': "MakeVM <<>>3 directory <<>> " + "<<>> g --ipl 100 --profile OSDFLT", 'out': "", 'overallRC': [0], }, { 'description': "Verify no console log is available: <<>>3", 'request': "getvm <<>>3 consoleoutput", 'out': "", 'overallRC': [8], 'rc': [8], 'rs': [8] }, { 'description': "Wait <<>> seconds for source " + "directory to be updated.", 'request': "SHELL echo 'Sleeping for <<>> seconds " + "to allow source directory update to complete';sleep " + "<<>>", 'out': "", 'overallRC': [0], }, { 'description': "Add modifications to the activation engine", 'request': 'ChangeVM <<>>3 aemod <<>> ' + '--invparms "action=addMdisk vaddr=101 filesys=ext4 ' + 'mntdir=/mnt/ephemeral/0.0.0101"', 'out': "", 'overallRC': [0], }, { 'description': "Add unknown script mods to the activation engine", 'request': 'ChangeVM <<>>3 aemod BAD ' + '--invparms "action=addMdisk vaddr=101 filesys=ext4 ' + 'mntdir=/mnt/ephemeral/0.0.0101"', 'out': "", 'overallRC': [4], 'rc': [4], 'rs': [400], }, { 'description': "Add modifications to activation engine for bad id", 'request': 'ChangeVM BADID aemod <<>> ' + '--invparms "action=addMdisk vaddr=101 filesys=ext4 ' + 'mntdir=/mnt/ephemeral/0.0.0101"', 'out': "", 'overallRC': [4], }, { 'description': "Purge the reader: <<>>3", 'request': "ChangeVM <<>>3 purgerdr", 'out': "", 'overallRC': [0], }, { 'description': "Add a 3390 disk to the system with ext4: " + "<<>>3", 'request': "changevm <<>>3 add3390 <<>> " + "101 100m --mode w --filesystem ext4 " + "--readpw readpw --writepw writepw --multipw multipw", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 3390 disk with ext4: <<>>3", 'request': "changevm <<>>3 removedisk 101", 'out': "", 'overallRC': [0], }, { 'description': "Add a 3390 disk to the system with xfs: " + "<<>>3", 'request': "changevm <<>>3 add3390 <<>> " + "102 100m --mode w --filesystem xfs", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 3390 disk with xfs: <<>>3", 'request': "changevm <<>>3 removedisk 102", 'out': "", 'overallRC': [0], }, { 'description': "Add a 3390 disk to the system with swap: " + "<<>>3", 'request': "changevm <<>>3 add3390 <<>> " + "103 100m --mode w --filesystem swap", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 3390 disk with swap: <<>>3", 'request': "changevm <<>>3 removedisk 103", 'out': "", 'overallRC': [0], }, { 'description': "Remove a disk that does not exist: <<>>3", 'request': "changevm <<>>3 removedisk 104", 'out': "", 'overallRC': [0], }, { 'description': "Add a 9336 disk to the system with ext4.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 add9336 <<>> " + "120 100m --mode w --filesystem ext4 " + "--readpw readpw --writepw writepw --multipw multipw", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 9336 disk with ext4.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 removedisk 120", 'out': "", 'overallRC': [0], }, { 'description': "Add a 9336 disk to the system with xfs.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 add9336 <<>> " + "121 100m --mode w --filesystem xfs", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 9336 disk with xfs.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 removedisk 121", 'out': "", 'overallRC': [0], }, { 'description': "Add a 9336 disk to the system with swap.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 add9336 <<>> " + "122 100m --mode w --filesystem swap", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 9336 disk with swap.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 removedisk 122", 'out': "", 'overallRC': [0], }, # >>>>>>>>> Deploy an image for active system tests. { 'description': "Add a 3390 disk for the root disk: <<>>3", 'request': "ChangeVM <<>>3 add3390 <<>> 100 " + "<<>>", 'out': "", 'overallRC': [0], }, { 'description': "Unpack the image into the disk: <<>>3", 'request': "SHELL_TEST <<>> <<>>3 100 " + "<<>>", 'out': "", 'overallRC': [0], }, { 'description': "Punch the config drive tar file to the system: " + "<<>>3", 'request': "ChangeVM <<>>3 punchfile " + "<<>> --class x", 'out': "", 'overallRC': [0], }, { 'description': "Send an aemod to allow IUCV access by this system.", 'request': "ChangeVM <<>>3 aemod <<>> " + "--invparms <<>>", 'out': "", 'overallRC': [0], }, { 'description': "Power on the system and wait for to OS to " + "come up: <<>>3", 'request': "PowerVM <<>>3 on --wait --state up", 'out': "", 'overallRC': [0], }, # >>>>>>>>> Tests that are related to active systems. { 'description': "Start console spooling on the system: " + "<<>>3", 'request': "CmdVM <<>>3 cmd 'vmcp spool console " + "to <<>>3 start'", 'overallRC': [0], }, { 'description': "Enable tracing so we put stuff to the " + "console of <<>>3", 'request': "CmdVM <<>>3 cmd 'vmcp trace diag run'", 'overallRC': [0], }, { 'description': "Force more to the console of " + "<<>>3", 'request': "CmdVM <<>>3 cmd 'vmcp query userid'", 'overallRC': [0], }, { 'description': "Get the console log of the system: <<>>3", 'request': "getvm <<>>3 consoleoutput", 'out': "List of spool files containing console logs " + "from <<>>3:", 'overallRC': [0], }, { 'description': "Add a 3390 disk to the system with ext4: " + "<<>>3", 'request': "changevm <<>>3 add3390 <<>> " + "110 100m --mode w --filesystem ext4", 'out': "", 'overallRC': [0], }, { 'description': "Online the 110 ECKD disk with ext4: " + "<<>>3", 'request': "CmdVM <<>>3 cmd '/sbin/cio_ignore -r 110; " + "which udevadm &> /dev/null && udevadm settle || udevsettle ;" + "/sbin/chccwdev -e 110 2>&1'", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 3390 disk with ext4: <<>>3 110", 'request': "changevm <<>>3 removedisk 110", 'out': "", 'overallRC': [0], }, { 'description': "Add a 3390 disk to the system with xfs: " + "<<>>3", 'request': "changevm <<>>3 add3390 <<>> " + "111 100m --mode w --filesystem xfs", 'out': "", 'overallRC': [0], }, # Don't online the disk. This makes the chccwdev fail but the # failure should be ignored. { 'description': "Remove the 3390 disk with xfs: " + "<<>>3 111", 'request': "changevm <<>>3 removedisk 111", 'out': "", 'overallRC': [0], }, { 'description': "Add a 3390 disk to the system with swap: " + "<<>>3 112", 'request': "changevm <<>>3 add3390 <<>> " + "112 100m --mode w --filesystem swap", 'out': "", 'overallRC': [0], }, { 'description': "Online the 112 ECKD disk with swap: " + "<<>>3", 'request': "CmdVM <<>>3 cmd '/sbin/cio_ignore -r 112; " + "which udevadm &> /dev/null && udevadm settle || udevsettle ;" + "/sbin/chccwdev -e 112 2>&1'", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 3390 disk with swap: " + "<<>>3 112", 'request': "changevm <<>>3 removedisk 112", 'out': "", 'overallRC': [0], }, { 'description': "Add a 9336 disk to an active system with ext4.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 add9336 <<>> " + "130 100m --mode w --filesystem ext4 " + "--readpw readpw --writepw writepw --multipw multipw", 'out': "", 'overallRC': [0], }, { 'description': "Check out the user entry", 'request': "GetVM <<>>3 directory", 'out': "", 'overallRC': [0], }, { 'description': "Online the 130 FBA disk with swap: " + "<<>>3", 'request': "CmdVM <<>>3 cmd '/sbin/cio_ignore -r 130; " + "which udevadm &> /dev/null && udevadm settle || udevsettle ;" + "/sbin/chccwdev -e 130 2>&1'", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 9336 disk with ext4.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 removedisk 130", 'out': "", 'overallRC': [0], }, { 'description': "Add a 9336 disk to an active system with xfs.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 add9336 <<>> " + "131 100m --mode w --filesystem xfs", 'out': "", 'overallRC': [0], }, { 'description': "Online the 131 FBA disk with swap: " + "<<>>3", 'request': "CmdVM <<>>3 cmd '/sbin/cio_ignore -r 131; " + "which udevadm &> /dev/null && udevadm settle || udevsettle ;" + "/sbin/chccwdev -e 131 2>&1'", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 9336 disk with xfs.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 removedisk 131", 'out': "", 'overallRC': [0], }, { 'description': "Add a 9336 disk to an active system with swap.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 add9336 <<>> " + "132 100m --mode w --filesystem swap", 'out': "", 'overallRC': [0], }, { 'description': "Online the 132 FBA disk with swap: " + "<<>>3", 'request': "CmdVM <<>>3 cmd '/sbin/cio_ignore -r 132; " + "which udevadm &> /dev/null && udevadm settle || udevsettle ;" + "/sbin/chccwdev -e 132 2>&1'", 'out': "", 'overallRC': [0], }, { 'description': "Remove the 9336 disk with swap.", 'doIf': "'<<>>' != ''", 'request': "changevm <<>>3 removedisk 132", 'out': "", 'overallRC': [0], }, { 'description': "Add/change an IPL statement", 'request': "changevm <<>>3 ipl 100", 'out': "", 'overallRC': [0], }, { 'description': "Add/change an IPL statement with loadparms", 'request': "changevm <<>>3 ipl 100 --loadparms cl", 'out': "", 'overallRC': [0], }, { 'description': "Add/change an IPL statement with loadparms", 'request': "changevm <<>>3 ipl 100 --loadparms lots", 'out': "", 'overallRC': [0], }, { 'description': "Add/change an IPL statement with parms", 'request': "changevm <<>>3 ipl cms --parms autocr", 'out': "", 'overallRC': [0], }, { 'description': "Verify IPL statement exists.", 'request': "smapi <<>>3 api Image_Query_DM", 'out': "IPL CMS PARM AUTOCR", 'overallRC': [0], }, { 'description': "Remove an IPL statement", 'request': "changevm <<>>3 removeipl", 'out': "", 'overallRC': [0], }, { 'description': "Add some loaddev statements", 'request': "changevm <<>>3 loaddev --boot 0 " + "--addr 123411 --lun 12345678 --wwpn " + "5005076800aa0001 --scpDataType hex " "--scpData 1212", 'out': "", 'overallRC': [0], }, { 'description': "No datatype loaddev statements", 'request': "changevm <<>>3 loaddev --boot 0 " + "--addr 123411 --lun 12345678 --wwpn " + "5005076800aa0001 --scpData 1212", 'out': "", 'overallRC': [4], 'rc': [4], 'rs': [14], }, { 'description': "No data loaddev statements", 'request': "changevm <<>>3 loaddev --boot 0 " + "--addr 123411 --lun 12345678 --wwpn " + "5005076800aa0001 --scpDataType hex", 'out': "", 'overallRC': [4], 'rc': [4], 'rs': [14], }, { 'description': "Bad datatype loaddev statements", 'request': "changevm <<>>3 loaddev --boot 0 " + "--addr 123411 --lun 12345678 --wwpn " + "5005076800aa0001 --scpDataType BAD --scpData 1212", 'out': "", 'overallRC': [4], 'rc': [4], 'rs': [16], }, { 'description': "Really long scp data", 'request': "changevm <<>>3 loaddev --boot 0 " + "--addr 123411 --lun 12345678 --wwpn " + "5005076800aa0001 --scpDataType hex " + "--scpData <<>>", 'out': "", 'overallRC': [0], }, { 'description': "No boot parm (keep old boot)", 'request': "changevm <<>>3 loaddev --addr 123411 " + "--lun 12345678 --wwpn 5005076800aa0001 " + "--scpDataType hex --scpData 1212", 'out': "", 'overallRC': [0], }, { 'description': "No addr parm (keep old block address)", 'request': "changevm <<>>3 loaddev --lun " + "12345678 --wwpn 5005076800aa0001 " + "--scpDataType hex --scpData 1212", 'out': "", 'overallRC': [0], }, { 'description': "No lun parm (keep old lun)", 'request': "changevm <<>>3 loaddev --wwpn " + "5005076800aa0001 --scpDataType hex --scpData 1212", 'out': "", 'overallRC': [0], }, { 'description': "No wwpn parm (keep old wwpn)", 'request': "changevm <<>>3 loaddev --scpDataType " + "hex --scpData 1212", 'out': "", 'overallRC': [0], }, { 'description': "No parms (keep old parms)", 'request': "changevm <<>>3 loaddev", 'out': "", 'overallRC': [0], }, { 'description': "Verify loaddev boot statements exist", 'request': "smapi <<>>3 api Image_Query_DM", 'out': "LOADDEV BOOTPROG 0", 'overallRC': [0], }, { 'description': "Verify loaddev addr statements exist", 'request': "smapi <<>>3 api Image_Query_DM", 'out': "LOADDEV BR_LBA 0000000000123411", 'overallRC': [0], }, { 'description': "Verify loaddev lun statements exist", 'request': "smapi <<>>3 api Image_Query_DM", 'out': "LOADDEV LUN 0000000012345678", 'overallRC': [0], }, { 'description': "Verify loaddev wwpn statements exist.", 'request': "smapi <<>>3 api Image_Query_DM", 'out': "LOADDEV PORTNAME 5005076800AA0001", 'overallRC': [0], }, { 'description': "Verify loaddev wwpn statements exist", 'request': "smapi <<>>3 api Image_Query_DM", 'out': "LOADDEV SCPDATA HEX", 'overallRC': [0], }, { 'description': "Delete statements", 'request': "changevm <<>>3 loaddev --boot DELETE " + "--addr DELETE --lun DELETE --wwpn DELETE " + "--scpDataType DELETE", 'out': "", 'overallRC': [0], }, { 'description': "Verify loaddev statements are gone", 'request': "SMAPI <<>>3 API " + "Image_SCSI_Characteristics_Query_DM", 'out': "", 'overallRC': [8], 'rc': [0], 'rs': [28], }, { 'description': "Successfully purge the reader: <<>>3", 'request': "changeVM <<>>3 purgeRDR ", 'overallRC': [0], }, { 'description': "Try to purge read of a bad id: <<>>", 'request': "changeVM <<>> purgeRDR ", 'out': "Syntax error in function parameter 8", 'overallRC': [8], 'rc': [24], 'rs': [813] }, { 'description': "Punch the config drive tar file to the system.", 'request': "ChangeVM <<>>3 punchfile <<>>", 'out': "", 'overallRC': [0], }, { 'description': "Punch the config drive tar file to the system" + " with valid spool class.", 'request': "ChangeVM <<>>3 punchfile <<>>" + " --class b", 'out': "", 'overallRC': [0], }, { 'description': "Punch the config drive tar file to the system" + " with an invalid userid and file.", 'request': "ChangeVM <<>> punchfile invalid.config", 'out': "", 'overallRC': [4], 'rc': [7], 'rs': [401], }, { 'description': "Punch the config drive tar file to the system" + " with an invalid userid and spool class.", 'request': "ChangeVM <<>>3 punchfile invalid.config" + " --class b*", 'out': "", 'overallRC': [4], 'rc': [7], 'rs': [401], }, { 'description': "Punch the config drive tar file to the system" + " with an invalid userid.", 'request': "ChangeVM <<>> punchfile <<>>" + " --class b", 'out': "", 'overallRC': [4], 'rc': [4], 'rs': [424], }, { 'description': "Punch the config drive tar file to the system" + " with an invalid class.", 'request': "ChangeVM <<>>3 punchfile <<>>" + " --class b*", 'out': "", 'overallRC': [4], 'rc': [8], 'rs': [404], }, { 'description': "Punch the config drive tar file to the system" + " with an invalid file.", 'request': "ChangeVM <<>>3 punchfile invalid.config", 'out': "", 'overallRC': [4], 'rc': [7], 'rs': [401], }, # >>>>>>>>> Clean up by destroying the system. { 'description': "Delete the system: <<>>3", 'request': "deletevm <<>>3 directory", 'out': "", 'overallRC': [0], }, { 'description': "Clean up an reader files for <<>>3.", 'request': "CODE_SEG purgeRdr('<<>>3')", 'overallRC': [0], }, ] powerTests = [ { 'description': "Test PowerVM VERSION.", 'request': "PowerVM version", 'out': "^Version:", 'overallRC': [0], }, { 'description': "'PowerVM xxx JUNK' fails", 'request': "PowerVM xxx junk", 'out': "", 'overallRC': [4], }, { 'description': "Power off a system: <<>>", 'request': "PowerVM <<>> off --wait", 'out': "", 'overallRC': [0], }, { 'description': "Check status of powered off system.", 'request': "PowerVM <<>> status", 'out': "<<>>: off", 'overallRC': [0], 'rc': [0], 'rs': [1] }, { 'description': "Check isreachable of powered off system.", 'request': "PowerVM <<>> isreachable", 'out': "<<>>: unreachable", 'overallRC': [0], 'rs': [0] }, { 'description': "Power off an already powered off system.", 'request': "PowerVM <<>> off", 'out': "", 'overallRC': [0], }, { 'description': "Power on a system: <<>>", 'request': "PowerVM <<>> on", 'out': "", 'overallRC': [0], }, { 'description': "Power off a system with softOff option: " + "<<>>", 'request': "PowerVM <<>> softoff", 'out': "", 'overallRC': [0], }, { 'description': "Power on a system: <<>>", 'request': "PowerVM <<>> on", 'out': "", 'overallRC': [0], }, { 'description': "Power on a system that is on but not up: " + "<<>>", 'request': "PowerVM <<>> on --wait --state up", 'out': "<<>>: up", 'overallRC': [0], }, { 'description': "Check status of powered on system: <<>>", 'request': "PowerVM <<>> status", 'out': "<<>>: on", 'overallRC': [0], 'rc': [0], 'rs': [0] }, { 'description': "Check isreachable of powered on system: " + "<<>>", 'request': "PowerVM <<>> isreachable", 'out': "<<>>: reachable", 'overallRC': [0], 'rs': [1] }, { 'description': "Pause a system: <<>>", 'request': "PowerVM <<>> pause", 'out': "", 'overallRC': [0], }, { 'description': "Isreachable of a paused system is unreachable: " + "<<>>", 'request': "PowerVM <<>> isreachable", 'out': "<<>>: unreachable", 'overallRC': [0], 'rs': [0] }, { 'description': "Unpause a system: <<>>", 'request': "PowerVM <<>> unpause", 'out': "", 'overallRC': [0], }, { 'description': "Isreachable of an unpaused system is reachable: " + "<<>>", 'request': "PowerVM <<>> isreachable", 'out': "<<>>: reachable", 'overallRC': [0], 'rs': [1] }, { 'description': "Reset a system: <<>>", 'request': "PowerVM <<>> reset --wait --state up", 'out': "", 'overallRC': [0], }, { 'description': "Isreachable of an unpaused system is reachable: " + "<<>>", 'request': "PowerVM <<>> isreachable", 'out': "<<>>: reachable", 'overallRC': [0], 'rs': [1] }, { 'description': "Reboot a system: <<>>", 'request': "PowerVM <<>> reboot --wait", 'out': "", 'overallRC': [0], }, { 'description': "Reboot a system w/o waiting for the OS to be up: " + "<<>>", 'request': "PowerVM <<>> reboot", 'out': "", 'overallRC': [0], }, { 'description': "Wait for the OS to come up: <<>>", 'request': "PowerVM <<>> wait --state up", 'out': "<<>>: up", 'overallRC': [0], 'rs': [0] }, ] smapiTests = [ { 'description': "Directory related query w/o operands.", 'request': "smapi <<>> api Image_Query_DM", 'out': "", 'overallRC': [0], }, { 'description': "Disk pool query with operands.", 'request': "smapi <<>> api Image_Volume_Space_Query_DM " + "--operands '-q' 1 '-e' 1", 'out': "", 'overallRC': [0], }, { 'description': "Failing disk pool query with operands.", 'request': "smapi <<>> api Image_Volume_Space_Query_DM " + "--operands '-q' 4 '-e' 1", 'out': "", 'overallRC': [8], 'rc': [24], 'rs': [1018], }, ] testSets = { 'DEPLOY': { 'description': 'ECKD deploy image tests', 'doIf': "'<<>>' != ''", 'tests': deployTests}, 'GENERAL': { 'description': 'Tests that are not specific to a ' + 'particular function.', 'tests': generalTests}, 'GUEST': { 'description': 'Guest tests that are not covered under ' + 'other functions.', 'tests': guestTests}, 'HOST': { 'description': 'Host related tests', 'tests': hostTests}, 'IUCV': { 'description': 'Send commands to VM over IUCV', 'tests': iucvTests}, 'LIFECYCLE': { 'description': 'VM Life Cycle related tests', 'tests': lifecycleTests}, 'MIGRATE': { 'description': 'VM Migration related tests', 'tests': migrateTests}, 'MODIFY': { 'description': 'Modify a VM', 'tests': modifyTests}, 'POWER': { 'description': 'VM Power function tests', 'tests': powerTests}, 'SMAPI': { 'description': 'SMAP API invocation tests', 'tests': smapiTests}, } def localize(localFile, subs, testSets): """ Perform localization of substitution variables and test sets. This allows the invoker to extend or modify defined tests without modifying this file. Input: Name of local tailorization file (without .py) e.g. smutTestLocal for smutTestLocal.py file. Substitution dictionary to be updated. Test set dictionary to be updated. Output: None Note: - Upon exit the substitution and test set dictionary have been updated with the data from the localization file. """ try: smutTestLocal = __import__(localFile, fromlist=["*"]) except Exception as e: print(e) return 1 # Apply local overrides to the subs dictionary. if len(smutTestLocal.localSubs) > 0: print("Localizing localSubs dictionary.") for key in smutTestLocal.localSubs: print("Localizing " + key + ": " + smutTestLocal.localSubs[key]) subs[key] = smutTestLocal.localSubs[key] else: print("No local overrides exist for the subs dictionary.") # Apply local overrides to the testSets dictionary. if len(smutTestLocal.localTestSets) > 0: print("Localizing the test sets.") if 'clear:testSets' in smutTestLocal.localTestSets: print("Removing all original test sets.") testSets.clear() for key in smutTestLocal.localTestSets: if key == 'clear:testSets': continue print("Localizing test set: " + key) testSets[key] = smutTestLocal.localTestSets[key] else: print("No local test sets exist.") return 0 def purgeRdr(userid): """ Purge contents in this system's reader from a userid. Input: userid that originated the files we want to purge. Output: Return code - 0: no problems, 1: problem encountered. """ subRC = 0 userid = userid.upper() spoolList = [] queryCmd = ("sudo /sbin/vmcp query rdr userid '*' | " + "grep ^" + userid + " | awk '{print $2}'") try: qryRes = subprocess.check_output( queryCmd, close_fds=True, shell=True) qryRes = bytes.decode(qryRes) spoolList = qryRes.splitlines() except Exception as e: # All exceptions. print("Unable to purge reader files for in this " + "system's reader originally owned by: " + userid + ", exception: " + str(e)) subRC = 1 purgeCmd = ['sudo', '/sbin/vmcp', 'purge', 'reader', '0'] for purgeCmd[3] in spoolList: try: subprocess.check_output( purgeCmd, close_fds=True) except Exception as e: # All exceptions. print("Unable to purge reader file " + purgeCmd[3] + ", exception: " + str(e)) subRC = 1 return subRC def runTest(smut, test): """ Drive a test and validate the results. Input: SMUT daemon object Dictionary element for the test to drive. Output: Final test score - 0: failed, 1: passed, """ global args if test['request'][0:10] != 'SHELL_TEST': reqHandle = ReqHandle(cmdName=sys.argv[0], captureLogs=True) results = reqHandle.parseCmdline(test['request']) if results['overallRC'] == 0: results = reqHandle.driveFunction() else: # Issue a function that is not considered a test. results = { 'overallRC': 0, 'rc': 0, 'rs': 0, 'errno': 0, 'strError': '', 'response': [], 'logEntries': [], } shellCmd = test['request'][11:] try: results['response'] = subprocess.check_output( shellCmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) results['response'] = bytes.decode(results['response']) except CalledProcessError as e: results['response'] = e.output results['overallRC'] = e.returncode except Exception as e: # All other exceptions. if 'output' in e: results['response'] = e.output else: results['response'] = ('Exception encountered: %s, ' + "details: %s" % (type(e).__name__, str(e))) if 'returncode' in e: results['overallRC'] = e.returncode else: results['overallRC'] = -9999999 if isinstance(results['response'], string_types): results['response'] = [results['response']] print(" Overall rc: %s" % results['overallRC']) print(" rc: %s" % results['rc']) print(" rs: %s" % results['rs']) if len(results['response']) > 0: print(" Response:") for line in results['response']: print(" " + line) else: print(" Response: None returned") # Validate the response strings respScore = 1 # Assume the response tests passed. if 'out' in test.keys() and len(test['out']) > 0: # Expect a response let's test it. if len(results['response']) == 0: # No response returned when one was expected -> failed respScore = 0 else: # Test the response to see it matches an expected response # Put the response into a file. This avoids problems with # having special characters in the response that would # cause the shell to complain or get confused. tempFile = NamedTemporaryFile(delete=False) file = open(tempFile.name, 'w') for line in results['response']: file.write(line + '\n') file.close() cmd = ['grep', ''.join(test['out']), tempFile.name] try: junk = subprocess.check_output(cmd, close_fds=True) junk = bytes.decode(junk) if junk == '': respScore = 0 except Exception: respScore = 0 os.remove(tempFile.name) else: pass # No responses listed, treat as a match # Validate the Overall return code orcScore = 0 # Assume RC is not a desired one if 'overallRC' not in test.keys(): orcScore = 1 # No special value, assume it passed elif len(test['overallRC']) == 1: if test['overallRC'][0] == results['overallRC']: orcScore = 1 else: for wanted in test['overallRC']: if results['overallRC'] == wanted: orcScore = 1 break # Validate the failure return code rcScore = 0 # Assume RC is not a desired one if 'rc' not in test.keys(): rcScore = 1 # No special value, assume it passed elif len(test['rc']) == 1: if test['rc'][0] == results['rc']: rcScore = 1 else: for wanted in test['rc']: if results['rc'] == wanted: rcScore = 1 break # Validate the failure reason code rsScore = 0 # Assume RC is not a desired one if 'rs' not in test.keys(): rsScore = 1 # No special value, assume it passed elif len(test['rs']) == 1: if test['rs'][0] == results['rs']: rsScore = 1 else: for wanted in test['rs']: if results['rs'] == wanted: rsScore = 1 break # Determine the final score and show the success or failure of the test if respScore != 1 or orcScore != 1 or rcScore != 1 or rsScore != 1: testScore = 0 if len(results['logEntries']) != 0: print(" Log Entries:") for line in results['logEntries']: print(" " + line) print(" Test Status: FAILED") if respScore != 1: print(" Response Validation: FAILED") if orcScore != 1: print(" Overall RC Validation: FAILED") if rcScore != 1: print(" rc Validation: FAILED") if rsScore != 1: print(" rs Validation: FAILED") else: testScore = 1 if args.showLog is True and len(results['logEntries']) != 0: print(" Log Entries:") for line in results['logEntries']: print(" " + line) print(" Test Status: PASSED") return testScore def driveTestSet(smut, setId, setToTest): """ Drive a set of test. Input: SMUT daemon object Dictionary element for the test to drive. Global: Count of tests Count of passed tests Count of failed tests List of failed Tests Output: Global values changed """ global args global cnts print(" ") print("******************************************************************") print("******************************************************************") print("Beginning Test Set: " + setToTest['description']) print("******************************************************************") print("******************************************************************") localTotal = 0 localAttempted = 0 localPassed = 0 localFailed = 0 localBypassed = 0 failInfo = [] startTime = datetime.datetime.now() for test in setToTest['tests']: if args.listParms is True: # Only want to list the requests. print(test['request']) continue # Produce Common Test/shell count info. print("") localTotal += 1 cntInfo = "%i/%i" % (localTotal, (cnts['total'] + localTotal)) if 'doIf' in test and not eval(test['doIf']): print("Bypassing %s: %s" % (cntInfo, test['description'])) localBypassed += 1 continue if test['request'][0:6] == 'SHELL ': # Issue a function that is not considered a test. print("Shell %s: %s" % (cntInfo, test['description'])) shellCmd = test['request'][6:] shellRC = 0 try: out = subprocess.check_output( shellCmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) out = bytes.decode(out) out = "".join(out) except CalledProcessError as e: out = e.output shellRC = e.returncode except Exception as e: # All other exceptions. if 'output' in e: out = e.output else: out = ('Exception encountered: %s, ' + "details: %s" % (type(e).__name__, str(e))) if 'returncode' in e: shellRC = e.returncode else: shellRC = -9999999 if isinstance(out, string_types): out = [out] shellOk = 0 if 'overallRC' in test: for testRC in test['overallRC']: if shellRC == testRC: shellOk = 1 break if shellOk == 0: print("***Warning*** A non test related shell function " + "returned rc: " + str(shellRC) + " out: " + ''.join(out)) elif test['request'][0:9] == 'CODE_SEG ': print("Code Segment: %s: %s" % (cntInfo, test['description'])) codeSeg = test['request'][9:] exec(codeSeg) else: # Attempt the test. print("Test %s: %s" % (cntInfo, test['description'])) localAttempted += 1 testScore = runTest(smut, test) if testScore == 1: localPassed += 1 else: localFailed += 1 failInfo.append(cntInfo) endTime = datetime.datetime.now() cnts['total'] += localTotal cnts['attempted'] += localAttempted cnts['passed'] += localPassed cnts['failed'] += localFailed cnts['bypassed'] += localBypassed print(" ") print("Status of this set...") print(" Time:") print(" Started: " + str(startTime)) print(" Ended: " + str(endTime)) print(" Duration: " + str(endTime - startTime)) print(" Total Requests: %i, Bypassed Requests: %i" % (localTotal, localBypassed)) print(" Tests attempted: %i, passed: %i, failed: %i" % (localAttempted, localPassed, localFailed)) if localFailed > 0: cnts['failedTests'].append(setId + ": " + " ".join(failInfo)) """ ****************************************************************************** main routine ****************************************************************************** """ # Parse the input and assign it to the variables. parser = argparse.ArgumentParser() parser.add_argument('--listareas', dest='listAreas', action='store_true', help='List names of the test set areas.') parser.add_argument('--listparms', dest='listParms', action='store_true', help='List the command being run.') parser.add_argument('--local', default='smutTestLocal', dest='localFile', help="Localization file or 'none'.") parser.add_argument('--showlog', dest='showLog', action='store_true', help='Show log entries for successful tests.') parser.add_argument('setsToRun', metavar='N', nargs='*', help='Test sets to run') args = parser.parse_args() if args.localFile != 'none': # Perform the localization. print("Localization file specified as: " + args.localFile) print("Importing " + args.localFile) rc = localize(args.localFile, subs, testSets) if rc != 0: exit(2) else: print("No localization will be performed.") # The next lines produce the code that allows the regular expressions to work. regSubs = dict((re.escape(k), v) for k, v in subs.iteritems()) pattern = re.compile("|".join(regSubs.keys())) smut = SMUT() smut.enableLogCapture() # Capture request related logs cnts = {} cnts['total'] = 0 cnts['passed'] = 0 cnts['failed'] = 0 cnts['failedTests'] = [] cnts['attempted'] = 0 cnts['bypassed'] = 0 # Temporary Preparation for punchFile Test. Create a sample config file. f = open("sample.config", "w+") f.write("This is sample config file for punchFile Test") f.close() if args.listAreas is True: for key in sorted(testSets): print(key + ": " + testSets[key]['description']) else: # Initialize the environment. Online the punch. cmd = "sudo /sbin/cio_ignore -r d; sudo /sbin/chccwdev -e d" try: subprocess.check_output( cmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) except CalledProcessError as e: print("Warning: Failed to enable the punch, " + "cmd: %s, rc: %i, out: %s" % (cmd, e.returncode, e.output)) except Exception as e: # All other exceptions. if 'output' in e: out = e.output else: out = ('Exception encountered: %s, ' + "details: %s" % (type(e).__name__, str(e))) if 'returncode' in e: eRC = e.returncode else: eRC = -9999999 print("Warning: Failed to enable the punch, " + "cmd: %s, rc: %i, %s" % (cmd, eRC, out)) # Perform the substitution change to all requests and responses for key in testSets: if 'doIf' in testSets[key]: testSets[key]['doIf'] = pattern.sub(lambda m: regSubs[re.escape(m.group(0))], testSets[key]['doIf']) for test in testSets[key]['tests']: test['description'] = pattern.sub(lambda m: regSubs[re.escape(m.group(0))], test['description']) test['request'] = pattern.sub(lambda m: regSubs[re.escape(m.group(0))], test['request']) if 'doIf' in test: test['doIf'] = pattern.sub(lambda m: regSubs[re.escape(m.group(0))], test['doIf']) if 'out' in test: test['out'] = pattern.sub(lambda m: regSubs[re.escape(m.group(0))], test['out']) # Apply testSet['doIf'] to the tests, if it exists. if 'doIf' in testSets[key]: if 'doIf' in test: test['doIf'] = (testSets[key]['doIf'] + ' and ' + test['doIf']) else: test['doIf'] = testSets[key]['doIf'] # Determine the tests to run based on the first argument. tests = [] totalStartTime = datetime.datetime.now() if len(args.setsToRun) > 0: for key in args.setsToRun: key = key.upper() if key in testSets: driveTestSet(smut, key, testSets[key]) else: print("The following tests set was not recognized: " + key) else: for key in sorted(testSets): driveTestSet(smut, key, testSets[key]) totalEndTime = datetime.datetime.now() # Cleanup the work files. if (os.path.exists("sample.config")): os.remove("sample.config") if (os.path.exists(subs['<<>>'])): os.remove(subs['<<>>']) print("") print("******************************************************************") print("Status of this run...") print(" Time:") print(" Started: " + str(totalStartTime)) print(" Ended: " + str(totalEndTime)) print(" Duration: " + str(totalEndTime - totalStartTime)) print(" Total Requests: %i, Bypassed Requests: %i" % (cnts['total'], cnts['bypassed'])) print(" Tests attempted: %i, passed: %i, failed: %i" % (cnts['attempted'], cnts['passed'], cnts['failed'])) print(" Failed Test(s): " + str(cnts['failedTests'])) print("******************************************************************") if cnts['failed'] == 0: exit(0) else: exit(1) zVMCloudConnector-1.4.1/smutLayer/migrateVM.py0000664000175000017510000005061413442676317020773 0ustar ruirui00000000000000# MigrateVM functions for Systems Management Ultra Thin Layer # # 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. from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import invokeSMCLI modId = 'MIG' version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'CANCEL': ['cancel', lambda rh: cancelMigrate(rh)], 'HELP': ['help', lambda rh: help(rh)], 'MODIFY': ['modify', lambda rh: modifyMigrate(rh)], 'MOVE': ['move', lambda rh: moveVM(rh)], 'STATUS': ['status', lambda rh: getStatus(rh)], 'TEST': ['test', lambda rh: testMigrate(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = {} """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) For example, the 'WAITFOR' subfunction has two keyword operands 'poll' and 'maxwait', and each of them take one additional operand (time in seconds) which is an int. """ keyOpsList = { 'CANCEL': {'--showparms': ['showParms', 0, 0]}, 'HELP': {}, 'MODIFY': { '--maxquiesce': ['maxQuiesce', 1, 1], '--maxtotal': ['maxTotal', 1, 1], '--showparms': ['showParms', 0, 0]}, 'MOVE': { '--destination': ['dest', 1, 2], '--forcearch': ['forcearch', 0, 0], '--forcedomain': ['forcedomain', 0, 0], '--forcestorage': ['forcestorage', 0, 0], '--immediate': ['immediate', 0, 0], '--maxquiesce': ['maxQuiesce', 1, 1], '--maxtotal': ['maxTotal', 1, 1], '--showparms': ['showParms', 0, 0]}, 'STATUS': { '--all': ['all', 0, 0], '--incoming': ['incoming', 0, 0], '--outgoing': ['outgoing', 0, 0], '--showparms': ['showParms', 0, 0]}, 'TEST': { '--destination': ['dest', 1, 2], '--showparms': ['showParms', 0, 0]}, 'VERSION': {}, } def cancelMigrate(rh): """ Cancel an existing VMRelocate request. Input: Request Handle with the following properties: function - 'MIGRATEVM' subfunction - 'CANCEL' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.cancelMigrate") parms = ["-T", rh.userid, "-k", "action=CANCEL"] results = invokeSMCLI(rh, "VMRELOCATE", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['rc'] == 8 and results['rs'] == 3000: if "1926" in results['response']: # No relocation in progress msg = msgs.msg['0419'][1] % (modId, rh.userid) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0419'][0]) else: # More details in message codes lines = results['response'].split("\n") for line in lines: if "Details:" in line: codes = line.split(' ', 1)[1] msg = msgs.msg['420'][1] % (modId, "VMRELOCATE Cancel", rh.userid, codes) rh.printLn("ES", msg) rh.printSysLog("Exit migrateVM.cancelMigrate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: migrateVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit migrateVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getStatus(rh): """ Get status of a VMRelocate request. Input: Request Handle with the following properties: function - 'MIGRATEVM' subfunction - 'STATUS' userid - userid of the virtual machine parms['all'] - If present, set status_target to ALL. parms['incoming'] - If present, set status_target to INCOMING. parms['outgoing'] - If present, set status_target to OUTGOING. if parms does not contain 'all', 'incoming' or 'outgoing', the status_target is set to 'USER '. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.getStatus") parms = ["-T", rh.userid] if 'all' in rh.parms: parms.extend(["-k", "status_target=ALL"]) elif 'incoming' in rh.parms: parms.extend(["-k", "status_target=INCOMING"]) elif 'outgoing' in rh.parms: parms.extend(["-k", "status_target=OUTGOING"]) else: parms.extend(["-k", "status_target=USER " + rh.userid + ""]) results = invokeSMCLI(rh, "VMRELOCATE_Status", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['rc'] == 4 and results['rs'] == 3001: # No relocation in progress msg = msgs.msg['0419'][1] % (modId, rh.userid) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0419'][0]) else: rh.printLn("N", results['response']) rh.printSysLog("Exit migrateVM.getStatus, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for MigrateVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def modifyMigrate(rh): """ Modify an existing VMRelocate request. Input: Request Handle with the following properties: function - 'MIGRATEVM' subfunction - 'MODIFY' userid - userid of the virtual machine parms['maxQuiesce'] - maximum quiesce time in seconds, or -1 to indicate no limit. parms['maxTotal'] - maximum total time in seconds, or -1 to indicate no limit. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.modifyMigrate") parms = ["-T", rh.userid] if 'maxQuiesce' in rh.parms: if rh.parms['maxQuiesce'] == -1: parms.extend(["-k", "max_quiesce=NOLIMIT"]) else: parms.extend(["-k", "max_quiesce=" + str(rh.parms['maxQuiesce'])]) if 'maxTotal' in rh.parms: if rh.parms['maxTotal'] == -1: parms.extend(["-k", "max_total=NOLIMIT"]) else: parms.extend(["-k", "max_total=" + str(rh.parms['maxTotal'])]) results = invokeSMCLI(rh, "VMRELOCATE_Modify", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['rc'] == 8 and results['rs'] == 3010: if "1926" in results['response']: # No relocations in progress msg = msgs.msg['0419'][1] % (modId, rh.userid) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0419'][0]) else: # More details in message codes lines = results['response'].split("\n") for line in lines: if "Details:" in line: codes = line.split(' ', 1)[1] msg = msgs.msg['0420'][1] % (modId, "VMRELOCATE Modify", rh.userid, codes) rh.printLn("ES", msg) rh.printSysLog("Exit migrateVM.modifyMigrate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def moveVM(rh): """ Initiate a VMRelocate request to move a userid. Input: Request Handle with the following properties: function - 'MIGRATEVM' subfunction - 'MOVE' userid - userid of the virtual machine parms['destination'] - target SSI member parms['forcearch'] - if present, force=architecture is set. parms['forcedomain'] - if present, force=domain is set. parms['forcestorage'] - if present, force=storage is set. parms['immediate'] - if present, immediate=YES is set. parms['maxquiesce'] - maximum quiesce time in seconds, or -1 to indicate no limit. parms['maxTotal'] - maximum total time in seconds, or -1 to indicate no limit. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.moveVM") parms = ["-T", rh.userid, "-k", "action=MOVE"] if 'dest' in rh.parms: parms.extend(["-k", "destination=" + rh.parms['dest']]) forceOption = '' if 'forcearch' in rh.parms: forceOption = "ARCHITECTURE " if 'forcedomain' in rh.parms: forceOption = forceOption + "DOMAIN " if 'forcestorage' in rh.parms: forceOption = forceOption + "STORAGE " if forceOption != '': parms.extend(["-k", "\'force=" + forceOption + "\'"]) if 'immediate' in rh.parms: parms.extend(["-k", "\'immediate=YES"]) if 'maxQuiesce' in rh.parms: if rh.parms['maxQuiesce'] == -1: parms.extend(["-k", "max_quiesce=NOLIMIT"]) else: parms.extend(["-k", "max_quiesce=" + str(rh.parms['maxQuiesce'])]) if 'maxTotal' in rh.parms: if rh.parms['maxTotal'] == -1: parms.extend(["-k", "max_total=NOLIMIT"]) else: parms.extend(["-k", "max_total=" + str(rh.parms['maxTotal'])]) results = invokeSMCLI(rh, "VMRELOCATE", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['rc'] == 8 and results['rs'] == 3000: if "0045" in results['response']: # User not logged on msg = msgs.msg['0418'][1] % (modId, rh.userid) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0418'][0]) else: # More details in message codes lines = results['response'].split("\n") for line in lines: if "Details:" in line: codes = line.split(' ', 1)[1] msg = msgs.msg['0420'][1] % (modId, "VMRELOCATE Move", rh.userid, codes) rh.printLn("ES", msg) rh.printSysLog("Exit migrateVM.moveVM, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit migrateVM.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) rh.printSysLog("Exit migrateVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " MigrateVM cancel") rh.printLn("N", " python " + rh.cmdName + " MigrateVM help") rh.printLn("N", " python " + rh.cmdName + " MigrateVM modify [--maxtotal ]") rh.printLn("N", " [--maxquiesce ]") rh.printLn("N", " python " + rh.cmdName + " MigrateVM move --destination ") rh.printLn("N", " [--immediate] [--forcearch] " + "[--forcedomain] [--forcestorage]") rh.printLn("N", " [--maxtotal ] " + "[--maxquiesce ]") rh.printLn("N", " python " + rh.cmdName + " MigrateVM status " + "[--all | --incoming | --outgoing]") rh.printLn("N", " python " + rh.cmdName + " MigrateVM test --destination ") rh.printLn("N", " python " + rh.cmdName + " MigrateVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the MigrateVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " cancel - " + "cancels the relocation of the specified virtual machine.") rh.printLn("N", " help - Displays this help information.") rh.printLn("N", " modify - " + "modifies the time limits associated with the relocation already") rh.printLn("N", " in progress .") rh.printLn("N", " move - " + "moves the specified virtual machine, while it continues to run,") rh.printLn("N", " " + "to the specified system within the SSI cluster.") rh.printLn("N", " status - requests information about " + "relocations currently in progress.") rh.printLn("N", " test - tests the specified virtual machine " + "and reports whether or not") rh.printLn("N", " " + "it is eligible to be relocated to the specified system.") rh.printLn("N", " version - show the version of the power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " --all - All relocations") rh.printLn("N", " --destination - " + "Specifies the SSI name of the target destination") rh.printLn("N", " z/VM system ") rh.printLn("N", " --forcearch - " + "force relocations past architecture checks.") rh.printLn("N", " --forcedomain - " + "force relocations past domain checks.") rh.printLn("N", " --forcestorage - " + "force relocations past storage checks.") rh.printLn("N", " --immediate - " + "causes the VMRELOCATE command to do one early") rh.printLn("N", " " + "pass through virtual machine storage and then go") rh.printLn("N", " " + "directly to the quiesce stage.") rh.printLn("N", " --incoming - Incoming relocations") rh.printLn("N", " --maxquiesce - " + "indicates the maximum quiesce time (in seconds)") rh.printLn("N", " for this relocation.") rh.printLn("N", " --maxtotal - " + "indicates the maximum total time (in seconds)") rh.printLn("N", " " + "for relocation to complete.") rh.printLn("N", " --outgoing - Out-going relocations") rh.printLn("N", " - " + "Userid of the target virtual machine") return def testMigrate(rh): """ Test the ability to use VMRelocate on the target userid. Input: Request Handle with the following properties: function - 'MIGRATEVM' subfunction - 'TEST' userid - userid of the virtual machine parms['dest'] - Target SSI system. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter migrateVM.testMigrate") parms = ["-T", rh.userid, "-k", "action=TEST"] if 'dest' in rh.parms: parms.extend(["-k", "destination=" + rh.parms['dest']]) results = invokeSMCLI(rh, "VMRELOCATE", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['rc'] == 4 and results['rs'] == 3000: if "0045" in results['response']: # User not logged on msg = msgs.msg['0418'][1] % (modId, rh.userid) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0418'][0]) else: # More details in message codes lines = results['response'].split("\n") for line in lines: if "Details:" in line: codes = line.split(' ', 1)[1] msg = msgs.msg['0420'][1] % (modId, "VMRELOCATE Move", rh.userid, codes) rh.printSysLog("Exit migrateVM.testMigrate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] zVMCloudConnector-1.4.1/smutLayer/smapi.py0000664000175000017510000001672713442676317020220 0ustar ruirui00000000000000# SMAPI functions for Systems Management Ultra Thin Layer # # 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. from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import invokeSMCLI modId = 'SMP' version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'API': ['invokeSmapiApi', lambda rh: invokeSmapiApi(rh)], 'HELP': ['help', lambda rh: help(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)]} """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = { 'API': [ ['API Name', 'apiName', True, 2]] } """ List of additional operands/options supported by the various subfunctions. The dictionary following the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'API': { '--operands': ['operands', -1, 2], '--showparms': ['showParms', 0, 0]} } def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter smapi.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms']: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: smapi." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit smapi.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for SMAPI functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def invokeSmapiApi(rh): """ Invoke a SMAPI API. Input: Request Handle with the following properties: function - 'SMAPI' subfunction - 'API' userid - 'HYPERVISOR' parms['apiName'] - Name of API as defined by SMCLI parms['operands'] - List (array) of operands to send or an empty list. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter smapi.invokeSmapiApi") if rh.userid != 'HYPERVISOR': userid = rh.userid else: userid = 'dummy' parms = ["-T", userid] if 'operands' in rh.parms: parms.extend(rh.parms['operands']) results = invokeSMCLI(rh, rh.parms['apiName'], parms) if results['overallRC'] == 0: rh.printLn("N", results['response']) else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit smapi.invokeCmd, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter smapi.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit smapi.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) rh.printSysLog("Exit smapi.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " SMAPI " + "api [--operands ]") rh.printLn("N", " python " + rh.cmdName + " SMAPI help") rh.printLn("N", " python " + rh.cmdName + " SMAPI version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the " + rh.function + " function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " api - Invoke a SMAPI API.") rh.printLn("N", " help - Displays this help information.") rh.printLn("N", " version - " + "show the version of the power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " - " + "Userid of the target virtual machine") rh.printLn("N", " - Name of the API to invoke") rh.printLn("N", " --operands - Additional API operands") return zVMCloudConnector-1.4.1/smutLayer/getHost.py0000664000175000017510000004455713442676317020526 0ustar ruirui00000000000000# GetHost functions for Systems Management Ultra Thin Layer # # 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. import os import subprocess from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import invokeSMCLI modId = 'GHO' version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'DISKPOOLNAMES': ['help', lambda rh: getDiskPoolNames(rh)], 'DISKPOOLSPACE': ['help', lambda rh: getDiskPoolSpace(rh)], 'FCPDEVICES': ['help', lambda rh: getFcpDevices(rh)], 'GENERAL': ['help', lambda rh: getGeneralInfo(rh)], 'HELP': ['help', lambda rh: help(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = { 'DISKPOOLSPACE': [ ['Disk Pool Name', 'poolName', False, 2] ] } """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'DISKPOOLNAMES': {'--showparms': ['showParms', 0, 0]}, 'DISKPOOLSPACE': {'--showparms': ['showParms', 0, 0]}, 'FCPDEVICES': {'--showparms': ['showParms', 0, 0]}, 'GENERAL': {'--showparms': ['showParms', 0, 0]}, 'HELP': {'--showparms': ['showParms', 0, 0]}, 'VERSION': {'--showparms': ['showParms', 0, 0]}, } def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getHost.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: getHost." + str(subfuncHandler[rh.subfunction][0]) + "(rh)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit getHost.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getDiskPoolNames(rh): """ Obtain the list of disk pools known to the directory manager. Input: Request Handle with the following properties: function - 'GETHOST' subfunction - 'DISKPOOLNAMES' Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getHost.getDiskPoolNames") parms = ["-q", "1", "-e", "3", "-T", "dummy"] results = invokeSMCLI(rh, "Image_Volume_Space_Query_DM", parms) if results['overallRC'] == 0: for line in results['response'].splitlines(): poolName = line.partition(' ')[0] rh.printLn("N", poolName) else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit getHost.getDiskPoolNames, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getDiskPoolSpace(rh): """ Obtain disk pool space information for all or a specific disk pool. Input: Request Handle with the following properties: function - 'GETHOST' subfunction - 'DISKPOOLSPACE' parms['poolName'] - Name of the disk pool. Optional, if not present then information for all disk pools is obtained. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getHost.getDiskPoolSpace") results = {'overallRC': 0} if 'poolName' not in rh.parms: poolNames = ["*"] else: if isinstance(rh.parms['poolName'], list): poolNames = rh.parms['poolName'] else: poolNames = [rh.parms['poolName']] if results['overallRC'] == 0: # Loop thru each pool getting total. Do it for query 2 & 3 totals = {} for qType in ["2", "3"]: parms = [ "-q", qType, "-e", "3", "-T", "DUMMY", "-n", " ".join(poolNames)] results = invokeSMCLI(rh, "Image_Volume_Space_Query_DM", parms) if results['overallRC'] == 0: for line in results['response'].splitlines(): parts = line.split() if len(parts) == 9: poolName = parts[7] else: poolName = parts[4] if poolName not in totals: totals[poolName] = {"2": 0., "3": 0.} if parts[1][:4] == "3390": totals[poolName][qType] += int(parts[3]) * 737280 elif parts[1][:4] == "9336": totals[poolName][qType] += int(parts[3]) * 512 else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI break if results['overallRC'] == 0: if len(totals) == 0: # No pool information found. msg = msgs.msg['0402'][1] % (modId, " ".join(poolNames)) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0402'][0]) else: # Produce a summary for each pool for poolName in sorted(totals): total = totals[poolName]["2"] + totals[poolName]["3"] rh.printLn("N", poolName + " Total: " + generalUtils.cvtToMag(rh, total)) rh.printLn("N", poolName + " Used: " + generalUtils.cvtToMag(rh, totals[poolName]["3"])) rh.printLn("N", poolName + " Free: " + generalUtils.cvtToMag(rh, totals[poolName]["2"])) rh.printSysLog("Exit getHost.getDiskPoolSpace, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getFcpDevices(rh): """ Lists the FCP device channels that are active, free, or offline. Input: Request Handle with the following properties: function - 'GETHOST' subfunction - 'FCPDEVICES' Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getHost.getFcpDevices") parms = ["-T", "dummy"] results = invokeSMCLI(rh, "System_WWPN_Query", parms) if results['overallRC'] == 0: rh.printLn("N", results['response']) else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit getHost.getFcpDevices, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getGeneralInfo(rh): """ Obtain general information about the host. Input: Request Handle with the following properties: function - 'GETHOST' subfunction - 'GENERAL' Output: Request Handle updated with the results. Return code - 0: ok Return code - 4: problem getting some info """ rh.printSysLog("Enter getHost.getGeneralInfo") # Get host using VMCP rh.results['overallRC'] = 0 cmd = ["sudo", "/sbin/vmcp", "query userid"] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: host = subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) host = bytes.decode(host) userid = host.split()[0] host = host.split()[2] except subprocess.CalledProcessError as e: msg = msgs.msg['0405'][1] % (modId, "Hypervisor Name", strCmd, e.output) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) host = "no info" except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.updateResults(msgs.msg['0421'][0]) host = "no info" # Get a bunch of info from /proc/sysinfo lparCpuTotal = "no info" lparCpuUsed = "no info" cecModel = "no info" cecVendor = "no info" hvInfo = "no info" with open('/proc/sysinfo', 'r') as myFile: for num, line in enumerate(myFile, 1): # Get total physical CPU in this LPAR if "LPAR CPUs Total" in line: lparCpuTotal = line.split()[3] # Get used physical CPU in this LPAR if "LPAR CPUs Configured" in line: lparCpuUsed = line.split()[3] # Get CEC model if "Type:" in line: cecModel = line.split()[1] # Get vendor of CEC if "Manufacturer:" in line: cecVendor = line.split()[1] # Get hypervisor type and version if "VM00 Control Program" in line: hvInfo = line.split()[3] + " " + line.split()[4] if lparCpuTotal == "no info": msg = msgs.msg['0405'][1] % (modId, "LPAR CPUs Total", "cat /proc/sysinfo", "not found") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) if lparCpuUsed == "no info": msg = msgs.msg['0405'][1] % (modId, "LPAR CPUs Configured", "cat /proc/sysinfo", "not found") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) if cecModel == "no info": msg = msgs.msg['0405'][1] % (modId, "Type:", "cat /proc/sysinfo", "not found") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) if cecVendor == "no info": msg = msgs.msg['0405'][1] % (modId, "Manufacturer:", "cat /proc/sysinfo", "not found") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) if hvInfo == "no info": msg = msgs.msg['0405'][1] % (modId, "VM00 Control Program", "cat /proc/sysinfo", "not found") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) # Get processor architecture arch = str(os.uname()[4]) # Get LPAR memory total & offline parm = ["-T", "dummy", "-k", "STORAGE="] lparMemTotal = "no info" lparMemStandby = "no info" results = invokeSMCLI(rh, "System_Information_Query", parm) if results['overallRC'] == 0: for line in results['response'].splitlines(): if "STORAGE=" in line: lparMemOnline = line.split()[0] lparMemStandby = line.split()[4] lparMemTotal = lparMemOnline.split("=")[2] lparMemStandby = lparMemStandby.split("=")[1] else: # SMAPI API failed, so we put out messages # 300 and 405 for consistency rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI msg = msgs.msg['0405'][1] % (modId, "LPAR memory", "(see message 300)", results['response']) rh.printLn("ES", msg) # Get LPAR memory in use parm = ["-T", "dummy", "-k", "detailed_cpu=show=no"] lparMemUsed = "no info" results = invokeSMCLI(rh, "System_Performance_Information_Query", parm) if results['overallRC'] == 0: for line in results['response'].splitlines(): if "MEMORY_IN_USE=" in line: lparMemUsed = line.split("=")[1] lparMemUsed = generalUtils.getSizeFromPage(rh, lparMemUsed) else: # SMAPI API failed, so we put out messages # 300 and 405 for consistency rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI msg = msgs.msg['0405'][1] % (modId, "LPAR memory in use", "(see message 300)", results['response']) rh.printLn("ES", msg) # Get IPL Time ipl = "" cmd = ["sudo", "/sbin/vmcp", "query cplevel"] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: ipl = subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) ipl = bytes.decode(ipl).split("\n")[2] except subprocess.CalledProcessError as e: msg = msgs.msg['0405'][1] % (modId, "IPL Time", strCmd, e.output) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0405'][0]) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.updateResults(msgs.msg['0421'][0]) # Create output string outstr = "ZCC USERID: " + userid outstr += "\nz/VM Host: " + host outstr += "\nArchitecture: " + arch outstr += "\nCEC Vendor: " + cecVendor outstr += "\nCEC Model: " + cecModel outstr += "\nHypervisor OS: " + hvInfo outstr += "\nHypervisor Name: " + host outstr += "\nLPAR CPU Total: " + lparCpuTotal outstr += "\nLPAR CPU Used: " + lparCpuUsed outstr += "\nLPAR Memory Total: " + lparMemTotal outstr += "\nLPAR Memory Offline: " + lparMemStandby outstr += "\nLPAR Memory Used: " + lparMemUsed outstr += "\nIPL Time: " + ipl rh.printLn("N", outstr) rh.printSysLog("Exit getHost.getGeneralInfo, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for GetHost functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getHost.parseCmdline") rh.userid = '' if rh.totalParms >= 2: rh.subfunction = rh.request[1].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 2 # Begin Parsing at 3rd operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) rh.printSysLog("Exit getHost.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " GetHost " + "diskpoolnames") rh.printLn("N", " python " + rh.cmdName + " GetHost " + "diskpoolspace ") rh.printLn("N", " python " + rh.cmdName + " GetHost fcpdevices") rh.printLn("N", " python " + rh.cmdName + " GetHost general") rh.printLn("N", " python " + rh.cmdName + " GetHost help") rh.printLn("N", " python " + rh.cmdName + " GetHost version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the GetHost function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " diskpoolnames - " + "Returns the names of the directory manager disk pools.") rh.printLn("N", " diskpoolspace - " + "Returns disk pool size information.") rh.printLn("N", " fcpdevices - " + "Lists the FCP device channels that are active, free, or") rh.printLn("N", " offline.") rh.printLn("N", " general - " + "Returns the general information related to the z/VM") rh.printLn("N", " hypervisor environment.") rh.printLn("N", " help - Returns this help information.") rh.printLn("N", " version - Show the version of this function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " - Name of the disk pool.") return zVMCloudConnector-1.4.1/smutLayer/cmdVM.py0000664000175000017510000001617113442676317020106 0ustar ruirui00000000000000# CmdVM functions for Systems Management Ultra Thin Layer # # 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. from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import execCmdThruIUCV modId = 'CMD' version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'CMD': ['invokeCmd', lambda rh: invokeCmd(rh)], 'HELP': ['help', lambda rh: help(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = { 'CMD': [ ['Command to send', 'cmd', True, 2], ], } """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'CMD': { '--showparms': ['showParms', 0, 0], }, } def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter cmdVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: cmdVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit cmdVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for CmdVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def invokeCmd(rh): """ Invoke the command in the virtual machine's operating system. Input: Request Handle with the following properties: function - 'CMDVM' subfunction - 'CMD' userid - userid of the virtual machine parms['cmd'] - Command to send Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter cmdVM.invokeCmd, userid: " + rh.userid) results = execCmdThruIUCV(rh, rh.userid, rh.parms['cmd']) if results['overallRC'] == 0: rh.printLn("N", results['response']) else: rh.printLn("ES", results['response']) rh.updateResults(results) rh.printSysLog("Exit cmdVM.invokeCmd, rc: " + str(results['overallRC'])) return results['overallRC'] def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter cmdVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit cmdVM.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) rh.printSysLog("Exit cmdVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " CmdVM cmd ") rh.printLn("N", " python " + rh.cmdName + " CmdVM help") rh.printLn("N", " python " + rh.cmdName + " CmdVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the CmdVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " cmd - " + "Send a command to a virtual machine's operating system.") rh.printLn("N", " help - " + "Displays this help information.") rh.printLn("N", " version - " + "show the version of the power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " - " + "Userid of the target virtual machine") rh.printLn("N", " - " + "Command to send to the virtual machine's OS.") return zVMCloudConnector-1.4.1/smutLayer/vmUtils.py0000664000175000017510000012275113442676324020543 0ustar ruirui00000000000000# Virtual Machine Utilities for Systems Management Ultra Thin Layer # # 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. import re import subprocess from subprocess import CalledProcessError import time from smutLayer import msgs modId = 'VMU' version = '1.0.0' # Version of this script def disableEnableDisk(rh, userid, vaddr, option): """ Disable or enable a disk. Input: Request Handle: owning userid virtual address option ('-e': enable, '-d': disable) Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - rc from the chccwdev command or IUCV transmission. rs - rs from the chccwdev command or IUCV transmission. results - possible error message from the IUCV transmission. """ rh.printSysLog("Enter vmUtils.disableEnableDisk, userid: " + userid + " addr: " + vaddr + " option: " + option) results = { 'overallRC': 0, 'rc': 0, 'rs': 0, 'response': '' } """ Can't guarantee the success of online/offline disk, need to wait Until it's done because we may detach the disk after -d option or use the disk after the -e option """ for secs in [0.1, 0.4, 1, 1.5, 3, 7, 15, 32, 30, 30, 60, 60, 60, 60, 60]: strCmd = "sudo /sbin/chccwdev " + option + " " + vaddr + " 2>&1" results = execCmdThruIUCV(rh, userid, strCmd) if results['overallRC'] == 0: break elif (results['overallRC'] == 2 and results['rc'] == 8 and results['rs'] == 1 and option == '-d'): # Linux does not know about the disk being disabled. # Ok, nothing to do. Treat this as a success. results = {'overallRC': 0, 'rc': 0, 'rs': 0, 'response': ''} break time.sleep(secs) rh.printSysLog("Exit vmUtils.disableEnableDisk, rc: " + str(results['overallRC'])) return results def execCmdThruIUCV(rh, userid, strCmd, hideInLog=[]): """ Send a command to a virtual machine using IUCV. Input: Request Handle Userid of the target virtual machine Command string to send (Optional) List of strCmd words (by index) to hide in sysLog by replacing the word with "". Output: Dictionary containing the following: overallRC - overall return code, 0: success, 2: failure rc - RC returned from iucvclnt if overallRC != 0. rs - RS returned from iucvclnt if overallRC != 0. errno - Errno returned from iucvclnt if overallRC != 0. response - Output of the iucvclnt command or this routine. Notes: 1) This routine does not use the Request Handle printLn function. This is because an error might be expected and we might desire to suppress it. Instead, any error messages are put in the response dictionary element that is returned. """ if len(hideInLog) == 0: rh.printSysLog("Enter vmUtils.execCmdThruIUCV, userid: " + userid + " cmd: " + strCmd) else: logCmd = strCmd.split(' ') for i in hideInLog: logCmd[i] = '' rh.printSysLog("Enter vmUtils.execCmdThruIUCV, userid: " + userid + " cmd: " + ' '.join(logCmd)) iucvpath = '/opt/zthin/bin/IUCV/' results = { 'overallRC': 0, 'rc': 0, 'rs': 0, 'errno': 0, 'response': [], } cmd = ['sudo', iucvpath + "iucvclnt", userid, strCmd] try: results['response'] = subprocess.check_output( cmd, stderr=subprocess.STDOUT, close_fds=True) if isinstance(results['response'], bytes): results['response'] = bytes.decode(results['response']) except CalledProcessError as e: msg = [] results['overallRC'] = 2 results['rc'] = e.returncode output = bytes.decode(e.output) match = re.search('Return code (.+?),', output) if match: try: results['rc'] = int(match.group(1)) except ValueError: # Return code in response from IUCVCLNT is not an int. msg = msgs.msg['0311'][1] % (modId, userid, strCmd, results['rc'], match.group(1), output) if not msg: # We got the rc. Now, get the rs. match = re.search('Reason code (.+?)\.', output) if match: try: results['rs'] = int(match.group(1)) except ValueError: # Reason code in response from IUCVCLNT is not an int. msg = msgs.msg['0312'][1] % (modId, userid, strCmd, results['rc'], match.group(1), output) if msg: # Already produced an error message. pass elif results['rc'] == 1: # Command was not authorized or a generic Linux error. msg = msgs.msg['0313'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) elif results['rc'] == 2: # IUCV client parameter error. msg = msgs.msg['0314'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) elif results['rc'] == 4: # IUCV socket error msg = msgs.msg['0315'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) elif results['rc'] == 8: # Executed command failed msg = msgs.msg['0316'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) elif results['rc'] == 16: # File Transport failed msg = msgs.msg['0317'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) elif results['rc'] == 32: # IUCV server file was not found on this system. msg += msgs.msg['0318'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) else: # Unrecognized IUCV client error msg = msgs.msg['0319'][1] % (modId, userid, strCmd, results['rc'], results['rs'], output) results['response'] = msg except Exception as e: # Other exceptions from this system (i.e. not the managed system). results = msgs.msg['0421'][0] msg = msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e)) results['response'] = msg rh.printSysLog("Exit vmUtils.execCmdThruIUCV, rc: " + str(results['rc'])) return results def getPerfInfo(rh, useridlist): """ Get the performance information for a userid Input: Request Handle Userid to query <- may change this to a list later. Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - RC returned from SMCLI if overallRC = 0. rs - RS returned from SMCLI if overallRC = 0. errno - Errno returned from SMCLI if overallRC = 0. response - Stripped and reformatted output of the SMCLI command. """ rh.printSysLog("Enter vmUtils.getPerfInfo, userid: " + useridlist) parms = ["-T", rh.userid, "-c", "1"] results = invokeSMCLI(rh, "Image_Performance_Query", parms) if results['overallRC'] != 0: # SMCLI failed. rh.printLn("ES", results['response']) rh.printSysLog("Exit vmUtils.getPerfInfo, rc: " + str(results['overallRC'])) return results lines = results['response'].split("\n") usedTime = 0 totalCpu = 0 totalMem = 0 usedMem = 0 try: for line in lines: if "Used CPU time:" in line: usedTime = line.split()[3].strip('"') # Value is in us, need make it seconds usedTime = int(usedTime) / 1000000 if "Guest CPUs:" in line: totalCpu = line.split()[2].strip('"') if "Max memory:" in line: totalMem = line.split()[2].strip('"') # Value is in Kb, need to make it Mb totalMem = int(totalMem) / 1024 if "Used memory:" in line: usedMem = line.split()[2].strip('"') usedMem = int(usedMem) / 1024 except Exception as e: msg = msgs.msg['0412'][1] % (modId, type(e).__name__, str(e), results['response']) rh.printLn("ES", msg) results['overallRC'] = 4 results['rc'] = 4 results['rs'] = 412 if results['overallRC'] == 0: memstr = "Total Memory: %iM\n" % totalMem usedmemstr = "Used Memory: %iM\n" % usedMem procstr = "Processors: %s\n" % totalCpu timestr = "CPU Used Time: %i sec\n" % usedTime results['response'] = memstr + usedmemstr + procstr + timestr rh.printSysLog("Exit vmUtils.getPerfInfo, rc: " + str(results['rc'])) return results def installFS(rh, vaddr, mode, fileSystem, diskType): """ Install a filesystem on a virtual machine's dasd. Input: Request Handle: userid - Userid that owns the disk Virtual address as known to the owning system. Access mode to use to get the disk. Disk Type - 3390 or 9336 Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - RC returned from SMCLI if overallRC = 0. rs - RS returned from SMCLI if overallRC = 0. errno - Errno returned from SMCLI if overallRC = 0. response - Output of the SMCLI command. """ rh.printSysLog("Enter vmUtils.installFS, userid: " + rh.userid + ", vaddr: " + str(vaddr) + ", mode: " + mode + ", file system: " + fileSystem + ", disk type: " + diskType) results = { 'overallRC': 0, 'rc': 0, 'rs': 0, 'errno': 0, } out = '' diskAccessed = False # Get access to the disk. cmd = ["sudo", "/opt/zthin/bin/linkdiskandbringonline", rh.userid, vaddr, mode] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: out = subprocess.check_output(cmd, close_fds=True) if isinstance(out, bytes): out = bytes.decode(out) diskAccessed = True except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. results = msgs.msg['0421'][0] rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) if results['overallRC'] == 0: """ sample output: linkdiskandbringonline maint start time: 2017-03-03-16:20:48.011 Success: Userid maint vdev 193 linked at ad35 device name dasdh linkdiskandbringonline exit time: 2017-03-03-16:20:52.150 """ match = re.search('Success:(.+?)\n', out) if match: parts = match.group(1).split() if len(parts) > 9: device = "/dev/" + parts[9] else: strCmd = ' '.join(cmd) rh.printLn("ES", msgs.msg['0416'][1] % (modId, 'Success:', 10, strCmd, out)) results = msgs.msg['0416'][0] rh.updateResults(results) else: strCmd = ' '.join(cmd) rh.printLn("ES", msgs.msg['0417'][1] % (modId, 'Success:', strCmd, out)) results = msgs.msg['0417'][0] rh.updateResults(results) if results['overallRC'] == 0 and diskType == "3390": # dasdfmt the disk cmd = ["sudo", "/sbin/dasdfmt", "-y", "-b", "4096", "-d", "cdl", "-f", device] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: out = subprocess.check_output(cmd, close_fds=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. strCmd = " ".join(cmd) rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['overallRC'] == 0 and diskType == "3390": # Settle the devices so we can do the partition. strCmd = ("which udevadm &> /dev/null && " + "udevadm settle || udevsettle") rh.printSysLog("Invoking: " + strCmd) try: subprocess.check_output( strCmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. strCmd = " ".join(cmd) rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['overallRC'] == 0 and diskType == "3390": # Prepare the partition with fdasd cmd = ["sudo", "/sbin/fdasd", "-a", device] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, close_fds=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['overallRC'] == 0 and diskType == "9336": # Delete the existing partition in case the disk already # has a partition in it. cmd = "sudo /sbin/fdisk " + device + " << EOF\nd\nw\nEOF" rh.printSysLog("Invoking: /sbin/fdsik " + device + " << EOF\\nd\\nw\\nEOF ") try: out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, cmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, cmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['overallRC'] == 0 and diskType == "9336": # Prepare the partition with fdisk cmd = "sudo /sbin/fdisk " + device + " << EOF\nn\np\n1\n\n\nw\nEOF" rh.printSysLog("Invoking: sudo /sbin/fdisk " + device + " << EOF\\nn\\np\\n1\\n\\n\\nw\\nEOF") try: out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, cmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, cmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['overallRC'] == 0: # Settle the devices so we can do the partition. strCmd = ("which udevadm &> /dev/null && " + "udevadm settle || udevsettle") rh.printSysLog("Invoking: " + strCmd) try: subprocess.check_output( strCmd, stderr=subprocess.STDOUT, close_fds=True, shell=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. strCmd = " ".join(cmd) rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['overallRC'] == 0: # Install the file system into the disk. device = device + "1" # Point to first partition if fileSystem != 'swap': if fileSystem == 'xfs': cmd = ["sudo", "mkfs.xfs", "-f", device] else: cmd = ["sudo", "mkfs", "-F", "-t", fileSystem, device] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: out = subprocess.check_output(cmd, stderr=subprocess.STDOUT, close_fds=True) if isinstance(out, bytes): out = bytes.decode(out) rh.printLn("N", "File system: " + fileSystem + " is installed.") except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) else: rh.printLn("N", "File system type is swap. No need to install " + "a filesystem.") if diskAccessed: # Give up the disk. cmd = ["sudo", "/opt/zthin/bin/offlinediskanddetach", rh.userid, vaddr] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: out = subprocess.check_output(cmd, close_fds=True) if isinstance(out, bytes): out = bytes.decode(out) except CalledProcessError as e: rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode rh.updateResults(results) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) rh.printSysLog("Exit vmUtils.installFS, rc: " + str(results['rc'])) return results def invokeSMCLI(rh, api, parms, hideInLog=[]): """ Invoke SMCLI and parse the results. Input: Request Handle API name, SMCLI parms as an array (Optional) List of parms (by index) to hide in sysLog by replacing the parm with "". Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - RC returned from SMCLI if overallRC = 0. rs - RS returned from SMCLI if overallRC = 0. errno - Errno returned from SMCLI if overallRC = 0. response - String output of the SMCLI command. Note: - If the first three words of the header returned from smcli do not do not contain words that represent valid integer values or contain too few words then one or more error messages are generated. THIS SHOULD NEVER OCCUR !!!! """ if len(hideInLog) == 0: rh.printSysLog("Enter vmUtils.invokeSMCLI, userid: " + rh.userid + ", function: " + api + ", parms: " + str(parms)) else: logParms = parms for i in hideInLog: logParms[i] = '' rh.printSysLog("Enter vmUtils.invokeSMCLI, userid: " + rh.userid + ", function: " + api + ", parms: " + str(logParms)) goodHeader = False results = { 'overallRC': 0, 'rc': 0, 'rs': 0, 'errno': 0, 'response': [], 'strError': '', } cmd = [] cmd.append('sudo') cmd.append('/opt/zthin/bin/smcli') cmd.append(api) cmd.append('--addRCheader') try: smcliResp = subprocess.check_output(cmd + parms, close_fds=True) if isinstance(smcliResp, bytes): smcliResp = bytes.decode(smcliResp, errors='replace') smcliResp = smcliResp.split('\n', 1) results['response'] = smcliResp[1] results['overallRC'] = 0 results['rc'] = 0 except CalledProcessError as e: strCmd = " ".join(cmd + parms) # Break up the RC header into its component parts. if e.output == '': smcliResp = [''] else: smcliResp = bytes.decode(e.output).split('\n', 1) # Split the header into its component pieces. rcHeader = smcliResp[0].split('(details)', 1) if len(rcHeader) == 0: rcHeader = ['', ''] elif len(rcHeader) == 1: # No data after the details tag. Add empty [1] value. rcHeader.append('') codes = rcHeader[0].split(' ') # Validate the rc, rs, and errno. if len(codes) < 3: # Unexpected number of codes. Need at least 3. results = msgs.msg['0301'][0] results['response'] = msgs.msg['0301'][1] % (modId, api, strCmd, rcHeader[0], rcHeader[1]) else: goodHeader = True # Convert the first word (overall rc from SMAPI) to an int # and set the SMUT overall rc based on this value. orcError = False try: results['overallRC'] = int(codes[0]) if results['overallRC'] not in [8, 24, 25]: orcError = True except ValueError: goodHeader = False orcError = True if orcError: results['overallRC'] = 25 # SMCLI Internal Error results = msgs.msg['0302'][0] results['response'] = msgs.msg['0302'][1] % (modId, api, codes[0], strCmd, rcHeader[0], rcHeader[1]) # Convert the second word to an int and save as rc. try: results['rc'] = int(codes[1]) except ValueError: goodHeader = False results = msgs.msg['0303'][0] results['response'] = msgs.msg['0303'][1] % (modId, api, codes[1], strCmd, rcHeader[0], rcHeader[1]) # Convert the second word to an int and save it as either # the rs or errno. try: word3 = int(codes[2]) if results['overallRC'] == 8: results['rs'] = word3 # Must be an rs elif results['overallRC'] == 25: results['errno'] = word3 # Must be the errno # We ignore word 3 for everyone else and default to 0. except ValueError: goodHeader = False results = msgs.msg['0304'][0] results['response'] = msgs.msg['0304'][1] % (modId, api, codes[1], strCmd, rcHeader[0], rcHeader[1]) results['strError'] = rcHeader[1].lstrip() if goodHeader: # Produce a message that provides the error info. results['response'] = msgs.msg['0300'][1] % (modId, api, results['overallRC'], results['rc'], results['rs'], results['errno'], strCmd, smcliResp[1]) except Exception as e: # All other exceptions. strCmd = " ".join(cmd + parms) results = msgs.msg['0305'][0] results['response'] = msgs.msg['0305'][1] % (modId, strCmd, type(e).__name__, str(e)) rh.printSysLog("Exit vmUtils.invokeSMCLI, rc: " + str(results['overallRC'])) return results def isLoggedOn(rh, userid): """ Determine whether a virtual machine is logged on. Input: Request Handle: userid being queried Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - 0: if we got status. Otherwise, it is the error return code from the commands issued. rs - Based on rc value. For rc==0, rs is: 0: if we determined it is logged on. 1: if we determined it is logged off. """ rh.printSysLog("Enter vmUtils.isLoggedOn, userid: " + userid) results = { 'overallRC': 0, 'rc': 0, 'rs': 0, } cmd = ["sudo", "/sbin/vmcp", "query", "user", userid] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) except CalledProcessError as e: search_pattern = '(^HCP\w\w\w045E|^HCP\w\w\w361E)'.encode() match = re.search(search_pattern, e.output) if match: # Not logged on results['rs'] = 1 else: # Abnormal failure rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, e.output)) results = msgs.msg['0415'][0] results['rs'] = e.returncode except Exception as e: # All other exceptions. results = msgs.msg['0421'][0] rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.printSysLog("Exit vmUtils.isLoggedOn, overallRC: " + str(results['overallRC']) + " rc: " + str(results['rc']) + " rs: " + str(results['rs'])) return results def punch2reader(rh, userid, fileLoc, spoolClass): """ Punch a file to a virtual reader of the specified virtual machine. Input: Request Handle - for general use and to hold the results userid - userid of the virtual machine fileLoc - File to send spoolClass - Spool class Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter punch2reader.punchFile") results = {} # Setting rc to time out rc code as default and its changed during runtime results['rc'] = 9 # Punch to the current user intially and then change the spool class. cmd = ["sudo", "/usr/sbin/vmur", "punch", "-r", fileLoc] strCmd = ' '.join(cmd) for secs in [1, 2, 3, 5, 10]: rh.printSysLog("Invoking: " + strCmd) try: results['response'] = subprocess.check_output(cmd, close_fds=True, stderr=subprocess.STDOUT) if isinstance(results['response'], bytes): results['response'] = bytes.decode(results['response']) results['rc'] = 0 rh.updateResults(results) break except CalledProcessError as e: results['response'] = e.output # Check if we have concurrent instance of vmur active to_find = "A concurrent instance of vmur is already active" to_find = to_find.encode() if results['response'].find(to_find) == -1: # Failure in VMUR punch update the rc results['rc'] = 7 break else: # if concurrent vmur is active try after sometime rh.printSysLog("Punch in use. Retrying after " + str(secs) + " seconds") time.sleep(secs) except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] rh.updateResults(results) if results['rc'] == 7: # Failure while issuing vmur command (For eg: invalid file given) msg = msgs.msg['0401'][1] % (modId, fileLoc, userid, results['response']) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0401'][0]) elif results['rc'] == 9: # Failure due to vmur timeout msg = msgs.msg['0406'][1] % (modId, fileLoc) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0406'][0]) if rh.results['overallRC'] == 0: # On VMUR success change the class of the spool file spoolId = re.findall(r'\d+', str(results['response'])) cmd = ["sudo", "vmcp", "change", "rdr", str(spoolId[0]), "class", spoolClass] strCmd = " ".join(cmd) rh.printSysLog("Invoking: " + strCmd) try: results['response'] = subprocess.check_output(cmd, close_fds=True, stderr=subprocess.STDOUT) if isinstance(results['response'], bytes): results['response'] = bytes.decode(results['response']) rh.updateResults(results) except CalledProcessError as e: msg = msgs.msg['0404'][1] % (modId, spoolClass, e.output) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0404'][0]) # Class change failed # Delete the punched file from current userid cmd = ["sudo", "vmcp", "purge", "rdr", spoolId[0]] strCmd = " ".join(cmd) rh.printSysLog("Invoking: " + strCmd) try: results['response'] = subprocess.check_output(cmd, close_fds=True, stderr=subprocess.STDOUT) if isinstance(results['response'], bytes): results['response'] = bytes.decode(results['response']) # We only need to issue the printLn. # Don't need to change return/reason code values except CalledProcessError as e: msg = msgs.msg['0403'][1] % (modId, spoolId[0], e.output) rh.printLn("ES", msg) except Exception as e: # All other exceptions related to purge. # We only need to issue the printLn. # Don't need to change return/reason code values rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) except Exception as e: # All other exceptions related to change rdr. results = msgs.msg['0421'][0] rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.updateResults(msgs.msg['0421'][0]) if rh.results['overallRC'] == 0: # Transfer the file from current user to specified user cmd = ["sudo", "vmcp", "transfer", "*", "rdr", str(spoolId[0]), "to", userid, "rdr"] strCmd = " ".join(cmd) rh.printSysLog("Invoking: " + strCmd) try: results['response'] = subprocess.check_output(cmd, close_fds=True, stderr=subprocess.STDOUT) if isinstance(results['response'], bytes): results['response'] = bytes.decode(results['response']) rh.updateResults(results) except CalledProcessError as e: msg = msgs.msg['0424'][1] % (modId, fileLoc, userid, e.output) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0424'][0]) # Transfer failed so delete the punched file from current userid cmd = ["sudo", "vmcp", "purge", "rdr", spoolId[0]] strCmd = " ".join(cmd) rh.printSysLog("Invoking: " + strCmd) try: results['response'] = subprocess.check_output(cmd, close_fds=True, stderr=subprocess.STDOUT) if isinstance(results['response'], bytes): results['response'] = bytes.decode(results['response']) # We only need to issue the printLn. # Don't need to change return/reason code values except CalledProcessError as e: msg = msgs.msg['0403'][1] % (modId, spoolId[0], e.output) rh.printLn("ES", msg) except Exception as e: # All other exceptions related to purge. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) except Exception as e: # All other exceptions related to transfer. results = msgs.msg['0421'][0] rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.updateResults(msgs.msg['0421'][0]) rh.printSysLog("Exit vmUtils.punch2reader, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def waitForOSState(rh, userid, desiredState, maxQueries=90, sleepSecs=5): """ Wait for the virtual OS to go into the indicated state. Input: Request Handle userid whose state is to be monitored Desired state, 'up' or 'down', case sensitive Maximum attempts to wait for desired state before giving up Sleep duration between waits Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - RC returned from execCmdThruIUCV if overallRC = 0. rs - RS returned from execCmdThruIUCV if overallRC = 0. errno - Errno returned from execCmdThruIUCV if overallRC = 0. response - Updated with an error message if wait times out. Note: """ rh.printSysLog("Enter vmUtils.waitForOSState, userid: " + userid + " state: " + desiredState + " maxWait: " + str(maxQueries) + " sleepSecs: " + str(sleepSecs)) results = {} strCmd = "echo 'ping'" stateFnd = False for i in range(1, maxQueries + 1): results = execCmdThruIUCV(rh, rh.userid, strCmd) if results['overallRC'] == 0: if desiredState == 'up': stateFnd = True break else: if desiredState == 'down': stateFnd = True break if i < maxQueries: time.sleep(sleepSecs) if stateFnd is True: results = { 'overallRC': 0, 'rc': 0, 'rs': 0, } else: maxWait = maxQueries * sleepSecs rh.printLn("ES", msgs.msg['0413'][1] % (modId, userid, desiredState, maxWait)) results = msgs.msg['0413'][0] rh.printSysLog("Exit vmUtils.waitForOSState, rc: " + str(results['overallRC'])) return results def waitForVMState(rh, userid, desiredState, maxQueries=90, sleepSecs=5): """ Wait for the virtual machine to go into the indicated state. Input: Request Handle userid whose state is to be monitored Desired state, 'on' or 'off', case sensitive Maximum attempts to wait for desired state before giving up Sleep duration between waits Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - RC returned from SMCLI if overallRC = 0. rs - RS returned from SMCLI if overallRC = 0. Note: """ rh.printSysLog("Enter vmUtils.waitForVMState, userid: " + userid + " state: " + desiredState + " maxWait: " + str(maxQueries) + " sleepSecs: " + str(sleepSecs)) results = {} cmd = ["sudo", "/sbin/vmcp", "query", "user", userid] strCmd = " ".join(cmd) stateFnd = False for i in range(1, maxQueries + 1): rh.printSysLog("Invoking: " + strCmd) try: out = subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) if isinstance(out, bytes): out = bytes.decode(out) if desiredState == 'on': stateFnd = True break except CalledProcessError as e: match = re.search('(^HCP\w\w\w045E|^HCP\w\w\w361E)', e.output) if match: # Logged off if desiredState == 'off': stateFnd = True break else: # Abnormal failure out = e.output rh.printLn("ES", msgs.msg['0415'][1] % (modId, strCmd, e.returncode, out)) results = msgs.msg['0415'][0] results['rs'] = e.returncode break except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) results = msgs.msg['0421'][0] if i < maxQueries: # Sleep a bit before looping. time.sleep(sleepSecs) if stateFnd is True: results = { 'overallRC': 0, 'rc': 0, 'rs': 0, } else: maxWait = maxQueries * sleepSecs rh.printLn("ES", msgs.msg['0414'][1] % (modId, userid, desiredState, maxWait)) results = msgs.msg['0414'][0] rh.printSysLog("Exit vmUtils.waitForVMState, rc: " + str(results['overallRC'])) return results def purgeReader(rh): """ Purge reader of the specified userid. Input: Request Handle Output: Dictionary containing the following: overallRC - overall return code, 0: success, non-zero: failure rc - RC returned from SMCLI if overallRC = 0. rs - RS returned from SMCLI if overallRC = 0. errno - Errno returned from SMCLI if overallRC = 0. response - Updated with an error message. Note: """ rh.printSysLog("Enter vmUtils.purgeRDR, userid: " + rh.userid) results = {'overallRC': 0, 'rc': 0, 'rs': 0, 'response': []} parms = ['-T', rh.userid, '-k', 'spoolids=all'] results = invokeSMCLI(rh, "System_RDR_File_Manage", parms) if results['overallRC'] != 0: rh.printLn("ES", results['response']) rh.updateResults(results) rh.printSysLog("Exit vmUtils.purgeReader, rc: " + str(results['overallRC'])) return results zVMCloudConnector-1.4.1/smutLayer/deleteVM.py0000664000175000017510000002101413442676317020575 0ustar ruirui00000000000000# DeleteVM functions for Systems Management Ultra Thin Layer # # 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. from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import invokeSMCLI, isLoggedOn, purgeReader modId = "DVM" version = "1.0.0" # List of subfunction handlers. # Each subfunction contains a list that has: # Readable name of the routine that handles the subfunction, # Code for the function call. subfuncHandler = { 'DIRECTORY': ['deleteMachine', lambda rh: deleteMachine(rh)], 'HELP': ['help', lambda rh: help(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = {} """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'DIRECTORY': {'--showparms': ['showParms', 0, 0]}, 'HELP': {}, 'VERSION': {}, } def deleteMachine(rh): """ Delete a virtual machine from the user directory. Input: Request Handle with the following properties: function - 'DELETEVM' subfunction - 'DIRECTORY' userid - userid of the virtual machine to be deleted. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter deleteVM.deleteMachine") results = {'overallRC': 0, 'rc': 0, 'rs': 0} # Is image logged on ? state = 'on' # Assume 'on'. results = isLoggedOn(rh, rh.userid) if results['overallRC'] != 0: # Cannot determine the log on/off state. # Message already included. Act as if it is 'on'. pass elif results['rs'] == 0: # State is powered on. pass else: state = 'off' # Reset values for rest of subfunction results['overallRC'] = 0 results['rc'] = 0 results['rs'] = 0 if state == 'on': parms = ["-T", rh.userid, "-f IMMED"] results = invokeSMCLI(rh, "Image_Deactivate", parms) if results['overallRC'] == 0: pass elif (results['overallRC'] == 8 and results['rc'] == 200 and (results['rs'] == 12 or results['rs'] == 16)): # Tolerable error. Machine is already in or going into the state # that we want it to enter. rh.updateResults({}, reset=1) else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results returned by invokeSMCLI # Clean up the reader before delete if results['overallRC'] == 0: result = purgeReader(rh) if result['overallRC'] != 0: # Tolerable the purge failure error rh.updateResults({}, reset=1) if results['overallRC'] == 0: parms = ["-T", rh.userid, "-e", "0"] results = invokeSMCLI(rh, "Image_Delete_DM", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results returned by invokeSMCLI rh.printSysLog("Exit deleteVM.deleteMachine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter deleteVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: deleteVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit deleteVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for DeleteVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter deleteVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit deleteVM.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) rh.printSysLog("Exit deleteVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " DeleteVM directory") rh.printLn("N", " python " + rh.cmdName + " DeleteVM help") rh.printLn("N", " python " + rh.cmdName + " DeleteVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the DeleteVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " directory - " + "Delete a virtual machine from the user directory.") rh.printLn("N", " help - " + "Displays this help information.") rh.printLn("N", " version - " + "Show the version of the power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " - " + "Userid of the target virtual machine") return zVMCloudConnector-1.4.1/smutLayer/msgs.py0000664000175000017510000013265013442676317020052 0ustar ruirui00000000000000# Messages for Systems Management Ultra Thin Layer # # 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. """ List of modules that use these messages and their module id. ModId Module ----- -------------------- CVM changeVM.py CMD cmdVM.py DVM deleteVM.py GUT generalUtils.py GHO getHost.py GVM getVM.py MVM makeVM.py MIG migrateVM.py PVM powerVM.py RQH ReqHandle.py SMC Reserved for smcli SMP smapi.py VMU vmUtils.py List of messages. Message id is the key. Messages are grouped by their message number. Each message is defined in a array that provides: - Dictionary of values for the result structure, e.g. {'overallRC': 4, 'rc': 100} The keys in the dictionary can contain: 'overallRC', 'rc', 'rs' 'errno', and 'strError'. These allow us to use the dictionary to update the ReqHandle results dictionary. - Message text (may contain substitution directives, e.g. %s, %i) - Sample text in a python tuple """ msg = { # 0001-0099: Parsing Messages '0001': [{'overallRC': 4, 'rc': 4, 'rs': 1}, "ULT%s0001E %s %s subfunction's operand at position %i (%s) " + "is not an integer: %s", ('RQH', 'FUNCTION_NAME', 'SUBFUNCTION_NAME', 'OPERAND_POSITION', 'OPERAND', 'OPERAND_VALUE')], # Explain: An error was detected while parsing the command. # The indicated operand is not an integer. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax so that the indicated operand is # an integer, e.g., 10 and reissue the command. '0002': [{'overallRC': 4, 'rc': 4, 'rs': 2}, "ULT%s0002E %s's %s subfunction is missing positional " + "operand (%s) at position %i.", ('RQH', 'FUNCTION_NAME', 'SUBFUNCTION_NAME', 'OPERAND', 'OPERAND_POSITION')], # Explain: An error was detected while parsing the command. # A positional operand is missing. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax by specifying the missing operand # and reissue the command. '0003': [{'overallRC': 4, 'rc': 4, 'rs': 3}, "ULT%s0003E %s's %s subfunction %s keyword operand is " + "missing a value.", ('RQH', 'FUNCTION_NAME', 'SUBFUNCTION_NAME', 'KEYWORD')], # Explain: An error was detected while parsing the command. # A keyword operand that requires a value was specified without # the value. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax to provide a value for the specified # keyword operand and reissue the command. '0004': [{'overallRC': 4, 'rc': 4, 'rs': 4}, "ULT%s0004E %s's %s subfunction %s keyword operand is not " + "an integer: %s", ('RQH', 'FUNCTION_NAME', 'SUBFUNCTION_NAME', 'KEYWORD', 'KEYWORD_VALUE')], # Explain: An error was detected while parsing the command. # The specified operand for a keyword is not an integer. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax so that the keyword operand is # an integer, e.g., 10 and reissue the command. '0005': [{'overallRC': 4, 'rc': 4, 'rs': 5}, "ULT%s0005E %s's %s subfunction does not recognize keyword: %s", ('RQH', 'FUNCTION_NAME', 'SUBFUNCTION_NAME', 'KEYWORD')], # Explain: An error was detected while parsing the command. # An unrecognized keyword was encountered. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax to specify a recognized keyword # and reissue the command. '0006': [{'overallRC': 4, 'rc': 4, 'rs': 6}, "ULT%s0006E %s's %s subfunction encountered an unknown " + "operand: %s", ('RQH', 'FUNCTION_NAME', 'SUBFUNCTION_NAME', 'OPERAND')], # Explain: An error was detected while parsing the command. # An unknown operand was encountered. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax and reissue the command. '0007': [{'overallRC': 4, 'rc': 4, 'rs': 7}, "ULT%s0007E Unrecognized function: %s", ('RQH', 'FUNCTION_NAME')], # Explain: An error was detected while parsing the command. # The specified function is not known. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax and reissue the command. '0008': [{'overallRC': 4, 'rc': 4, 'rs': 8}, "ULT%s0008E Specified function is not 'HELP' or 'VERSION': %s", ('RQH', 'SPECIFIED_FUNCTION')], # Explain: An error was detected while parsing the command. # The specified function was not 'HELP' or 'VERSION' which are the # only valid functions for a command of the specified length. # SysAct: Processing of the function terminates. # UserResp: Correct the syntax and reissue the command. '0009': [{'overallRC': 4, 'rc': 4, 'rs': 9}, "ULT%s0009E Too few arguments specified.", ('RQH')], # Explain: An error was detected while parsing the command. # The minimum number of arguments were not provided for the command. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax and reissue the command. '0010': [{'overallRC': 4, 'rc': 4, 'rs': 10}, "ULT%s0010E Userid is missing", ('RQH')], # Explain: An error was detected while parsing the command. # A userid operand was not specified. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax and specify the userid along # with any other required operands and reissue the command. '0011': [{'overallRC': 4, 'rc': 4, 'rs': 11}, "ULT%s0011E Subfunction is missing. It should be one of " + "the following: %s", ('RQH', 'VALID_SUBFUNCTIONS')], # Explain: An error was detected while parsing the command. # The name of the subfunction was not specified. # SysAct: Processing of the function terminates. # UserResp: Correct the syntax and specify the userid along # with any other required operands and reissue the command. '0012': [{'overallRC': 4, 'rc': 4, 'rs': 12}, "ULT%s0012E The request data is not one of the supported types " + "of list or string: %s", ('RQH', 'REQUEST_DATA_TYPE')], # Explain: The ReqHandle parseCmdline method was called with # the request passed in a variable that was not a # list or base string. Only these types of variables are # supported for passing of the request to be parsed. # SysAct: Processing of the function terminates. # UserResp: Correct the calling function to use either a # list or base string to hold the request to be processed # and reinvoke the call. '0013': [{'overallRC': 4, 'rc': 4, 'rs': 13}, "ULT%s0010E The desired state was: %s. Valid states are: %s", ('RQH', 'DESIRED_STATS', 'VALID_STATS')], # Explain: An error was detected while parsing the command. # The state operand value is not one of the accepted values. # The valid values are shown in the message. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax to use one of the valid states # and reissue the command. '0014': [{'overallRC': 4, 'rc': 4, 'rs': 14}, "ULT%s0014E The option %s was specified but the option %s " + "was not specified. These options must both be specified.", ('RQH', 'OPTION1', 'OPTION2')], # Explain: An error was detected while parsing the command. # An option was specified which required a related # option that was not specified. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax to specify both options and # reissue the command. '0015': [{'overallRC': 4, 'rc': 4, 'rs': 15}, "ULT%s0015E The file system was not 'ext2', 'ext3', " + "'ext4', 'xfs' or 'swap': %s", ('RQH', 'FILE_SYSTEM')], # Explain: An error was detected while parsing the command. # The type of file system does not match one of the valid # values. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax to use a valid file system type # and reissue the command. '0016': [{'overallRC': 4, 'rc': 4, 'rs': 16}, "ULT%s0016E The scp Data Type was not 'hex', 'ebcdic', " + "or 'delete': %s", ('RQH', 'DATA_TYPE')], # Explain: An error was detected while parsing the command. # The value specified for the scp data type is not one of the # recognized values. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the syntax to use a valid scp data type and # reissue the command. '0017': [{'overallRC': 4, 'rc': 4, 'rs': 17}, # dict is not used "ULT%s0017W The maxwait time %i sec is not evenly divisible " + "by the poll interval %i sec. Maximum wait time will be %i " + "sec or %i poll intervals.", ('RQH', 'MAX_WAIT', 'POLL_INTERVAL', 'RECOMMEND_MAX_WAIT', 'RECOMMEND_POLL_INTERVAL')], # Explain: When trying to determine how many polling intervals # to wait for a desired guest power state, it was found that the # specified maximum wait time was not evenly divisible by the # number of polling interval seconds. The program instead # rounded the maximum wait time up to be evenly divisble # by the polling interval. # SysAct: Processing of the function continues with the # new wait time. # UserResp: If the wait time is unacceptably long, invoke # the function with a maximum wait time and polling # interval time which are evenly divisible and of an # acceptable duration. # 0200-0299: Utility Messages '0200': [{'overallRC': 4, 'rc': 4, 'rs': 200}, "ULT%s0200E The size of the disk is not valid: %s", ('GUT', 'DISK_SIZE')], # Explain: An error was encountered while attempting # to convert the size of a disk from bytes to cylinders # (for 3390 type disks) or bytes to blocks (for FBA type disks). # This error can be caused by specifying the size as only a # magnitude, (e.g., 'G' or 'M') instead of an integer # appended to a magnitude (e.g., '20G'). # SysAct: Processing of the subfunction terminates. # UserResp: Correct the disk size to specify a disk magnitude # that includes the integer portion of the size in addition # to the magnitude and reissue the command. '0201': [{'overallRC': 4, 'rc': 4, 'rs': 201}, "ULT%s0201E Failed to convert %s to a number of blocks.", ('GUT', 'DISK_SIZE')], # Explain: An error was encountered while attempting # to convert the size of a disk from bytes to blocks. # The size ended with a magnitude character and should have # had an integer value prepended to the magnitude character # (e.g. '10M' or '10G'). # The probable cause of the error is that the integer # portion of the size contains a non-numeric character. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the disk size to specify a valid value # and reissue the command. '0202': [{'overallRC': 4, 'rc': 4, 'rs': 202}, "ULT%s0202E %s is not an integer size of blocks.", ('GUT', 'NUM_BLOCKS')], # Explain: An error was encountered while attempting # to convert the size of a disk from bytes to blocks. # The size did not end with a valid magnitude character # (i.e., 'M' or 'G') so it was treated as an integer # value (e.g. '100000'). The probable cause of this # error is that the size contains non-numeric # characters. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the disk size to specify a valid value # and reissue the command. '0203': [{'overallRC': 4, 'rc': 4, 'rs': 203}, "ULT%s0203E Failed to convert %s to a number of cylinders.", ('GUT', 'DISK_SIZE')], # Explain: An error was encountered while attempting # to convert the size of a disk from bytes to cylinders. # The size ended with a magnitude character and should have # had an integer value prepended to the magnitude character # (e.g. '10M' or '10G'). # The probable cause of the error is that the integer # portion of the size contains non-numeric characters. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the disk size to specify a valid value # and reissue the command. '0204': [{'overallRC': 4, 'rc': 4, 'rs': 204}, "ULT%s0204E %s is not an integer size of cylinders.", ('GUT', 'DISK_SIZE')], # Explain: An error was encountered while attempting # to convert the size of a disk from bytes to cylinders. # The size did not end with a valid magnitude character # (i.e., 'M' or 'G') so it was treated as an integer # value (e.g. '100000'). The probable cause of this # error is that the size contains non-numeric # characters. # SysAct: Processing of the subfunction terminates. # UserResp: Correct the disk size to specify a valid value # and reissue the command. '0205': [{'overallRC': 4, 'rc': 4, 'rs': 205}, "ULT%s0204E memory size did not end with suffix 'G' or 'M'.", ('MVM')], # Explain: An error was encountered while handling memory size. # The size did not end with a valid magnitude character # (i.e., 'M' or 'G'). # SysAct: Processing of the subfunction terminates. # UserResp: Correct the memory size to specify a valid value # and reissue the command. '0206': [{'overallRC': 4, 'rc': 4, 'rs': 206}, "ULT%s0204E Max memory size %s specified is less than " + "initial memory size %s.", ('MVM', 'MAX_MEM_SIZE', 'INIT_MEM_SIZE')], # Explain: An error was encountered while handling memory size. # The size did not end with a valid magnitude character # (i.e., 'M' or 'G'). # SysAct: Processing of the subfunction terminates. # UserResp: Correct the memory size to specify a valid value # and reissue the command. # 0207-0299: Available # SMCLI and SMAPI related messages. '0300': [{'overallRC': 8}, # dict is not used. "ULT%s0300E SMAPI API failed: %s, overall rc: %s, rc: %s, " + "rs: %s, errno: %s, cmd: %s, out: %s", ('SMP', 'API_NAME', 'OVERALLRC', 'RC', 'RS', 'ERRNO', 'CMD', 'OUTPUT')], # Explain: The smcli program was invoked to call z/VM SMAPI for # the indicated API. An error was encountered. The overall rc # indicates the location of the problem: # 8 - SMAPI returned the error. The rc and rs values are # the values provided by SMAPI. The z/VM Systems # Management Application Programming book provides # additional documentation related to the return code and # reason code in the API description and in the "Return # and Reason Code Summary" chapter. # 24 - The smcli program identified a parameter validation # error. A message will indicate what was detected. # It could be a missing parameter, invalid parameter, etc. # Invoke the smcli program using the -h parameter and the # API name shown in the error message to obtain additional # invocation help, e.g. "./smcli Image_Query_DM -h". # In addition, the z/VM Systems Management Application # Programming book provides additional documentation # related to the return code and reason code in the API # description. # 25 - The smcli program encountered an internal error. # The rc and errno contains information related to the # problem. The error message from the smcli invocation # and log entries in the system log provide the most # useful information to debug this error. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the problem using the # information described in the explanation section. Reinvoke # the function after you correct the problem. '0301': [{'overallRC': 25, 'rc': 301, 'rs': 0}, "ULT%s0301E SMAPI API failed: %s, response header does not " + "have the expected 3 values before the (details) string. " + "cmd: %s, response header: %s, out: %s", ('SMP', 'API_NAME', 'CMD', 'HEADER', 'OUTPUT')], # Explain: The smcli program was invoked to call z/VM SMAPI for # the indicated API. The expected response from the smcli # program has a header that contains 3 integers followed by # the string '(details)'. The response returned by the program # does not have the expected header. This indicates a problem # in the smcli program or a problem invoking the smcli program. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure. If it is not a # Linux permission problem then investigate a possible coding # error in the smcli program. Correct the problem and reinvoke # the function. '0302': [{'overallRC': 25, 'rc': 302, 'rs': 0}, "ULT%s0302E SMAPI API failed: %s, word 1 in " + "the response header is not an integer or in the range of " + "expected values. word 1: %s, cmd: %s, response " + "header: %s, out: %s", ('SMP', 'API_NAME', 'WORD1', 'CMD', 'HEADER', 'OUTPUT')], # Explain: The smcli program was invoked to call z/VM SMAPI for # the indicated API. The expected response from the smcli # program has a header that contains 3 integers followed by # the string '(details)'. The first word should provide the # overall return code of the smcli invocation that indicates # where the failure occurred. However, it does not represent # an integer value or is not the expected error values of # 8, 24 or 25. This indicates a problem in the smcli program. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure. # Investigate a possible coding error in the smcli program. # Correct the problem and reinvoke the function. '0303': [{'overallRC': 25, 'rc': 303, 'rs': 0}, "ULT%s0303E SMAPI API failed: %s, word 2 in the response " + "header is not an integer. word 2: %s, cmd: %s, response " + "header: %s, out: %s", ('SMP', 'API_NAME', 'WORD2', 'CMD', 'HEADER', 'OUTPUT')], # Explain: The smcli program was invoked to call z/VM SMAPI for # the indicated API. The expected response from the smcli # program has a header that contains 3 integers followed by # the string '(details)'. The second word should provide the # specific return code of the smcli invocation. However, it # does not represent an integer value. This indicates a # problem in the smcli program. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure. # You may need an update to the smcli program. # Correct the problem and reinvoke the function. '0304': [{'overallRC': 25, 'rc': 304, 'rs': 0}, "ULT%s0304E SMAPI API failed: %s, word 3 in the response " + "header is not an integer. word 3: %s, cmd: %s, response " + "header: %s, out: %s", ('SMP', 'API_NAME', 'WORD3', 'CMD', 'HEADER', 'OUTPUT')], # Explain: The smcli program was invoked to call z/VM SMAPI for # the indicated API. The expected response from the smcli # program has a header that contains 3 integers followed by # the string '(details)'. The third word should provide # the reason code or errno, depending on the error. However, # it does not represent an integer value. This indicates # a problem in the smcli program. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure. # You may need an update to the smcli program. # Correct the problem and reinvoke the function. '0305': [{'overallRC': 99, 'rc': 305, 'rs': 0}, "ULT%s0305E Exception received on an attempt to " + "communicate with SMAPI, cmd: %s, exception: %s, " + "details: %s", ('SMP', 'CMD', 'EXCEPTION', 'EXCEPTION_DETAILS')], # Explain: The function attempted to invoke the smcli # program to communicate with z/VM SMAPI. This failed # due to the exception shown in the message. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure using # the exception and exception details provided in the message. # Reinvoke the function after correcting the problem. # 0306-0310: Available # IUCV related messages '0311': [{'overallRC': 2, 'rc': 2, 'rs': 99}, # dict is not used. "ULT%s0311E On %s, command sent through IUCV failed, " + "rc in response string is not an integer. " + "cmd: %s, rc: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'OUTPUT')], # Explain: The IUCV client returned a non-integer return # code value. # SysAct: Processing of the function terminates. # UserResp: Contact the support team with the information # included in the message. Investigate the problem in the # IUCVCLNT program, fix the code and reinvoke the function. '0312': [{'overallRC': 2, 'rc': 2, 'rs': 99}, # dict is not used. "ULT%s0312E On %s, command sent through IUCV failed, " + "reason code in response string is not an integer. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The IUCV client returned a non-integer reason # code value. # SysAct: Processing of the function terminates. # UserResp: Contact the support team with the information # included in the message. The IUCVCLNT program is the probable # cause of the failure. This will require a code change. '0313': [{'overallRC': 2, 'rc': 1}, # dict is not used. "ULT%s0313E On %s, command sent through IUCV was not " + "authorized or a generic Linux error occurred. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The command that was sent to the target system failed. # The cause of the failure is either a Linux permission problem # for the command being executed or a generic Linux error. # SysAct: Processing of the function terminates. # UserResp: Use the information included in the message to determine # the cause of the failure on the target system and correct the # problem. After correcting the problem, you should be able to # reinvoke the failing function. '0314': [{'overallRC': 2, 'rc': 2}, # dict is not used. "ULT%s0314E IUCV client parameter error sending command to %s. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The IUCVCLNT program communicates with managed # systems using IUCV. The program detected invocation # errors. This can be caused by a problem in the level of the # IUCVCLNT program or the function that invoked it. # SysAct: Processing of the function terminates. # UserResp: Use the information included in the message to determine # the cause of the failure. This could require the support # team to provide a code change to either the IUCVCLNT program # or the code that invoked it. '0315': [{'overallRC': 2, 'rc': 4}, # dict is not used. "ULT%s0315E IUCV socket error sending command to %s. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The IUCVCLNT program communicates with managed # systems using IUCV. The program encountered an IUCV # communication failure when it attempted to send a # command to the managed system. # This is probably caused by a failure in the managed system # that prevents the system from receiving the command. # One cause could be that the system logged off z/VM. # Another cause is that the managed system is not running the # related IUCV daemon or has not authorized access by # the system contacting it in the /etc/iucv_authorized_userid # file. # SysAct: Processing of the function terminates. # UserResp: Use the information included in the message to # determine the cause of the failure. Reinvoke the function # after you correct the problem. '0316': [{'overallRC': 2, 'rc': 8}, # dict is not used. "ULT%s0316E On %s, command sent through IUCV failed. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The command that was sent to the target system failed. # SysAct: Processing of the function terminates. # UserResp: Use the information included in the message to # determine the cause of the failure. Reinvoke the function # after you correct the problem. '0317': [{'overallRC': 2, 'rc': 16}, # dict is not used. "ULT%s0317E File transport failure while processing " + "command for %s. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The IUCVCLNT program failed to send a file to # the target system. # SysAct: Processing of the function terminates. # UserResp: Use the information included in the message to # determine the cause of the failure. Reinvoke the function # after you correct the problem. '0318': [{'overallRC': 2, 'rc': 32}, # dict is not used. "ULT%s0318E On %s, IUCV server file was not found. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The IUCVCLNT program failed to find the IUCVSERV # file on the local system. This file is expected to exist # in the same directory as the IUCVCLNT program. # SysAct: Processing of the function terminates. # UserResp: Determine the reason that the IUCVSERV file could # not be located and correct the problem. Reinvoke the # function after you correct the problem. '0319': [{'overallRC': 2}, # dict is not used. "ULT%s0319E Unrecognized IUCV client error encountered " + "while sending a command through IUCV to %s. " + "cmd: %s, rc: %s, rs: %s, out: %s", ('SMP', 'USERID', 'CMD', 'RC', 'RS', 'OUTPUT')], # Explain: The IUCVCLNT program returned a non-zero return code # that does not correspond to a recognized error value. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the error using the # information in the message. Log files on the local system # and the target system may contain useful information to # identify the failure. Reinvoke the function after you # correct the problem. # General subfunction processing messages '0400': [{'overallRC': 4, 'rc': 4, 'rs': 400}, "ULT%s0400E The worker script %s does not exist.", ('GUT', 'SCRIPT_NAME')], # Explain: The activation engine modification script specified # for "aeScript" cannot be found. # SysAct: Processing of the function ends with no action # taken. # UserResp: Correct the function call to point to an existing script # and reinvoke the function. '0401': [{'overallRC': 4, 'rc': 7, 'rs': 401}, "ULT%s0401E Failed to punch %s file to guest: %s, out: %s", ('GUT', 'FILE_LOCATION', 'USERID', 'OUTPUT')], # Explain: The vmur punch command failed for the specified # reason. # SysAct: Processing of the function ends with no action # taken. # UserResp: Look up the reason the vmur command failed, correct # the problem and reinvoke the function. '0402': [{'overallRC': 4, 'rc': 5, 'rs': 402}, "ULT%s0402E No information was found for the specified " + "pool(s): %s", ('GUT', 'DISK_POOL')], # Explain: Image_Volume_Space_Query_DM returned successfully # but the list of pools of the specified names was empty. # SysAct: Processing terminates with an error. # UserResp: Correct the function call to query existing pools and # reinvoke the function. '0403': [{'overallRC': 4, 'rc': 99, 'rs': 403}, # dict is not used. "ULT%s0403E Failed to purge reader file %s, out: %s", ('GUT', 'SPOOL_ID', 'OUTPUT')], # Explain: The vmcp purge reader file command failed. # The system was already in the process of cleaning up from a # failed attempt to punch a file, so the error processing # continues. # SysAct: Error processing continues. # UserResp: Manually clean up the specified reader file using # CP commands to avoid problems with old files or spool space # filling up. '0404': [{'overallRC': 4, 'rc': 8, 'rs': 404}, "ULT%s0404E Failed to spool the punch to the specified class %s" + ", out:%s ", ('GUT', 'SPOOL_CLASS', 'OUTPUT')], # Explain: The vmcp change reader command failed with the # specified output. # SysAct: Processing of the function ends with no action # taken. # UserResp: Look up the reason the change reader command failed # in the CP messages book or vmcp help. Correct the problem # and reinvoke the function. '0405': [{'overallRC': 4, 'rc': 6, 'rs': 405}, "ULT%s0405E Unable to obtain information related to: " + "%s. Command used was: %s. Output was: %s", ('GUT', 'KEYWORD', 'CMD', 'OUTPUT')], # Explain: While gathering hypervisor information, one of the # commands used failed and that piece of information could # not be queried. # SysAct: The getHost GENERAL function returns "no info" for # the specified hypervisor information. # UserResp: If the information is needed, investigate the # failure, correct it and reinvoke the function. '0406': [{'overallRC': 4, 'rc': 9, 'rs': 406}, "ULT%s0406E Failed to punch %s because of VMUR timeout ", ('GUT', 'FILE_LOCATION')], # Explain: When punching a file to the reader, the vmur punch # command is issued up to 5 times with increasing timeouts. # This error comes after the 5th try if the vmur command # was still unsuccessful. # SysAct: Processing of the function ends with no action taken. # UserResp: This error could be because of another process # also issuing vmur commands at the same time. Wait a few # seconds and reinvoke the function. '0407': [{'overallRC': 4, 'rc': 4, 'rs': 407}, # dict is not used. "ULT%s0407W Unable to spool reader to all classes, " + "it is possible that there may be additional console " + "files available that are not listed in the response. " + "Response from %s is %s", ('GUT', 'CMD', 'OUTPUT')], # Explain: The vmcp spool reader class * command was not # successful. This means the reader could not be changed # to get files of all classes, and thus there could be # files that are ignored. # SysAct: Processing of the function continues. # UserResp: If missing files are suspected, investigate the # cause of the failure in the CP messages book or vmcp # help and reinvoke the function. '0408': [{'overallRC': 4, 'rc': 4, 'rs': 408}, "ULT%s0408E Error getting list of files in the reader " + "to search for logs from user %s. Response from %s is %s", ('GUT', 'USERID', 'CMD', 'OUTPUT')], # Explain: The vmur list command failed. The list of files # in the user's reader could not be determined. # SysAct: Processing of the function ends with no action taken. # UserResp: Investigate the failure in vmur and correct the # problem, then reinvoke the function. '0409': [{'overallRC': 4, 'rc': 4, 'rs': 409}, "ULT%s0409E Unable to get console log for user %s. " + "The userid is either: not logged on, not spooling " + "its console, or has not created any console output. " + "Error rc=rs=8 returned from " + "Image_Console_Get.", ('GUT', 'USERID')], # Explain: The Image_Console_Get SMAPI call returned that # there were no spool files available for that user. # SysAct: Processing of the function ends with no action taken. # UserResp: Check that the user is logged on, has issued a # SPOOL CONSOLE command and has done some actions that # would result in console output, then reinvoke the function. '0410': [{'overallRC': 4, 'rc': 4, 'rs': 410}, "ULT%s0410E Unable to get console log for user %s " + "no spool files were found in our reader from this " + "user, it is possible another process has already " + "received them.", ('GUT', 'USERID')], # Explain: The Image_Console_Get SMAPI call should have # put files of class T and "CON" with the userid as the # filename in our reader. However no files were found # in the vmur list output with these characteristcs. # SysAct: Processing of the function ends with no action taken. # UserResp: Likely another process in this virtual machine # has already processed the spool files. They are gone. '0411': [{'overallRC': 4, 'rc': 4, 'rs': 411}, "ULT%s0411E Unable to receive console output file. " + "Reader not online. /sys/bus/ccw/drivers/vmur/0.0.000c" + "/online = 0", ('GUT')], # Explain: The reader is typically at virtual device address # x'000C'. Linux does not believe this device is online. # SysAct: Processing of the function ends with no action taken. # UserResp: If the reader is at a different virtual device # address, update the SMUT code to recognize the alternative # device address, otherwise bring the reader at x'000C' online # to Linux. Then, reinvoke the function. '0412': [{'overallRC': 4, 'rc': 4, 'rs': 412}, # dict is not used. "ULT%s0412E Malformed reply from SMAPI, unable to fill " + "in performance information, exception: %s, " + "details: %s, Response: %s", ('GUT', 'EXCEPTION', 'EXCEPTION_DETAILS', 'OUTPUT')], # Explain: An error was encountered while processing the # response information from the SMAPI Image_Performance_Query # API. The response is not in the expected format. # The exception that occurred during processing of the # response, its details and the response are included # in the message. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure. A code change # may be needed in the function or in the z/VM SMAPI code. # After correcting the code, reinvoke the function. '0413': [{'overallRC': 99, 'rc': 99, 'rs': 413}, "ULT%s0413E Userid '%s' did not enter the expected " + "operating system state of '%s' in %i seconds.", ('GUT', 'USERID', 'DESIRED_STATE', 'MAX_WAIT')], # Explain: The managed system did not enter the operating # system state that was shown in the message in the # maximum number of seconds allowed for this to happen. # The maximum number of seconds a combination of the, # specified or defaulted, polling interval and maximum # maximum number of polling attempts. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure and correct # the cause. '0414': [{'overallRC': 99, 'rc': 99, 'rs': 414}, "ULT%s0414E Userid '%s' did not enter the expected " + "virtual machine state of '%s' in %i seconds.", ('GUT', 'USERID', 'DESIRED_STATE', 'MAX_WAIT')], # Explain: The managed system did not enter the virtual # machine log on/off state that was shown in the message # in the maximum number of seconds allowed for this to happen. # The maximum number of seconds a combination of the, # specified or defaulted, polling interval and maximum # maximum number of polling attempts. # SysAct: Processing of the function terminates. # UserResp: Determine the cause of the failure and correct # the cause. '0415': [{'overallRC': 3, 'rc': 415}, # rs comes from failing rc "ULT%s0415E Command failed: '%s', rc: %i out: %s", ('GUT', 'CMD', 'RC', 'OUTPUT')], # Explain: The indicated command failed. The return code # and output from the command are shown. # SysAct: Function processing terminates. # UserResp: Use the information provided with the message # to determine the cause of the failure and correct the # problem. Reinvoke the function after you correct the # problem. '0416': [{'overallRC': 99, 'rc': 99, 'rs': 416}, "ULT%s0416E Command returned a response " + "containing '%s' but did not have at least %i words " + "following it. cmd: '%s', out: '%s'", ('GUT', 'KEYWORD', 'NUM', 'CMD', 'OUTPUT')], # Explain: A command was invoked that returned a successful # return code indication. The response contained the # expected string but did not contain the expected number # of words that follow the string. # SysAct: Processing of the function terminates. # UserResp: Use the information provided in the message # to determine the cause of the problem and correct it. # Reinvoke the function after you correct the problem. '0417': [{'overallRC': 99, 'rc': 99, 'rs': 417}, "ULT%s0417E Command did not return the expected response " + "containing '%s', cmd: '%s', out: '%s'", ('GUT', 'KEYWORD', 'CMD', 'OUTPUT')], # Explain: A command was invoked that returned a successful # return code indication. The response did not contain the # expected string. # SysAct: Processing of the function terminates. # UserResp: Use the information provided in the message # to determine the reason the identified string was not # present in the response to identify the cause. # Reinvoke the function after you correct the problem. '0418': [{'overallRC': 99, 'rc': 99, 'rs': 418}, "ULT%s0418E Userid %s is not logged on to this system.", ('GUT', 'USERID')], # Explain: A CP message HCP0045E was returned, indicating the # userid specified is not logged on to this z/VM system, # thus it cannot be relocated. # SysAct: Processing of the function ends with no action taken. # UserResp: Correct the function call to specify a correct userid and # reinvoke the function. '0419': [{'overallRC': 99, 'rc': 99, 'rs': 419}, "ULT%s0419E A relocation is not in progress for userid %s.", ('GUT', 'USERID')], # Explain: An attempt was made to query or cancel a relocation # for a user, but the SMAPI command indicated that no # relocation was in progress. # SysAct: Processing of the function ends with no action taken. # UserResp: Reinvoke the function for a relocation that is in # progress. '0420': [{'overallRC': 99, 'rc': 99, 'rs': 420}, # dict is not used. "ULT%s0420E An error occurred issuing a %s for userid %s. " + "Please look up message(s): %s in the CP Messages book for " + "more information.", ('GUT', 'CMD', 'USERID', 'ERROR_CODE')], # Explain: The VMRELOCATE command returns a list of messages # containing all the problems encountered when trying to issue # the command. # SysAct: Processing of the function ends with no action taken. # UserResp: Look up the codes provided in the CP messages book, # correct the problems and reinvoke the function. '0421': [{'overallRC': 99, 'rc': 421, 'rs': 0}, "ULT%s0421E Exception received on an attempt to " + "execute a cmd: %s, exception: %s, " + "details: %s", ('GUT', 'CMD', 'EXCEPTION', 'EXCEPTION_DETAILS')], # Explain: The command indicated by the message failed. # The error message contains exception name and details # contained in the exception. # SysAct: Processing of the function ends with no further # action taken. # UserResp: Use the information in the message to determine # the cause of the error and correct the problem. # Reinvoke the function after you have corrected the problem. '0422': [{'overallRC': 99, 'rc': 422, 'rs': 0}, "ULT%s0422W Exception received on an attempt to " + "execute a cmd: %s, exception: %s, " + "details: %s. Will attempt to continue processing.", ('GUT', 'CMD', 'EXCEPTION', 'EXCEPTION_DETAILS')], # Explain: While trying to execute a vmcp command, an error # occurred. However the vmcp command was not central # to processing the subfunction, so processing # continues. # SysAct: Function processing continues. # UserResp: If there is reason to suspect the function did # not execute completely, investigate the error. Otherwise # ignore this message. '0423': [{'overallRC': 4, 'rc': 4, 'rs': 423}, # dict is not used. "ULT%s0423W Unable to spool reader to all classes, " + "it is possible that there may be additional console " + "files available that are not listed in the response. " + "Command: %s, exception %s, details %s. Will attempt " + "to continue processing.", ('GUT', 'CMD', 'EXCEPTION', 'EXCEPTION_DETAILS')], # Explain: The vmcp spool reader class * command was not # successful. This means the reader could not be changed # to get files of all classes, and thus there could be # files that are ignored. The exception was of a different # type than in message 407. # SysAct: Processing of the function continues. # UserResp: If missing files are suspected, investigate the # cause of the failure in the CP messages book or vmcp # help and reinvoke the function. '0424': [{'overallRC': 4, 'rc': 4, 'rs': 424}, "ULT%s0424E Failed to transfer %s file to guest: %s, out: %s", ('GUT', 'FILE_LOCATION', 'USERID', 'OUTPUT')], # Explain: The vmcp transfer command failed for the specified # reason. # SysAct: Processing of the function ends with no action # taken. # UserResp: Look up the reason the vmcp transfer command failed, # correct the problem and reinvoke the function. # 5000-6100: Reserved for SMCLI } zVMCloudConnector-1.4.1/smutLayer/makeVM.py0000664000175000017510000003255113442676317020260 0ustar ruirui00000000000000# MakeVM functions for Systems Management Ultra Thin Layer # # 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. import os from tempfile import mkstemp from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import invokeSMCLI modId = 'MVM' version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'DIRECTORY': ['createVM', lambda rh: createVM(rh)], 'HELP': ['help', lambda rh: help(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)]} """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = { 'DIRECTORY': [ ['password', 'pw', True, 2], ['Primary Memory Size (e.g. 2G)', 'priMemSize', True, 2], ['Privilege Class(es)', 'privClasses', True, 2]], } """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'DIRECTORY': { '--cpus': ['cpuCnt', 1, 1], '--ipl': ['ipl', 1, 2], '--logonby': ['byUsers', 1, 2], '--maxMemSize': ['maxMemSize', 1, 2], '--profile': ['profName', 1, 2], '--maxCPU': ['maxCPU', 1, 1], '--setReservedMem': ['setReservedMem', 0, 0], '--showparms': ['showParms', 0, 0]}, 'HELP': {}, 'VERSION': {}, } def createVM(rh): """ Create a virtual machine in z/VM. Input: Request Handle with the following properties: function - 'CMDVM' subfunction - 'CMD' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter makeVM.createVM") dirLines = [] dirLines.append("USER " + rh.userid + " " + rh.parms['pw'] + " " + rh.parms['priMemSize'] + " " + rh.parms['maxMemSize'] + " " + rh.parms['privClasses']) if 'profName' in rh.parms: dirLines.append("INCLUDE " + rh.parms['profName']) if 'maxCPU' in rh.parms: dirLines.append("MACHINE ESA %i" % rh.parms['maxCPU']) dirLines.append("CPU 00 BASE") if 'cpuCnt' in rh.parms: for i in range(1, rh.parms['cpuCnt']): dirLines.append("CPU %0.2X" % i) if 'ipl' in rh.parms: dirLines.append("IPL %0.4s" % rh.parms['ipl']) if 'byUsers' in rh.parms: for user in rh.parms['byUsers']: dirLines.append("LOGONBY " + user) priMem = rh.parms['priMemSize'].upper() maxMem = rh.parms['maxMemSize'].upper() if 'setReservedMem' in rh.parms and (priMem != maxMem): reservedSize = getReservedMemSize(rh, priMem, maxMem) if rh.results['overallRC'] != 0: rh.printSysLog("Exit makeVM.createVM, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] if reservedSize != '0M': dirLines.append("COMMAND DEF STOR RESERVED %s" % reservedSize) # Construct the temporary file for the USER entry. fd, tempFile = mkstemp() to_write = '\n'.join(dirLines) + '\n' os.write(fd, to_write.encode()) os.close(fd) parms = ["-T", rh.userid, "-f", tempFile] results = invokeSMCLI(rh, "Image_Create_DM", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI os.remove(tempFile) rh.printSysLog("Exit makeVM.createVM, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter makeVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: makeVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit makeVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for MakeVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter makeVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit makeVM.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) if 'byUsers' in rh.parms: users = [] for user in rh.parms['byUsers'].split(' '): users.append(user) rh.parms['byUsers'] = [] rh.parms['byUsers'].extend(users) if rh.subfunction == 'DIRECTORY' and 'maxMemSize' not in rh.parms: rh.parms['maxMemSize'] = rh.parms['priMemSize'] rh.printSysLog("Exit makeVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " MakeVM directory ") rh.printLn("N", " --cpus " + "--ipl --logonby ") rh.printLn("N", " --maxMemSize " + "--profile ") rh.printLn("N", " --maxCPU " + "--setReservedMem") rh.printLn("N", " python " + rh.cmdName + " MakeVM help") rh.printLn("N", " python " + rh.cmdName + " MakeVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the MakeVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " directory - " + "Create a virtual machine in the z/VM user directory.") rh.printLn("N", " help - Displays this help information.") rh.printLn("N", " version - " + "show the version of the makeVM function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " --cpus - " + "Specifies the desired number of virtual CPUs the") rh.printLn("N", " " + "guest will have.") rh.printLn("N", " --maxcpu - " + "Specifies the maximum number of virtual CPUs the") rh.printLn("N", " " + "guest is allowed to define.") rh.printLn("N", " --ipl - " + "Specifies an IPL disk or NSS for the virtual") rh.printLn("N", " " + "machine's directory entry.") rh.printLn("N", " --logonby - " + "Specifies a list of up to 8 z/VM userids who can log") rh.printLn("N", " " + "on to the virtual machine using their id and password.") rh.printLn("N", " --maxMemSize - " + "Specifies the maximum memory the virtual machine") rh.printLn("N", " " + "is allowed to define.") rh.printLn("N", " --setReservedMem - " + "Set the additional memory space (maxMemSize - priMemSize)") rh.printLn("N", " " + "as reserved memory of the virtual machine.") rh.printLn("N", " - " + "Specifies the password for the new virtual") rh.printLn("N", " " + "machine.") rh.printLn("N", " - " + "Specifies the initial memory size for the new virtual") rh.printLn("N", " " + "machine.") rh.printLn("N", " - " + "Specifies the privilege classes for the new virtual") rh.printLn("N", " " + "machine.") rh.printLn("N", " --profile - " + "Specifies the z/VM PROFILE to include in the") rh.printLn("N", " " + "virtual machine's directory entry.") rh.printLn("N", " - " + "Userid of the virtual machine to create.") return def getReservedMemSize(rh, mem, maxMem): rh.printSysLog("Enter makeVM.getReservedMemSize") gap = '0M' # Check size suffix memSuffix = mem[-1].upper() maxMemSuffix = maxMem[-1].upper() if (memSuffix not in ['M', 'G']) or (maxMemSuffix not in ['M', 'G']): # Suffix is not 'M' or 'G' msg = msgs.msg['0205'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0205'][0]) rh.printSysLog("Exit makeVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return gap # Convert both size to 'M' memMb = int(mem[:-1]) maxMemMb = int(maxMem[:-1]) if memSuffix == 'G': memMb = memMb * 1024 if maxMemSuffix == 'G': maxMemMb = maxMemMb * 1024 # Check maxsize is greater than initial mem size if maxMemMb < memMb: msg = msgs.msg['0206'][1] % (modId, maxMem, mem) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0206'][0]) rh.printSysLog("Exit makeVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return gap # The define storage command can support 1-7 digits decimal number # So we will use 'M' as suffix unless the gap size exceeds 9999999 # then convert to Gb. gapSize = maxMemMb - memMb if gapSize > 9999999: gapSize = gapSize / 1024 gap = "%iG" % gapSize else: gap = "%iM" % gapSize rh.printSysLog("Exit makeVM.getReservedMemSize, rc: " + str(rh.results['overallRC'])) return gap zVMCloudConnector-1.4.1/smutLayer/generalUtils.py0000664000175000017510000003113513442676317021533 0ustar ruirui00000000000000# General Utilities for Systems Management Ultra Thin Layer # # 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. import math from smutLayer import msgs fiveGigSize = (1024 * 5) modId = 'GUT' def cvtToBlocks(rh, diskSize): """ Convert a disk storage value to a number of blocks. Input: Request Handle Size of disk in bytes Output: Results structure: overallRC - Overall return code for the function: 0 - Everything went ok 4 - Input validation error rc - Return code causing the return. Same as overallRC. rs - Reason code causing the return. errno - Errno value causing the return. Always zero. Converted value in blocks """ rh.printSysLog("Enter generalUtils.cvtToBlocks") blocks = 0 results = {'overallRC': 0, 'rc': 0, 'rs': 0, 'errno': 0} blocks = diskSize.strip().upper() lastChar = blocks[-1] if lastChar == 'G' or lastChar == 'M': # Convert the bytes to blocks byteSize = blocks[:-1] if byteSize == '': # The size of the disk is not valid. msg = msgs.msg['0200'][1] % (modId, blocks) rh.printLn("ES", msg) results = msgs.msg['0200'][0] else: try: if lastChar == 'M': blocks = (float(byteSize) * 1024 * 1024) / 512 elif lastChar == 'G': blocks = (float(byteSize) * 1024 * 1024 * 1024) / 512 blocks = str(int(math.ceil(blocks))) except Exception: # Failed to convert to a number of blocks. msg = msgs.msg['0201'][1] % (modId, byteSize) rh.printLn("ES", msg) results = msgs.msg['0201'][0] elif blocks.strip('1234567890'): # Size is not an integer size of blocks. msg = msgs.msg['0202'][1] % (modId, blocks) rh.printLn("ES", msg) results = msgs.msg['0202'][0] rh.printSysLog("Exit generalUtils.cvtToBlocks, rc: " + str(results['overallRC'])) return results, blocks def cvtToCyl(rh, diskSize): """ Convert a disk storage value to a number of cylinders. Input: Request Handle Size of disk in bytes Output: Results structure: overallRC - Overall return code for the function: 0 - Everything went ok 4 - Input validation error rc - Return code causing the return. Same as overallRC. rs - Reason code causing the return. errno - Errno value causing the return. Always zero. Converted value in cylinders """ rh.printSysLog("Enter generalUtils.cvtToCyl") cyl = 0 results = {'overallRC': 0, 'rc': 0, 'rs': 0, 'errno': 0} cyl = diskSize.strip().upper() lastChar = cyl[-1] if lastChar == 'G' or lastChar == 'M': # Convert the bytes to cylinders byteSize = cyl[:-1] if byteSize == '': # The size of the disk is not valid. msg = msgs.msg['0200'][1] % (modId, lastChar) rh.printLn("ES", msg) results = msgs.msg['0200'][0] else: try: if lastChar == 'M': cyl = (float(byteSize) * 1024 * 1024) / 737280 elif lastChar == 'G': cyl = (float(byteSize) * 1024 * 1024 * 1024) / 737280 cyl = str(int(math.ceil(cyl))) except Exception: # Failed to convert to a number of cylinders. msg = msgs.msg['0203'][1] % (modId, byteSize) rh.printLn("ES", msg) results = msgs.msg['0203'][0] elif cyl.strip('1234567890'): # Size is not an integer value. msg = msgs.msg['0204'][1] % (modId, cyl) rh.printLn("ES", msg) results = msgs.msg['0202'][0] rh.printSysLog("Exit generalUtils.cvtToCyl, rc: " + str(results['overallRC'])) return results, cyl def cvtToMag(rh, size): """ Convert a size value to a number with a magnitude appended. Input: Request Handle Size bytes Output: Converted value with a magnitude """ rh.printSysLog("Enter generalUtils.cvtToMag") mSize = '' size = size / (1024 * 1024) if size > (1024 * 5): # Size is greater than 5G. Using "G" magnitude. size = size / 1024 mSize = "%.1fG" % size else: # Size is less than or equal 5G. Using "M" magnitude. mSize = "%.1fM" % size rh.printSysLog("Exit generalUtils.cvtToMag, magSize: " + mSize) return mSize def getSizeFromPage(rh, page): """ Convert a size value from page to a number with a magnitude appended. Input: Request Handle Size in page Output: Converted value with a magnitude """ rh.printSysLog("Enter generalUtils.getSizeFromPage") bSize = float(page) * 4096 mSize = cvtToMag(rh, bSize) rh.printSysLog("Exit generalUtils.getSizeFromPage, magSize: " + mSize) return mSize def parseCmdline(rh, posOpsList, keyOpsList): """ Parse the request command input. Input: Request Handle Positional Operands List. This is a dictionary that contains an array for each subfunction. The array contains a entry (itself an array) for each positional operand. That array contains: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). Keyword Operands List. This is a dictionary that contains an item for each subfunction. The value for the subfunction is a dictionary that contains a key for each recognized operand. The value associated with the key is an array that contains the following: - the related ReqHandle.parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter generalUtils.parseCmdline") # Handle any positional operands on the line. if rh.results['overallRC'] == 0 and rh.subfunction in posOpsList: ops = posOpsList[rh.subfunction] currOp = 0 # While we have operands on the command line AND # we have more operands in the positional operand list. while rh.argPos < rh.totalParms and currOp < len(ops): key = ops[currOp][1] # key for rh.parms[] opType = ops[currOp][3] # data type if opType == 1: # Handle an integer data type try: rh.parms[key] = int(rh.request[rh.argPos]) except ValueError: # keyword is not an integer msg = msgs.msg['0001'][1] % (modId, rh.function, rh.subfunction, (currOp + 1), ops[currOp][0], rh.request[rh.argPos]) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0001'][0]) break else: rh.parms[key] = rh.request[rh.argPos] currOp += 1 rh.argPos += 1 if (rh.argPos >= rh.totalParms and currOp < len(ops) and ops[currOp][2] is True): # Check for missing required operands. msg = msgs.msg['0002'][1] % (modId, rh.function, rh.subfunction, ops[currOp][0], (currOp + 1)) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0002'][0]) # Handle any keyword operands on the line. if rh.results['overallRC'] == 0 and rh.subfunction in keyOpsList: while rh.argPos < rh.totalParms: if rh.request[rh.argPos] in keyOpsList[rh.subfunction]: keyword = rh.request[rh.argPos] rh.argPos += 1 ops = keyOpsList[rh.subfunction] if keyword in ops: key = ops[keyword][0] opCnt = ops[keyword][1] opType = ops[keyword][2] if opCnt == 0: # Keyword has no additional value rh.parms[key] = True else: # Keyword has values following it. storeIntoArray = False # Assume single word if opCnt < 0: storeIntoArray = True # Property is a list all of the rest of the parms. opCnt = rh.totalParms - rh.argPos if opCnt == 0: # Need at least 1 operand value opCnt = 1 elif opCnt > 1: storeIntoArray = True if opCnt + rh.argPos > rh.totalParms: # keyword is missing its related value operand msg = msgs.msg['0003'][1] % (modId, rh.function, rh.subfunction, keyword) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0003'][0]) break """ Add the expected value to the property. Take into account if there are more than 1. """ if storeIntoArray: # Initialize the list. rh.parms[key] = [] for i in range(0, opCnt): if opType == 1: # convert from string to int and save it. try: if not storeIntoArray: rh.parms[key] = ( int(rh.request[rh.argPos])) else: rh.parms[key].append(int( rh.request[rh.argPos])) except ValueError: # keyword is not an integer msg = (msgs.msg['0004'][1] % (modId, rh.function, rh.subfunction, keyword, rh.request[rh.argPos])) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0004'][0]) break else: # Value is a string, save it. if not storeIntoArray: rh.parms[key] = rh.request[rh.argPos] else: rh.parms[key].append(rh.request[rh.argPos]) rh.argPos += 1 if rh.results['overallRC'] != 0: # Upper loop had an error break from loops. break else: # keyword is not in the subfunction's keyword list msg = msgs.msg['0005'][1] % (modId, rh.function, rh.subfunction, keyword) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0005'][0]) break else: # Subfunction does not support keywords msg = (msgs.msg['0006'][1] % (modId, rh.function, rh.subfunction, rh.request[rh.argPos])) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0006'][0]) break rh.printSysLog("Exit generalUtils.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] zVMCloudConnector-1.4.1/smutLayer/changeVM.py0000664000175000017510000011472113442676317020570 0ustar ruirui00000000000000# ChangeVM functions for Systems Management Ultra Thin Layer # # 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. import os.path import re import shutil import tarfile import tempfile from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import disableEnableDisk, execCmdThruIUCV, installFS from smutLayer.vmUtils import invokeSMCLI, isLoggedOn from smutLayer.vmUtils import punch2reader, purgeReader modId = "CVM" version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'ADD3390': ['add3390', lambda rh: add3390(rh)], 'ADD9336': ['add9336', lambda rh: add9336(rh)], 'DEDICATE': ['dedicate', lambda rh: dedicate(rh)], 'UNDEDICATE': ['undedicate', lambda rh: undedicate(rh)], 'AEMOD': ['addAEMOD', lambda rh: addAEMOD(rh)], 'IPL': ['addIPL', lambda rh: addIPL(rh)], 'LOADDEV': ['addLOADDEV', lambda rh: addLOADDEV(rh)], 'HELP': ['help', lambda rh: help(rh)], 'PUNCHFILE': ['punchFile', lambda rh: punchFile(rh)], 'PURGERDR': ['purgeRDR', lambda rh: purgeRDR(rh)], 'REMOVEDISK': ['removeDisk', lambda rh: removeDisk(rh)], 'REMOVEIPL': ['removeIPL', lambda rh: removeIPL(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = { 'ADD3390': [ ['Disk pool name', 'diskPool', True, 2], ['Virtual address', 'vaddr', True, 2], ['Disk size', 'diskSize', True, 2]], 'ADD9336': [ ['Disk pool name', 'diskPool', True, 2], ['Virtual address', 'vaddr', True, 2], ['Disk size', 'diskSize', True, 2]], 'DEDICATE': [ ['Virtual device address', 'vaddr', True, 2], ['Real device address', 'raddr', True, 2], ['Read only mode', 'mode', True, 2]], 'UNDEDICATE': [ ['Virtual device address', 'vaddr', True, 2]], 'AEMOD': [ ['Activation Engine Modification Script', 'aeScript', True, 2]], 'IPL': [ ['Virtual Address or NSS name', 'addrOrNSS', True, 2]], 'PUNCHFILE': [ ['File to punch', 'file', True, 2]], 'REMOVEDISK': [ ['Virtual address', 'vaddr', True, 2]], 'REMOVEIPL': [], } """ List of additional operands/options supported by the various subfunctions. The dictionary following the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'ADD3390': { '--filesystem': ['fileSystem', 1, 2], '--mode': ['mode', 1, 2], '--multipw': ['multiPW', 1, 2], '--readpw': ['readPW', 1, 2], '--showparms': ['showParms', 0, 0], '--writepw': ['writePW', 1, 2]}, 'ADD9336': { '--filesystem': ['fileSystem', 1, 2], '--mode': ['mode', 1, 2], '--multipw': ['multiPW', 1, 2], '--readpw': ['readPW', 1, 2], '--showparms': ['showParms', 0, 0], '--writepw': ['writePW', 1, 2]}, 'AEMOD': { '--invparms': ['invParms', 1, 2], '--showparms': ['showParms', 0, 0]}, 'HELP': {}, 'IPL': { '--loadparms': ['loadParms', 1, 2], '--parms': ['parms', 1, 2], '--showparms': ['showParms', 0, 0]}, 'LOADDEV': { '--boot': ['boot', 1, 2], '--addr': ['addr', 1, 2], '--lun': ['lun', 1, 2], '--wwpn': ['wwpn', 1, 2], '--scpDataType': ['scpDataType', 1, 2], '--scpData': ['scpData', 1, 2], '--showparms': ['showParms', 0, 0]}, 'PUNCHFILE': { '--class': ['class', 1, 2], '--showparms': ['showParms', 0, 0], }, 'PURGERDR': {'--showparms': ['showParms', 0, 0]}, 'REMOVEDISK': {'--showparms': ['showParms', 0, 0]}, 'REMOVEIPL': {'--showparms': ['showParms', 0, 0]}, 'VERSION': {}, } def add3390(rh): """ Adds a 3390 (ECKD) disk to a virtual machine's directory entry. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'ADD3390' userid - userid of the virtual machine parms['diskPool'] - Disk pool parms['diskSize'] - size of the disk in cylinders or bytes. parms['fileSystem'] - Linux filesystem to install on the disk. parms['mode'] - Disk access mode parms['multiPW'] - Multi-write password parms['readPW'] - Read password parms['vaddr'] - Virtual address parms['writePW'] - Write password Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.add3390") results, cyl = generalUtils.cvtToCyl(rh, rh.parms['diskSize']) if results['overallRC'] != 0: # message already sent. Only need to update the final results. rh.updateResults(results) if results['overallRC'] == 0: parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-t", "3390", "-a", "AUTOG", "-r", rh.parms['diskPool'], "-u", "1", "-z", cyl, "-f", "1"] hideList = [] if 'mode' in rh.parms: parms.extend(["-m", rh.parms['mode']]) else: parms.extend(["-m", 'W']) if 'readPW' in rh.parms: parms.extend(["-R", rh.parms['readPW']]) hideList.append(len(parms) - 1) if 'writePW' in rh.parms: parms.extend(["-W", rh.parms['writePW']]) hideList.append(len(parms) - 1) if 'multiPW' in rh.parms: parms.extend(["-M", rh.parms['multiPW']]) hideList.append(len(parms) - 1) results = invokeSMCLI(rh, "Image_Disk_Create_DM", parms, hideInLog=hideList) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results returned by invokeSMCLI if (results['overallRC'] == 0 and 'fileSystem' in rh.parms): results = installFS( rh, rh.parms['vaddr'], rh.parms['mode'], rh.parms['fileSystem'], "3390") if results['overallRC'] == 0: results = isLoggedOn(rh, rh.userid) if results['overallRC'] != 0: # Cannot determine if VM is logged on or off. # We have partially failed. Pass back the results. rh.updateResults(results) elif results['rs'] == 0: # Add the disk to the active configuration. parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-m", rh.parms['mode']] results = invokeSMCLI(rh, "Image_Disk_Create", parms) if results['overallRC'] == 0: rh.printLn("N", "Added dasd " + rh.parms['vaddr'] + " to the active configuration.") else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit changeVM.add3390, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def add9336(rh): """ Adds a 9336 (FBA) disk to virtual machine's directory entry. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'ADD9336' userid - userid of the virtual machine parms['diskPool'] - Disk pool parms['diskSize'] - size of the disk in blocks or bytes. parms['fileSystem'] - Linux filesystem to install on the disk. parms['mode'] - Disk access mode parms['multiPW'] - Multi-write password parms['readPW'] - Read password parms['vaddr'] - Virtual address parms['writePW'] - Write password Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.add9336") results, blocks = generalUtils.cvtToBlocks(rh, rh.parms['diskSize']) if results['overallRC'] != 0: # message already sent. Only need to update the final results. rh.updateResults(results) if results['overallRC'] == 0: parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-t", "9336", "-a", "AUTOG", "-r", rh.parms['diskPool'], "-u", "1", "-z", blocks, "-f", "1"] hideList = [] if 'mode' in rh.parms: parms.extend(["-m", rh.parms['mode']]) else: parms.extend(["-m", 'W']) if 'readPW' in rh.parms: parms.extend(["-R", rh.parms['readPW']]) hideList.append(len(parms) - 1) if 'writePW' in rh.parms: parms.extend(["-W", rh.parms['writePW']]) hideList.append(len(parms) - 1) if 'multiPW' in rh.parms: parms.extend(["-M", rh.parms['multiPW']]) hideList.append(len(parms) - 1) results = invokeSMCLI(rh, "Image_Disk_Create_DM", parms, hideInLog=hideList) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if (results['overallRC'] == 0 and 'fileSystem' in rh.parms): # Install the file system results = installFS( rh, rh.parms['vaddr'], rh.parms['mode'], rh.parms['fileSystem'], "9336") if results['overallRC'] == 0: results = isLoggedOn(rh, rh.userid) if (results['overallRC'] == 0 and results['rs'] == 0): # Add the disk to the active configuration. parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-m", rh.parms['mode']] results = invokeSMCLI(rh, "Image_Disk_Create", parms) if results['overallRC'] == 0: rh.printLn("N", "Added dasd " + rh.parms['vaddr'] + " to the active configuration.") else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit changeVM.add9336, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def dedicate(rh): """ Dedicate device. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'DEDICATEDM' userid - userid of the virtual machine parms['vaddr'] - Virtual address parms['raddr'] - Real address parms['mode'] - Read only mode or not. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.dedicate") parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-r", rh.parms['raddr'], "-R", rh.parms['mode']] hideList = [] results = invokeSMCLI(rh, "Image_Device_Dedicate_DM", parms, hideInLog=hideList) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['overallRC'] == 0: results = isLoggedOn(rh, rh.userid) if (results['overallRC'] == 0 and results['rs'] == 0): # Dedicate device to active configuration. parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-r", rh.parms['raddr'], "-R", rh.parms['mode']] results = invokeSMCLI(rh, "Image_Device_Dedicate", parms) if results['overallRC'] == 0: rh.printLn("N", "Dedicated device " + rh.parms['vaddr'] + " to the active configuration.") else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit changeVM.dedicate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def undedicate(rh): """ Unedicate device. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'UNDEDICATE' userid - userid of the virtual machine parms['vaddr'] - Virtual address Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.undedicate") parms = [ "-T", rh.userid, "-v", rh.parms['vaddr']] hideList = [] results = invokeSMCLI(rh, "Image_Device_Undedicate_DM", parms, hideInLog=hideList) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['overallRC'] == 0: results = isLoggedOn(rh, rh.userid) if (results['overallRC'] == 0 and results['rs'] == 0): # Dedicate device to active configuration. parms = [ "-T", rh.userid, "-v", rh.parms['vaddr']] results = invokeSMCLI(rh, "Image_Device_Undedicate", parms) if results['overallRC'] == 0: rh.printLn("N", "Dedicated device " + rh.parms['vaddr'] + " to the active configuration.") else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit changeVM.undedicate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def addAEMOD(rh): """ Send an Activation Modification Script to the virtual machine. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'AEMOD' userid - userid of the virtual machine parms['aeScript'] - File specification of the AE script parms['invparms'] - invparms operand Output: Request Handle updated with the results. Return code - 0: ok Return code - 4: input error, rs - 11 AE script not found """ rh.printSysLog("Enter changeVM.addAEMOD") invokeScript = "invokeScript.sh" trunkFile = "aemod.doscript" fileClass = "X" tempDir = tempfile.mkdtemp() if os.path.isfile(rh.parms['aeScript']): # Get the short name of our activation engine modifier script if rh.parms['aeScript'].startswith("/"): s = rh.parms['aeScript'] tmpAEScript = s[s.rindex("/") + 1:] else: tmpAEScript = rh.parms['aeScript'] # Copy the mod script to our temp directory shutil.copyfile(rh.parms['aeScript'], tempDir + "/" + tmpAEScript) # Create the invocation script. conf = "#!/bin/bash \n" baseName = os.path.basename(rh.parms['aeScript']) parm = "/bin/bash %s %s \n" % (baseName, rh.parms['invParms']) fh = open(tempDir + "/" + invokeScript, "w") fh.write(conf) fh.write(parm) fh.close() # Generate the tar package for punch tar = tarfile.open(tempDir + "/" + trunkFile, "w") for file in os.listdir(tempDir): tar.add(tempDir + "/" + file, arcname=file) tar.close() # Punch file to reader punch2reader(rh, rh.userid, tempDir + "/" + trunkFile, fileClass) shutil.rmtree(tempDir) else: # Worker script does not exist. shutil.rmtree(tempDir) msg = msgs.msg['0400'][1] % (modId, rh.parms['aeScript']) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0400'][0]) rh.printSysLog("Exit changeVM.addAEMOD, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def addIPL(rh): """ Sets the IPL statement in the virtual machine's directory entry. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'IPL' userid - userid of the virtual machine parms['addrOrNSS'] - Address or NSS name parms['loadparms'] - Loadparms operand (optional) parms['parms'] - Parms operand (optional) Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.addIPL") parms = ["-T", rh.userid, "-s", rh.parms['addrOrNSS']] if 'loadparms' in rh.parms: parms.extend(["-l", rh.parms['loadparms']]) if 'parms' in rh.parms: parms.extend(["-p", rh.parms['parms']]) results = invokeSMCLI(rh, "Image_IPL_Set_DM", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit changeVM.addIPL, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def addLOADDEV(rh): """ Sets the LOADDEV statement in the virtual machine's directory entry. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'ADDLOADDEV' userid - userid of the virtual machine parms['boot'] - Boot program number parms['addr'] - Logical block address of the boot record parms['lun'] - One to eight-byte logical unit number of the FCP-I/O device. parms['wwpn'] - World-Wide Port Number parms['scpDataType'] - SCP data type parms['scpData'] - Designates information to be passed to the program is loaded during guest IPL. Note that any of the parms may be left blank, in which case we will not update them. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.addLOADDEV") # scpDataType and scpData must appear or disappear concurrently if ('scpData' in rh.parms and 'scpDataType' not in rh.parms): msg = msgs.msg['0014'][1] % (modId, "scpData", "scpDataType") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0014'][0]) return if ('scpDataType' in rh.parms and 'scpData' not in rh.parms): if rh.parms['scpDataType'].lower() == "delete": scpDataType = 1 else: # scpDataType and scpData must appear or disappear # concurrently unless we're deleting data msg = msgs.msg['0014'][1] % (modId, "scpDataType", "scpData") rh.printLn("ES", msg) rh.updateResults(msgs.msg['0014'][0]) return scpData = "" if 'scpDataType' in rh.parms: if rh.parms['scpDataType'].lower() == "hex": scpData = rh.parms['scpData'] scpDataType = 3 elif rh.parms['scpDataType'].lower() == "ebcdic": scpData = rh.parms['scpData'] scpDataType = 2 # scpDataType not hex, ebcdic or delete elif rh.parms['scpDataType'].lower() != "delete": msg = msgs.msg['0016'][1] % (modId, rh.parms['scpDataType']) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0016'][0]) return else: # Not specified, 0 for do nothing scpDataType = 0 scpData = "" if 'boot' not in rh.parms: boot = "" else: boot = rh.parms['boot'] if 'addr' not in rh.parms: block = "" else: block = rh.parms['addr'] if 'lun' not in rh.parms: lun = "" else: lun = rh.parms['lun'] # Make sure it doesn't have the 0x prefix lun.replace("0x", "") if 'wwpn' not in rh.parms: wwpn = "" else: wwpn = rh.parms['wwpn'] # Make sure it doesn't have the 0x prefix wwpn.replace("0x", "") parms = [ "-T", rh.userid, "-b", boot, "-k", block, "-l", lun, "-p", wwpn, "-s", str(scpDataType)] if scpData != "": parms.extend(["-d", scpData]) results = invokeSMCLI(rh, "Image_SCSI_Characteristics_Define_DM", parms) # SMAPI API failed. if results['overallRC'] != 0: rh.printLn("ES", results['response']) rh.updateResults(results) rh.printSysLog("Exit changeVM.addLOADDEV, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: changeVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit changeVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for ChangeVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit changeVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) if rh.results['overallRC'] == 0: if rh.subfunction in ['ADD3390', 'ADD9336']: if ('fileSystem' in rh.parms and rh.parms['fileSystem'] not in ['ext2', 'ext3', 'ext4', 'xfs', 'swap']): # Invalid file system specified. msg = msgs.msg['0015'][1] % (modId, rh.parms['fileSystem']) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0015'][0]) rh.printSysLog("Exit changeVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def punchFile(rh): """ Punch a file to a virtual reader of the specified virtual machine. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'PUNCHFILE' userid - userid of the virtual machine parms['class'] - Spool class (optional) parms['file'] - Filespec of the file to punch. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.punchFile") # Default spool class in "A" , if specified change to specified class spoolClass = "A" if 'class' in rh.parms: spoolClass = str(rh.parms['class']) punch2reader(rh, rh.userid, rh.parms['file'], spoolClass) rh.printSysLog("Exit changeVM.punchFile, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def purgeRDR(rh): """ Purge the reader belonging to the virtual machine. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'PURGERDR' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.purgeRDR") results = purgeReader(rh) rh.updateResults(results) rh.printSysLog("Exit changeVM.purgeRDR, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def removeDisk(rh): """ Remove a disk from a virtual machine. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'REMOVEDISK' userid - userid of the virtual machine parms['vaddr'] - Virtual address Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.removeDisk") results = {'overallRC': 0, 'rc': 0, 'rs': 0} # Is image logged on loggedOn = False results = isLoggedOn(rh, rh.userid) if results['overallRC'] == 0: if results['rs'] == 0: loggedOn = True results = disableEnableDisk( rh, rh.userid, rh.parms['vaddr'], '-d') if results['overallRC'] != 0: rh.printLn("ES", results['response']) rh.updateResults(results) if results['overallRC'] == 0 and loggedOn: strCmd = "/sbin/vmcp detach " + rh.parms['vaddr'] results = execCmdThruIUCV(rh, rh.userid, strCmd) if results['overallRC'] != 0: if re.search('(^HCP\w\w\w040E)', results['response']): # Device does not exist, ignore the error results = {'overallRC': 0, 'rc': 0, 'rs': 0, 'response': ''} else: rh.printLn("ES", results['response']) rh.updateResults(results) if results['overallRC'] == 0: # Remove the disk from the user entry. parms = [ "-T", rh.userid, "-v", rh.parms['vaddr'], "-e", "0"] results = invokeSMCLI(rh, "Image_Disk_Delete_DM", parms) if results['overallRC'] != 0: if (results['overallRC'] == 8 and results['rc'] == 208 and results['rs'] == 36): # Disk does not exist, ignore the error results = {'overallRC': 0, 'rc': 0, 'rs': 0, 'response': ''} else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI else: # Unexpected error. Message already sent. rh.updateResults(results) rh.printSysLog("Exit changeVM.removeDisk, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def removeIPL(rh): """ Sets the IPL statement in the virtual machine's directory entry. Input: Request Handle with the following properties: function - 'CHANGEVM' subfunction - 'REMOVEIPL' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.removeIPL") parms = ["-T", rh.userid] results = invokeSMCLI(rh, "Image_IPL_Delete_DM", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit changeVM.removeIPL, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " ChangeVM add3390 ") rh.printLn("N", " --mode " + " --readpw ") rh.printLn("N", " --writepw " + "--multipw --filesystem ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM add9336 ") rh.printLn("N", " --mode " + " --readpw ") rh.printLn("N", " --writepw " + "--multipw --filesystem ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM aemod --invparms ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM IPL --loadparms ") rh.printLn("N", " --parms ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM loaddev --boot --addr ") rh.printLn("N", " --wwpn --lun " + "--scpdatatype --scpdata ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM punchFile --class ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM purgeRDR") rh.printLn("N", " python " + rh.cmdName + " ChangeVM removedisk ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM removeIPL ") rh.printLn("N", " python " + rh.cmdName + " ChangeVM help") rh.printLn("N", " python " + rh.cmdName + " ChangeVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the ChangeVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " add3390 - Add a 3390 (ECKD) disk " + "to a virtual machine's directory") rh.printLn("N", " entry.") rh.printLn("N", " add9336 - Add a 9336 (FBA) disk " + "to virtual machine's directory") rh.printLn("N", " entry.") rh.printLn("N", " aemod - Sends an activation " + "engine script to the managed virtual") rh.printLn("N", " machine.") rh.printLn("N", " help - Displays this help " + "information.") rh.printLn("N", " ipl - Sets the IPL statement in " + "the virtual machine's") rh.printLn("N", " directory entry.") rh.printLn("N", " loaddev - Sets the LOADDEV statement " + "in the virtual machine's") rh.printLn("N", " directory entry.") rh.printLn("N", " punchfile - Punch a file to a virtual " + "reader of the specified") rh.printLn("N", " virtual machine.") rh.printLn("N", " purgerdr - Purges the reader " + "belonging to the virtual machine.") rh.printLn("N", " removedisk - " + "Remove an mdisk from a virtual machine.") rh.printLn("N", " removeIPL - " + "Remove an IPL from a virtual machine's directory entry.") rh.printLn("N", " version - " + "show the version of the power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " -addr - " + "Specifies the logical block address of the") rh.printLn("N", " " + "boot record.") rh.printLn("N", " - " + "Specifies the virtual address or NSS name") rh.printLn("N", " to IPL.") rh.printLn("N", " - " + "aeScript is the fully qualified file") rh.printLn("N", " " + "specification of the script to be sent") rh.printLn("N", " --boot - " + "Boot program number") rh.printLn("N", " --class - " + "The class is optional and specifies the spool") rh.printLn("N", " " + "class for the reader file.") rh.printLn("N", " - " + "Specifies the directory manager disk pool to") rh.printLn("N", " " + "use to obtain the disk.") rh.printLn("N", " - " + "Specifies the size of the ECKD minidisk. ") rh.printLn("N", " - " + "Specifies the size of the FBA type minidisk.") rh.printLn("N", " - " + "File to punch to the target system.") rh.printLn("N", " --filesystem - " + "Specifies type of filesystem to be created on") rh.printLn("N", " the minidisk.") rh.printLn("N", " --invparms - " + "Specifies the parameters to be specified in the") rh.printLn("N", " " + "invocation script to call the aeScript.") rh.printLn("N", " --loadparms - " + "Specifies a 1 to 8-character load parameter that") rh.printLn("N", " " + "is used by the IPL'd system.") rh.printLn("N", " --lun - " + "One to eight-byte logical unit number of the") rh.printLn("N", " FCP-I/O device.") rh.printLn("N", " --mode - " + "Specifies the access mode for the minidisk.") rh.printLn("N", " --multipw - " + "Specifies the password that allows sharing the") rh.printLn("N", " " + "minidisk in multiple-write mode.") rh.printLn("N", " --parms - " + "Specifies a parameter string to pass to the") rh.printLn("N", " " + "virtual machine in general-purpose registers at") rh.printLn("N", " " + "user's the completion of the IPL.") rh.printLn("N", " --readpw - " + "Specifies the password that allows sharing the") rh.printLn("N", " " + "minidisk in read mode.") rh.printLn("N", " --scpdata - " + "Provides the SCP data information.") rh.printLn("N", " --scpdatatype - " + "Specifies whether the scp data is in hex,") rh.printLn("N", " " + "EBCDIC, or should be deleted.") rh.printLn("N", " - " + "Userid of the target virtual machine.") rh.printLn("N", " - " + "Virtual address of the device.") rh.printLn("N", " --writepw - " + "Specifies is the password that allows sharing") rh.printLn("N", " " + "the minidisk in write mode.") rh.printLn("N", " --wwpn - " + "The world-wide port number.") return zVMCloudConnector-1.4.1/smutLayer/getVM.py0000664000175000017510000004654713442676317020134 0ustar ruirui00000000000000# GetVM functions for Systems Management Ultra Thin Layer # # 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. import re import subprocess from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import execCmdThruIUCV, getPerfInfo, invokeSMCLI from smutLayer.vmUtils import isLoggedOn modId = 'GVM' version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'CONSOLEOUTPUT': ['getConsole', lambda rh: getConsole(rh)], 'DIRECTORY': ['getDirectory', lambda rh: getDirectory(rh)], 'HELP': ['help', lambda rh: help(rh)], 'ISREACHABLE': ['checkIsReachable', lambda rh: checkIsReachable(rh)], 'STATUS': ['getStatus', lambda rh: getStatus(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], 'FCPINFO': ['fcpinfo', lambda rh: fcpinfo(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = { 'FCPINFO': [ ['Status filter', 'status', True, 2], ]} """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) """ keyOpsList = { 'CONSOLEOUTPUT': {'--showparms': ['showParms', 0, 0]}, 'DIRECTORY': {'--showparms': ['showParms', 0, 0]}, 'HELP': {}, 'ISREACHABLE': {'--showparms': ['showParms', 0, 0]}, 'STATUS': { '--all': ['allBasic', 0, 0], '--cpu': ['cpu', 0, 0], '--memory': ['memory', 0, 0], '--power': ['power', 0, 0], '--showparms': ['showParms', 0, 0]}, 'VERSION': {}, } def checkIsReachable(rh): """ Check if a virtual machine is reachable. Input: Request Handle Output: Request Handle updated with the results. overallRC - 0: determined the status, non-zero: some weird failure while trying to execute a command on the guest via IUCV rc - RC returned from execCmdThruIUCV rs - 0: not reachable, 1: reachable """ rh.printSysLog("Enter getVM.checkIsReachable, userid: " + rh.userid) strCmd = "echo 'ping'" results = execCmdThruIUCV(rh, rh.userid, strCmd) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": reachable") reachable = 1 else: # A failure from execCmdThruIUCV is acceptable way of determining # that the system is unreachable. We won't pass along the # error message. rh.printLn("N", rh.userid + ": unreachable") reachable = 0 rh.updateResults({"rs": reachable}) rh.printSysLog("Exit getVM.checkIsReachable, rc: 0") return 0 def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: getVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit getVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getConsole(rh): """ Get the virtual machine's console output. Input: Request Handle with the following properties: function - 'CMDVM' subfunction - 'CMD' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getVM.getConsole") # Transfer the console to this virtual machine. parms = ["-T", rh.userid] results = invokeSMCLI(rh, "Image_Console_Get", parms) if results['overallRC'] != 0: if (results['overallRC'] == 8 and results['rc'] == 8 and results['rs'] == 8): # Give a more specific message. Userid is either # not logged on or not spooling their console. msg = msgs.msg['0409'][1] % (modId, rh.userid) else: msg = results['response'] rh.updateResults(results) # Use results from invokeSMCLI rh.printLn("ES", msg) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] # Check whether the reader is online with open('/sys/bus/ccw/drivers/vmur/0.0.000c/online', 'r') as myfile: out = myfile.read().replace('\n', '') myfile.close() # Nope, offline, error out and exit if int(out) != 1: msg = msgs.msg['0411'][1] rh.printLn("ES", msg) rh.updateResults(msgs.msg['0411'][0]) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] # We should set class to *, otherwise we will get errors like: # vmur: Reader device class does not match spool file class cmd = ["sudo", "/sbin/vmcp", "spool reader class *"] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: # If we couldn't change the class, that's not fatal # But we want to warn about possibly incomplete # results msg = msgs.msg['0407'][1] % (modId, strCmd, e.output) rh.printLn("WS", msg) except Exception as e: # All other exceptions. # If we couldn't change the class, that's not fatal # But we want to warn about possibly incomplete # results rh.printLn("ES", msgs.msg['0422'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.printLn("ES", msgs.msg['0423'][1] % modId, strCmd, type(e).__name__, str(e)) # List the spool files in the reader cmd = ["sudo", "/usr/sbin/vmur", "list"] strCmd = ' '.join(cmd) rh.printSysLog("Invoking: " + strCmd) try: files = subprocess.check_output( cmd, close_fds=True, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as e: # Uh oh, vmur list command failed for some reason msg = msgs.msg['0408'][1] % (modId, rh.userid, strCmd, e.output) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0408'][0]) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] except Exception as e: # All other exceptions. rh.printLn("ES", msgs.msg['0421'][1] % (modId, strCmd, type(e).__name__, str(e))) rh.updateResults(msgs.msg['0421'][0]) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] # Now for each line that contains our user and is a # class T console file, add the spool id to our list spoolFiles = files.split('\n') outstr = "" for myfile in spoolFiles: if (myfile != "" and myfile.split()[0] == rh.userid and myfile.split()[2] == "T" and myfile.split()[3] == "CON"): fileId = myfile.split()[1] outstr += fileId + " " # No files in our list if outstr == "": msg = msgs.msg['0410'][1] % (modId, rh.userid) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0410'][0]) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] # Output the list rh.printLn("N", "List of spool files containing " "console logs from %s: %s" % (rh.userid, outstr)) rh.results['overallRC'] = 0 rh.printSysLog("Exit getVM.getConsole, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getDirectory(rh): """ Get the virtual machine's directory statements. Input: Request Handle with the following properties: function - 'CMDVM' subfunction - 'CMD' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getVM.getDirectory") parms = ["-T", rh.userid] results = invokeSMCLI(rh, "Image_Query_DM", parms) if results['overallRC'] == 0: results['response'] = re.sub('\*DVHOPT.*', '', results['response']) rh.printLn("N", results['response']) else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit getVM.getDirectory, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getStatus(rh): """ Get the basic status of a virtual machine. Input: Request Handle with the following properties: function - 'CMDVM' subfunction - 'CMD' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getVM.getStatus, userid: " + rh.userid) results = isLoggedOn(rh, rh.userid) if results['rc'] != 0: # Uhoh, can't determine if guest is logged on or not rh.updateResults(results) rh.printSysLog("Exit getVM.getStatus, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] if results['rs'] == 1: # Guest is logged off, everything is 0 powerStr = "Power state: off" memStr = "Total Memory: 0M" usedMemStr = "Used Memory: 0M" procStr = "Processors: 0" timeStr = "CPU Used Time: 0 sec" else: powerStr = "Power state: on" if 'power' in rh.parms: # Test here to see if we only need power state # Then we can return early rh.printLn("N", powerStr) rh.updateResults(results) rh.printSysLog("Exit getVM.getStatus, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] if results['rs'] != 1: # Guest is logged on, go get more info results = getPerfInfo(rh, rh.userid) if results['overallRC'] != 0: # Something went wrong in subroutine, exit rh.updateResults(results) rh.printSysLog("Exit getVM.getStatus, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] else: # Everything went well, response should be good memStr = results['response'].split("\n")[0] usedMemStr = results['response'].split("\n")[1] procStr = results['response'].split("\n")[2] timeStr = results['response'].split("\n")[3] # Build our output string according # to what information was asked for if 'memory' in rh.parms: outStr = memStr + "\n" + usedMemStr elif 'cpu' in rh.parms: outStr = procStr + "\n" + timeStr else: # Default to all outStr = powerStr + "\n" + memStr + "\n" + usedMemStr outStr += "\n" + procStr + "\n" + timeStr rh.printLn("N", outStr) rh.printSysLog("Exit getVM.getStatus, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for GetVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter getVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) rh.printSysLog("Exit getVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " GetVM [ consoleoutput | directory | isreachable ]") rh.printLn("N", " python " + rh.cmdName + " GetVM ") rh.printLn("N", " " + "status [ --all | --cpu | --memory | --power ]") rh.printLn("N", " python " + rh.cmdName + " GetVM help") rh.printLn("N", " python " + rh.cmdName + " GetVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the GetVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " consoleoutput - " + "Obtains the console log from the virtual machine.") rh.printLn("N", " directory - " + "Displays the user directory lines for the virtual machine.") rh.printLn("N", " help - " + "Displays this help information.") rh.printLn("N", " isreachable - " + "Determine whether the virtual OS in a virtual machine") rh.printLn("N", " is reachable") rh.printLn("N", " status - " + "show the log on/off status of the virtual machine") rh.printLn("N", " version - " + "show the version of the power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " - " + "Userid of the target virtual machine") rh.printLn("N", " [ --all | --cpu | " + "--memory | --power ]") rh.printLn("N", " - " + "Returns information machine related to the number") rh.printLn("N", " " + "of virtual CPUs, memory size, power status or all of the") rh.printLn("N", " information.") return def extract_fcp_data(raw_data, status): """ extract data from smcli System_WWPN_Query output. Input: raw data returned from smcli Output: data extracted would be like: 'status:Free \n fcp_dev_no:1D2F\n physical_wwpn:C05076E9928051D1\n channel_path_id:8B\n npiv_wwpn': 'NONE'\n status:Free\n fcp_dev_no:1D29\n physical_wwpn:C05076E9928051D1\n channel_path_id:8B\n npiv_wwpn:NONE """ raw_data = raw_data.split('\n') # clear blank lines data = [] for i in raw_data: i = i.strip(' \n') if i == '': continue else: data.append(i) # process data into one list of dicts results = [] for i in range(0, len(data), 5): temp = data[i + 1].split(':')[-1].strip() # only return results match the status if temp.lower() == status.lower(): results.extend(data[i:i + 5]) return '\n'.join(results) def fcpinfo(rh): """ Get fcp info and filter by the status. Input: Request Handle with the following properties: function - 'GETVM' subfunction - 'FCPINFO' userid - userid of the virtual machine parms['status'] - The status for filter results. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter changeVM.dedicate") parms = ["-T", rh.userid] hideList = [] results = invokeSMCLI(rh, "System_WWPN_Query", parms, hideInLog=hideList) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['overallRC'] == 0: # extract data from smcli return ret = extract_fcp_data(results['response'], rh.parms['status']) # write the ret into results['response'] rh.printLn("N", ret) else: rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI return rh.results['overallRC'] zVMCloudConnector-1.4.1/smutLayer/smutCmd.py0000664000175000017510000000406513442676317020513 0ustar ruirui00000000000000#!/usr/bin/env python # Command line processor for Systems Management Ultra Thin Layer # # 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. import sys from smutLayer.smut import SMUT from smutLayer.ReqHandle import ReqHandle version = '1.0.0' # Version of this script """ ****************************************************************************** main routine ****************************************************************************** """ useSMUT = True if useSMUT: results = SMUT(cmdName=sys.argv[0]).request(sys.argv[1:], captureLogs=True) else: reqHandle = ReqHandle(cmdName=sys.argv[0], captureLogs=True) results = reqHandle.parseCmdline(sys.argv[1:]) if results['overallRC'] == 0: results = reqHandle.driveFunction() # On error, show the result codes (overall rc, rc, rs, ...) if results['overallRC'] != 0: print("overall rc: " + str(results['overallRC'])) print(" rc: " + str(results['rc'])) print(" rs: " + str(results['rs'])) print(" errno: " + str(results['errno'])) print(" strError: " + str(results['strError'])) print("") print("Response:") # Show the response lines if len(results['response']) != 0: for line in results['response']: print(line) elif results['overallRC'] == 0: print("Command succeeded.") # On error, show the trace log. if results['overallRC'] != 0: print("") print("Trace Log:") for line in results['logEntries']: print(line) if results['overallRC'] != 0: exit(results['overallRC']) zVMCloudConnector-1.4.1/smutLayer/tests/0000775000175000017510000000000013442723341017650 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/smutLayer/tests/__init__.py0000664000175000017510000000000013442676317021761 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/smutLayer/tests/unit/0000775000175000017510000000000013442723341020627 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/smutLayer/tests/unit/__init__.py0000664000175000017510000000000013442676317022740 0ustar ruirui00000000000000zVMCloudConnector-1.4.1/smutLayer/tests/unit/test_makeVM.py0000664000175000017510000000461513442676317023440 0ustar ruirui00000000000000# Copyright 2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from smutLayer import makeVM from smutLayer import ReqHandle from smutLayer.tests.unit import base class SMUTMakeVMTestCase(base.SMUTTestCase): """Test cases for makeVM.py in smutLayer.""" def test_getReservedMemSize(self): rh = mock.Mock() rh.results = {'overallRC': 0, 'rc': 0, 'rs': 0} gap = makeVM.getReservedMemSize(rh, '1024M', '128g') self.assertEqual(gap, '130048M') def test_getReservedMemSize_invalid_suffix(self): rh = ReqHandle.ReqHandle(captureLogs=False, smut=mock.Mock()) gap = makeVM.getReservedMemSize(rh, '1024M', '128T') self.assertEqual(gap, '0M') self.assertEqual(rh.results['overallRC'], 4) self.assertEqual(rh.results['rc'], 4) self.assertEqual(rh.results['rs'], 205) def test_getReservedMemSize_max_less_than_initial(self): rh = ReqHandle.ReqHandle(captureLogs=False, smut=mock.Mock()) gap = makeVM.getReservedMemSize(rh, '64G', '32G') self.assertEqual(gap, '0M') self.assertEqual(rh.results['overallRC'], 4) self.assertEqual(rh.results['rc'], 4) self.assertEqual(rh.results['rs'], 206) def test_getReservedMemSize_equal_size(self): rh = ReqHandle.ReqHandle(captureLogs=False, smut=mock.Mock()) gap = makeVM.getReservedMemSize(rh, '1024M', '1G') self.assertEqual(gap, '0M') self.assertEqual(rh.results['overallRC'], 0) def test_getReservedMemSize_gap_G(self): rh = ReqHandle.ReqHandle(captureLogs=False, smut=mock.Mock()) gap = makeVM.getReservedMemSize(rh, '512m', '9999G') self.assertEqual(gap, '9998G') self.assertEqual(rh.results['overallRC'], 0) zVMCloudConnector-1.4.1/smutLayer/tests/unit/base.py0000664000175000017510000000132313442676317022124 0ustar ruirui00000000000000# Copyright 2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # 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 unittest class SMUTTestCase(unittest.TestCase): def setUp(self): super(SMUTTestCase, self).setUp() zVMCloudConnector-1.4.1/smutLayer/tests/unit/test_vmUtils.py0000664000175000017510000000424113442676317023716 0ustar ruirui00000000000000# -*- coding: utf-8 # Copyright 2018 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from smutLayer import vmUtils from smutLayer import ReqHandle from smutLayer.tests.unit import base class SMUTvmUtilsTestCase(base.SMUTTestCase): """Test cases for vmUtils.py in smutLayer.""" def test_getVM_directory_py3(self): rh = ReqHandle.ReqHandle(captureLogs=False) with mock.patch('subprocess.check_output') as exec_cmd: # subprocess.check_output returns bytes in py3 exec_cmd.return_value = ( b"0 0 0 (details) None\n" b"USER T9572493 LBYONLY 2048m 64G G\nINCLUDE ZCCDFLT\n" b"COMMAND DEF STOR RESERVED 63488M\n" b"CPU 00 BASE\nIPL 0100\nLOGONBY MAINT\nMACHINE ESA 32\n" b"MDISK 0100 3390 48697 5500 OMB1B6 MR\n" b"*DVHOPT LNK0 LOG1 RCM1 SMS0 NPW1 LNGAMENG PWC20180808 " b"CRC\xf3:\n") expected_resp = ( u"USER T9572493 LBYONLY 2048m 64G G\nINCLUDE ZCCDFLT\n" u"COMMAND DEF STOR RESERVED 63488M\nCPU 00 BASE\nIPL 0100\n" u"LOGONBY MAINT\nMACHINE ESA 32\n" u"MDISK 0100 3390 48697 5500 OMB1B6 MR\n" u"*DVHOPT LNK0 LOG1 RCM1 SMS0 NPW1 LNGAMENG PWC20180808 " u"CRC\ufffd:\n") res = vmUtils.invokeSMCLI(rh, "Image_Query_DM", ['-T', 'fakeuid']) self.assertEqual(res['response'], expected_resp) exec_cmd.assert_called_once_with( ['sudo', '/opt/zthin/bin/smcli', 'Image_Query_DM', '--addRCheader', '-T', 'fakeuid'], close_fds=True) zVMCloudConnector-1.4.1/smutLayer/powerVM.py0000664000175000017510000007455613442676317020512 0ustar ruirui00000000000000# Power functions for Systems Management Ultra Thin Layer # # 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. import time from smutLayer import generalUtils from smutLayer import msgs from smutLayer.vmUtils import execCmdThruIUCV, invokeSMCLI from smutLayer.vmUtils import isLoggedOn from smutLayer.vmUtils import waitForOSState, waitForVMState modId = 'PVM' vmOSUpStates = ['on', 'up'] vmOSUpDownStates = ['down', 'off', 'on', 'up'] version = "1.0.0" """ List of subfunction handlers. Each subfunction contains a list that has: Readable name of the routine that handles the subfunction, Code for the function call. """ subfuncHandler = { 'HELP': ['help', lambda rh: help(rh)], 'ISREACHABLE': ['checkIsReachable', lambda rh: checkIsReachable(rh)], 'OFF': ['deactivate', lambda rh: deactivate(rh)], 'ON': ['activate', lambda rh: activate(rh)], 'PAUSE': ['pause', lambda rh: pause(rh)], 'REBOOT': ['reboot', lambda rh: reboot(rh)], 'RESET': ['reset', lambda rh: reset(rh)], 'SOFTOFF': ['softDeactivate', lambda rh: softDeactivate(rh)], 'STATUS': ['getStatus', lambda rh: getStatus(rh)], 'UNPAUSE': ['unpause', lambda rh: unpause(rh)], 'VERSION': ['getVersion', lambda rh: getVersion(rh)], 'WAIT': ['wait', lambda rh: wait(rh)], } """ List of positional operands based on subfunction. Each subfunction contains a list which has a dictionary with the following information for the positional operands: - Human readable name of the operand, - Property in the parms dictionary to hold the value, - Is it required (True) or optional (False), - Type of data (1: int, 2: string). """ posOpsList = {} """ List of additional operands/options supported by the various subfunctions. The dictionary followng the subfunction name uses the keyword from the command as a key. Each keyword has a dictionary that lists: - the related parms item that stores the value, - how many values follow the keyword, and - the type of data for those values (1: int, 2: string) For example, the 'WAIT' subfunction has a 'poll' operand that takes one additional operand (time in seconds) which is an int. While the 'showparms' operand is just the keyword and has no additional value portion. """ keyOpsList = { 'HELP': {}, 'ISREACHABLE': { '--showparms': ['showParms', 0, 0]}, 'OFF': { '--maxwait': ['maxWait', 1, 1], '--poll': ['poll', 1, 1], '--showparms': ['showParms', 0, 0], '--wait': ['wait', 0, 0]}, 'ON': { '--state': ['desiredState', 1, 2], '--maxwait': ['maxWait', 1, 1], '--poll': ['poll', 1, 1], '--showparms': ['showParms', 0, 0], '--wait': ['wait', 0, 0]}, 'PAUSE': {'--showparms': ['showParms', 0, 0]}, 'REBOOT': { '--maxwait': ['maxWait', 1, 1], '--poll': ['poll', 1, 1], '--showparms': ['showParms', 0, 0], '--wait': ['wait', 0, 0]}, 'RESET': { '--state': ['desiredState', 1, 2], '--maxwait': ['maxWait', 1, 1], '--poll': ['poll', 1, 1], '--showparms': ['showParms', 0, 0], '--wait': ['wait', 0, 0]}, 'SOFTOFF': { '--maxwait': ['maxWait', 1, 1], '--poll': ['poll', 1, 1], '--showparms': ['showParms', 0, 0], '--wait': ['wait', 0, 0]}, 'STATUS': { '--showparms': ['showParms', 0, 0] }, 'UNPAUSE': { '--showparms': ['showParms', 0, 0]}, 'VERSION': {}, 'WAIT': { '--maxwait': ['maxWait', 1, 1], '--poll': ['poll', 1, 1], '--showparms': ['showParms', 0, 0], '--state': ['desiredState', 1, 2]}, } def activate(rh): """ Activate a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'ON' userid - userid of the virtual machine parms['desiredState'] - Desired state. Optional, unless 'maxQueries' is specified. parms['maxQueries'] - Maximum number of queries to issue. Optional. parms['maxWait'] - Maximum time to wait in seconds. Optional, unless 'maxQueries' is specified. parms['poll'] - Polling interval in seconds. Optional, unless 'maxQueries' is specified. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.activate, userid: " + rh.userid) parms = ["-T", rh.userid] smcliResults = invokeSMCLI(rh, "Image_Activate", parms) if smcliResults['overallRC'] == 0: pass elif (smcliResults['overallRC'] == 8 and smcliResults['rc'] == 200 and smcliResults['rs'] == 8): pass # All good. No need to change the ReqHandle results. else: # SMAPI API failed. rh.printLn("ES", smcliResults['response']) rh.updateResults(smcliResults) # Use results from invokeSMCLI if rh.results['overallRC'] == 0 and 'maxQueries' in rh.parms: # Wait for the system to be in the desired state of: # OS is 'up' and reachable or VM is 'on'. if rh.parms['desiredState'] == 'up': results = waitForOSState( rh, rh.userid, rh.parms['desiredState'], maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) else: results = waitForVMState( rh, rh.userid, rh.parms['desiredState'], maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) if results['overallRC'] == 0: rh.printLn("N", "%s: %s" % (rh.userid, rh.parms['desiredState'])) else: rh.updateResults(results) rh.printSysLog("Exit powerVM.activate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def checkIsReachable(rh): """ Check if a virtual machine is reachable. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'ISREACHABLE' userid - userid of the virtual machine Output: Request Handle updated with the results. overallRC - 0: determined the status, non-zero: some weird failure while trying to execute a command on the guest via IUCV rc - RC returned from execCmdThruIUCV rs - 0: not reachable, 1: reachable """ rh.printSysLog("Enter powerVM.checkIsReachable, userid: " + rh.userid) strCmd = "echo 'ping'" results = execCmdThruIUCV(rh, rh.userid, strCmd) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": reachable") reachable = 1 else: # A failure from execCmdThruIUCV is acceptable way of determining # that the system is unreachable. We won't pass along the # error message. rh.printLn("N", rh.userid + ": unreachable") reachable = 0 rh.updateResults({"rs": reachable}) rh.printSysLog("Exit powerVM.checkIsReachable, rc: 0") return 0 def deactivate(rh): """ Deactivate a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'OFF' userid - userid of the virtual machine parms['maxQueries'] - Maximum number of queries to issue. Optional. parms['maxWait'] - Maximum time to wait in seconds. Optional, unless 'maxQueries' is specified. parms['poll'] - Polling interval in seconds. Optional, unless 'maxQueries' is specified. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.deactivate, userid: " + rh.userid) parms = ["-T", rh.userid, "-f", "IMMED"] results = invokeSMCLI(rh, "Image_Deactivate", parms) if results['overallRC'] == 0: pass elif (results['overallRC'] == 8 and results['rc'] == 200 and (results['rs'] == 12 or results['rs'] == 16)): # Tolerable error. Machine is already in or going into the state # we want it to enter. rh.printLn("N", rh.userid + ": off") rh.updateResults({}, reset=1) else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['overallRC'] == 0 and 'maxQueries' in rh.parms: results = waitForVMState( rh, rh.userid, 'off', maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": off") else: rh.updateResults(results) rh.printSysLog("Exit powerVM.deactivate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def doIt(rh): """ Perform the requested function by invoking the subfunction handler. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.doIt") # Show the invocation parameters, if requested. if 'showParms' in rh.parms and rh.parms['showParms'] is True: rh.printLn("N", "Invocation parameters: ") rh.printLn("N", " Routine: powerVM." + str(subfuncHandler[rh.subfunction][0]) + "(reqHandle)") rh.printLn("N", " function: " + rh.function) rh.printLn("N", " userid: " + rh.userid) rh.printLn("N", " subfunction: " + rh.subfunction) rh.printLn("N", " parms{}: ") for key in rh.parms: if key != 'showParms': rh.printLn("N", " " + key + ": " + str(rh.parms[key])) rh.printLn("N", " ") # Call the subfunction handler subfuncHandler[rh.subfunction][1](rh) rh.printSysLog("Exit powerVM.doIt, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getStatus(rh): """ Get the power (logon/off) status of a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'STATUS' userid - userid of the virtual machine Output: Request Handle updated with the results. results['overallRC'] - 0: ok, non-zero: error if ok: results['rc'] - 0: for both on and off cases results['rs'] - 0: powered on results['rs'] - 1: powered off """ rh.printSysLog("Enter powerVM.getStatus, userid: " + rh.userid) results = isLoggedOn(rh, rh.userid) if results['overallRC'] != 0: # Unexpected error pass elif results['rs'] == 0: rh.printLn("N", rh.userid + ": on") else: rh.printLn("N", rh.userid + ": off") rh.updateResults(results) rh.printSysLog("Exit powerVM.getStatus, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def getVersion(rh): """ Get the version of this function. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printLn("N", "Version: " + version) return 0 def help(rh): """ Produce help output specifically for PowerVM functions. Input: Request Handle Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ showInvLines(rh) showOperandLines(rh) return 0 def parseCmdline(rh): """ Parse the request command input. Input: Request Handle Output: Request Handle updated with parsed input. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.parseCmdline") if rh.totalParms >= 2: rh.userid = rh.request[1].upper() else: # Userid is missing. msg = msgs.msg['0010'][1] % modId rh.printLn("ES", msg) rh.updateResults(msgs.msg['0010'][0]) rh.printSysLog("Exit powerVM.parseCmdLine, rc: " + rh.results['overallRC']) return rh.results['overallRC'] if rh.totalParms == 2: rh.subfunction = rh.userid rh.userid = '' if rh.totalParms >= 3: rh.subfunction = rh.request[2].upper() # Verify the subfunction is valid. if rh.subfunction not in subfuncHandler: # Subfunction is missing. subList = ', '.join(sorted(subfuncHandler.keys())) msg = msgs.msg['0011'][1] % (modId, subList) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0011'][0]) # Parse the rest of the command line. if rh.results['overallRC'] == 0: rh.argPos = 3 # Begin Parsing at 4th operand generalUtils.parseCmdline(rh, posOpsList, keyOpsList) waiting = 0 if rh.results['overallRC'] == 0: if rh.subfunction == 'WAIT': waiting = 1 if rh.parms['desiredState'] not in vmOSUpDownStates: # Desired state is not: down, off, on or up. msg = msgs.msg['0013'][1] % (modId, rh.parms['desiredState'], ", ".join(vmOSUpDownStates)) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0013'][0]) if (rh.results['overallRC'] == 0 and 'wait' in rh.parms): waiting = 1 if 'desiredState' not in rh.parms: if rh.subfunction in ['ON', 'RESET', 'REBOOT']: rh.parms['desiredState'] = 'up' else: # OFF and SOFTOFF default to 'off'. rh.parms['desiredState'] = 'off' if rh.results['overallRC'] == 0 and waiting == 1: if rh.subfunction == 'ON' or rh.subfunction == 'RESET': if ('desiredState' not in rh.parms or rh.parms['desiredState'] not in vmOSUpStates): # Desired state is not: on or up. msg = msgs.msg['0013'][1] % (modId, rh.parms['desiredState'], ", ".join(vmOSUpStates)) rh.printLn("ES", msg) rh.updateResults(msgs.msg['0013'][0]) if rh.results['overallRC'] == 0: if 'maxWait' not in rh.parms: rh.parms['maxWait'] = 300 if 'poll' not in rh.parms: rh.parms['poll'] = 15 rh.parms['maxQueries'] = (rh.parms['maxWait'] + rh.parms['poll'] - 1) / rh.parms['poll'] # If we had to do some rounding, give a warning # out to the command line user that the wait # won't be what they expected. if rh.parms['maxWait'] % rh.parms['poll'] != 0: msg = msgs.msg['0017'][1] % (modId, rh.parms['maxWait'], rh.parms['poll'], rh.parms['maxQueries'] * rh.parms['poll'], rh.parms['maxQueries']) rh.printLn("W", msg) rh.printSysLog("Exit powerVM.parseCmdLine, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def pause(rh): """ Pause a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'PAUSE' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.pause, userid: " + rh.userid) parms = ["-T", rh.userid, "-k", "PAUSE=YES"] results = invokeSMCLI(rh, "Image_Pause", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit powerVM.pause, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def reboot(rh): """ Reboot a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'REBOOT' userid - userid of the virtual machine parms['desiredState'] - Desired state. Optional, unless 'maxQueries' is specified. parms['maxQueries'] - Maximum number of queries to issue. Optional. parms['maxWait'] - Maximum time to wait in seconds. Optional, unless 'maxQueries' is specified. parms['poll'] - Polling interval in seconds. Optional, unless 'maxQueries' is specified. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.reboot, userid: " + rh.userid) strCmd = "shutdown -r now" results = execCmdThruIUCV(rh, rh.userid, strCmd) if results['overallRC'] != 0: # Command failed to execute using IUCV. rh.printLn("ES", results['response']) rh.updateResults(results) if rh.results['overallRC'] == 0: # Wait for the OS to go down results = waitForOSState(rh, rh.userid, "down", maxQueries=30, sleepSecs=10) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": down (interim state)") if rh.results['overallRC'] == 0 and 'maxQueries' in rh.parms: results = waitForOSState(rh, rh.userid, 'up', maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": up") else: rh.updateResults(results) rh.printSysLog("Exit powerVM.reboot, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def reset(rh): """ Reset a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'RESET' userid - userid of the virtual machine parms['maxQueries'] - Maximum number of queries to issue. Optional. parms['maxWait'] - Maximum time to wait in seconds. Optional, unless 'maxQueries' is specified. parms['poll'] - Polling interval in seconds. Optional, unless 'maxQueries' is specified. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.reset, userid: " + rh.userid) # Log off the user parms = ["-T", rh.userid] results = invokeSMCLI(rh, "Image_Deactivate", parms) if results['overallRC'] != 0: if results['rc'] == 200 and results['rs'] == 12: # Tolerated error. Machine is already in the desired state. results['overallRC'] = 0 results['rc'] = 0 results['rs'] = 0 else: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI # Wait for the logoff to complete if results['overallRC'] == 0: results = waitForVMState(rh, rh.userid, "off", maxQueries=30, sleepSecs=10) # Log the user back on if results['overallRC'] == 0: parms = ["-T", rh.userid] results = invokeSMCLI(rh, "Image_Activate", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI if results['overallRC'] == 0 and 'maxQueries' in rh.parms: if rh.parms['desiredState'] == 'up': results = waitForOSState( rh, rh.userid, rh.parms['desiredState'], maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) else: results = waitForVMState( rh, rh.userid, rh.parms['desiredState'], maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": " + rh.parms['desiredState']) else: rh.updateResults(results) rh.printSysLog("Exit powerVM.reset, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def showInvLines(rh): """ Produce help output related to command synopsis Input: Request Handle """ if rh.subfunction != '': rh.printLn("N", "Usage:") rh.printLn("N", " python " + rh.cmdName + " PowerVM ") rh.printLn("N", " [isreachable | pause | " + "status | unpause]") rh.printLn("N", " python " + rh.cmdName + " PowerVM ") rh.printLn("N", " [on | reset] --wait --state " + "[on | up] --maxwait ") rh.printLn("N", " --poll ") rh.printLn("N", " python " + rh.cmdName + " PowerVM ") rh.printLn("N", " [off | reboot | softoff] " + "--maxwait --poll ") rh.printLn("N", " python " + rh.cmdName + " PowerVM " + " wait") rh.printLn("N", " --state [down | on | off | up] " + "--maxwait ") rh.printLn("N", " --poll ") rh.printLn("N", " python " + rh.cmdName + " PowerVM help") rh.printLn("N", " python " + rh.cmdName + " PowerVM version") return def showOperandLines(rh): """ Produce help output related to operands. Input: Request Handle """ if rh.function == 'HELP': rh.printLn("N", " For the PowerVM function:") else: rh.printLn("N", "Sub-Functions(s):") rh.printLn("N", " help - Displays this help " + "information.") rh.printLn("N", " isreachable - Determine whether the " + "virtual OS in a virtual machine") rh.printLn("N", " is reachable") rh.printLn("N", " on - Log on the virtual machine") rh.printLn("N", " off - Log off the virtual machine") rh.printLn("N", " pause - Pause a virtual machine") rh.printLn("N", " reboot - Issue a shutdown command to " + "reboot the OS in a virtual") rh.printLn("N", " machine") rh.printLn("N", " reset - Power a virtual machine off " + "and then back on") rh.printLn("N", " softoff - Issue a shutdown command to " + "shutdown the OS in a virtual") rh.printLn("N", " machine and then log the " + "virtual machine off z/VM.") rh.printLn("N", " status - show the log on/off status " + "of the virtual machine") rh.printLn("N", " unpause - Unpause a virtual machine") rh.printLn("N", " wait - Wait for the virtual machine " + "to go into the specified") rh.printLn("N", " state of either:") rh.printLn("N", " down: virtual machine's " + "OS is not reachable with IUCV") rh.printLn("N", " off: virtual machine is " + "logged off") rh.printLn("N", " on: virtual machine is " + "logged on") rh.printLn("N", " up: virtual machine's OS " + "is reachable with IUCV") rh.printLn("N", " version - show the version of the " + "power function") if rh.subfunction != '': rh.printLn("N", "Operand(s):") rh.printLn("N", " - Userid of the target " + "virtual machine") rh.printLn("N", " --maxwait - " + "Maximum time in seconds to wait") rh.printLn("N", " --poll - " + "Seconds to wait between polling") rh.printLn("N", " --state [down | off | on | up] - " + "Desired state for virtual machine") rh.printLn("N", " (on or off) or for the operating " + "system (down or up).") rh.printLn("N", " --wait - wait for the machine to go into " + "the desired state.") return def softDeactivate(rh): """ Deactivate a virtual machine by first shutting down Linux and then log it off. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'SOFTOFF' userid - userid of the virtual machine parms['maxQueries'] - Maximum number of queries to issue. Optional. parms['maxWait'] - Maximum time to wait in seconds. Optional, unless 'maxQueries' is specified. parms['poll'] - Polling interval in seconds. Optional, unless 'maxQueries' is specified. Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.softDeactivate, userid: " + rh.userid) strCmd = "echo 'ping'" iucvResults = execCmdThruIUCV(rh, rh.userid, strCmd) if iucvResults['overallRC'] == 0: # We could talk to the machine, tell it to shutdown nicely. strCmd = "shutdown -h now" iucvResults = execCmdThruIUCV(rh, rh.userid, strCmd) if iucvResults['overallRC'] == 0: time.sleep(15) else: # Shutdown failed. Let CP take down the system # after we log the results. rh.printSysLog("powerVM.softDeactivate " + rh.userid + " is unreachable. Treating it as already shutdown.") else: # Could not ping the machine. Treat it as a success # after we log the results. rh.printSysLog("powerVM.softDeactivate " + rh.userid + " is unreachable. Treating it as already shutdown.") # Tell z/VM to log off the system. parms = ["-T", rh.userid] smcliResults = invokeSMCLI(rh, "Image_Deactivate", parms) if smcliResults['overallRC'] == 0: pass elif (smcliResults['overallRC'] == 8 and smcliResults['rc'] == 200 and (smcliResults['rs'] == 12 or + smcliResults['rs'] == 16)): # Tolerable error. # Machine is already logged off or is logging off. rh.printLn("N", rh.userid + " is already logged off.") else: # SMAPI API failed. rh.printLn("ES", smcliResults['response']) rh.updateResults(smcliResults) # Use results from invokeSMCLI if rh.results['overallRC'] == 0 and 'maxQueries' in rh.parms: # Wait for the system to log off. waitResults = waitForVMState( rh, rh.userid, 'off', maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) if waitResults['overallRC'] == 0: rh.printLn("N", "Userid '" + rh.userid + " is in the desired state: off") else: rh.updateResults(waitResults) rh.printSysLog("Exit powerVM.softDeactivate, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def unpause(rh): """ Unpause a virtual machine. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'UNPAUSE' userid - userid of the virtual machine Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.unpause, userid: " + rh.userid) parms = ["-T", rh.userid, "-k", "PAUSE=NO"] results = invokeSMCLI(rh, "Image_Pause", parms) if results['overallRC'] != 0: # SMAPI API failed. rh.printLn("ES", results['response']) rh.updateResults(results) # Use results from invokeSMCLI rh.printSysLog("Exit powerVM.unpause, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] def wait(rh): """ Wait for the virtual machine to go into the specified state. Input: Request Handle with the following properties: function - 'POWERVM' subfunction - 'WAIT' userid - userid of the virtual machine parms['desiredState'] - Desired state parms['maxQueries'] - Maximum number of queries to issue parms['maxWait'] - Maximum time to wait in seconds parms['poll'] - Polling interval in seconds Output: Request Handle updated with the results. Return code - 0: ok, non-zero: error """ rh.printSysLog("Enter powerVM.wait, userid: " + rh.userid) if (rh.parms['desiredState'] == 'off' or rh.parms['desiredState'] == 'on'): results = waitForVMState( rh, rh.userid, rh.parms['desiredState'], maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) else: results = waitForOSState( rh, rh.userid, rh.parms['desiredState'], maxQueries=rh.parms['maxQueries'], sleepSecs=rh.parms['poll']) if results['overallRC'] == 0: rh.printLn("N", rh.userid + ": " + rh.parms['desiredState']) else: rh.updateResults(results) rh.printSysLog("Exit powerVM.wait, rc: " + str(rh.results['overallRC'])) return rh.results['overallRC'] zVMCloudConnector-1.4.1/smutLayer/smut.py0000664000175000017510000000715513442676317020072 0ustar ruirui00000000000000# Daemon for Systems Management Ultra Thin Layer # # 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. from time import time from smutLayer.ReqHandle import ReqHandle from zvmsdk import config from zvmsdk import log version = '1.0.0' # Version of this function. class SMUT(object): """ Systems Management Ultra Thin daemon. """ def __init__(self, **kwArgs): """ Constructor Input: cmdName= - Specifies the name of the command that drives SMUT. captureLogs= Enables or disables log capture for all requests. """ self.reqIdPrefix = int(time() * 100) self.reqCnt = 0 # Number of requests so far logger = log.Logger('SMUT') logger.setup(log_dir=config.CONF.logging.log_dir, log_level='logging.DEBUG', log_file_name='smut.log') self.logger = logger.getlog() # Initialize the command name associated with this SMUT instance. if 'cmdName' in kwArgs.keys(): self.cmdName = kwArgs['cmdName'] else: self.cmdName = "" # Setup the log capture flag. if 'captureLogs' in kwArgs.keys(): self.captureLogs = kwArgs['captureLogs'] else: self.captureLogs = False # Don't capture & return Syslog entries def disableLogCapture(self): """ Disable capturing of log entries for all requests. """ self.captureLogs = False # Don't capture Syslog entries def enableLogCapture(self): """ Enable capturing of log entries for all requests. """ self.captureLogs = True # Begin capturing & returning Syslog entries def request(self, requestData, **kwArgs): """ Process a request. Input: Request as either a string or a list. captureLogs= Enables or disables log capture per request. This overrides the value from SMUT. requestId= to pass a value for the request Id instead of using one generated by SMUT. Output: Dictionary containing the results. See ReqHandle.buildReturnDict() for information on the contents of the dictionary. """ self.reqCnt = self.reqCnt + 1 # Determine whether the request will be capturing logs if 'captureLogs' in kwArgs.keys(): logFlag = kwArgs['captureLogs'] else: logFlag = self.captureLogs # Pass along or generate a request Id if 'requestId' in kwArgs.keys(): requestId = kwArgs['requestId'] else: requestId = str(self.reqIdPrefix) + str(self.reqCnt) rh = ReqHandle( requestId=requestId, captureLogs=logFlag, smut=self) rh.parseCmdline(requestData) if rh.results['overallRC'] == 0: rh.printSysLog("Processing: " + rh.requestString) rh.driveFunction() return rh.results zVMCloudConnector-1.4.1/smutLayer/ReqHandle.py0000664000175000017510000003364513442676317020750 0ustar ruirui00000000000000# Request Handle for Systems Management Ultra Thin Layer # # 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. import logging import logging.handlers import shlex from six import string_types from smutLayer import changeVM from smutLayer import cmdVM from smutLayer import deleteVM from smutLayer import getHost from smutLayer import getVM from smutLayer import makeVM from smutLayer import migrateVM from smutLayer import msgs from smutLayer import smapi from smutLayer import powerVM from zvmsdk import log as zvmsdklog modId = "RQH" version = '1.0.0' # Version of this script class ReqHandle(object): """ Systems Management Ultra Thin Layer Request Handle. This class contains all information related to a specific request. All functions are passed this request handle. """ funcHandler = { 'CHANGEVM': [ lambda rh: changeVM.showInvLines(rh), lambda rh: changeVM.showOperandLines(rh), lambda rh: changeVM.parseCmdline(rh), lambda rh: changeVM.doIt(rh)], 'CMDVM': [ lambda rh: cmdVM.showInvLines(rh), lambda rh: cmdVM.showOperandLines(rh), lambda rh: cmdVM.parseCmdline(rh), lambda rh: cmdVM.doIt(rh)], 'DELETEVM': [ lambda rh: deleteVM.showInvLines(rh), lambda rh: deleteVM.showOperandLines(rh), lambda rh: deleteVM.parseCmdline(rh), lambda rh: deleteVM.doIt(rh)], 'GETHOST': [ lambda rh: getHost.showInvLines(rh), lambda rh: getHost.showOperandLines(rh), lambda rh: getHost.parseCmdline(rh), lambda rh: getHost.doIt(rh)], 'GETVM': [ lambda rh: getVM.showInvLines(rh), lambda rh: getVM.showOperandLines(rh), lambda rh: getVM.parseCmdline(rh), lambda rh: getVM.doIt(rh)], 'MAKEVM': [ lambda rh: makeVM.showInvLines(rh), lambda rh: makeVM.showOperandLines(rh), lambda rh: makeVM.parseCmdline(rh), lambda rh: makeVM.doIt(rh)], 'MIGRATEVM': [ lambda rh: migrateVM.showInvLines(rh), lambda rh: migrateVM.showOperandLines(rh), lambda rh: migrateVM.parseCmdline(rh), lambda rh: migrateVM.doIt(rh)], 'POWERVM': [ lambda rh: powerVM.showInvLines(rh), lambda rh: powerVM.showOperandLines(rh), lambda rh: powerVM.parseCmdline(rh), lambda rh: powerVM.doIt(rh)], 'SMAPI': [ lambda rh: smapi.showInvLines(rh), lambda rh: smapi.showOperandLines(rh), lambda rh: smapi.parseCmdline(rh), lambda rh: smapi.doIt(rh)], } def __init__(self, **kwArgs): """ Constructor Input: captureLogs= Enables or disables log capture for all requests. cmdName= Name of the command that is using ReqHandle. This is only used for the function help. It defaults to "smutCmd.py". requestId=requestId Optional request Id smut= SMUT daemon, it it exists. """ self.results = { 'overallRC': 0, # Overall return code for the function, e.g. # 0 - Everything went ok # 2 - Something in the IUCVCLNT failed # 3 - Something in a local vmcp failed # 4 - Input validation error # 5 - Miscellaneous processing error # 8 - SMCLI - SMAPI failure # 24 - SMCLI - Parsing failure # 25 - SMCLI - Internal Processing Error # 99 - Unexpected failure 'rc': 0, # Return code causing the return 'rs': 0, # Reason code causing the return 'errno': 0, # Errno value causing the return 'strError': '', # Error as a string value. # Normally, this is the errno description. 'response': [], # Response strings 'logEntries': [], # Syslog entries related to this request } if 'smut' in kwArgs.keys(): self.daemon = kwArgs['smut'] # SMUT Daemon # Actual SysLog handling is done in SMUT. else: self.daemon = '' # Set up SysLog handling to be done by ReqHandle self.logger = logging.getLogger(__name__) self.logger.setLevel(logging.DEBUG) self.handler = logging.handlers.SysLogHandler(address = '/dev/log') self.formatter = ( logging.Formatter('%(module)s.%(funcName)s: %(message)s')) self.handler.setFormatter(self.formatter) self.logger.addHandler(self.handler) if 'cmdName' in kwArgs.keys(): self.cmdName = kwArgs['cmdName'] else: self.cmdName = 'smutCmd.py' if 'requestId' in kwArgs.keys(): self.requestId = kwArgs['requestId'] else: self.requestId = 'REQ_' + hex(id(self))[2:] # Need to generate a default request Id self.function = '' # Function being processed self.subfunction = '' # Subfunction be processed (optional) self.userid = '' # Target userid self.parms = {} # Dictionary of additional parms self.argPos = 0 # Prep to parse first command line arg # Capture & return Syslog entries if 'captureLogs' in kwArgs.keys(): self.captureLogs = kwArgs['captureLogs'] else: self.captureLogs = False def driveFunction(self): """ Drive the function/subfunction call. Input: Self with request filled in. Output: Request Handle updated with the results. Overall return code - 0: successful, non-zero: error """ if self.function == 'HELP': # General help for all functions. self.printLn("N", "") self.printLn("N", "Usage:") self.printLn("N", " python " + self.cmdName + " --help") for key in sorted(ReqHandle.funcHandler): ReqHandle.funcHandler[key][0](self) self.printLn("N", "") self.printLn("N", "Operand(s):") for key in sorted(ReqHandle.funcHandler): ReqHandle.funcHandler[key][1](self) self.printLn("N", "") self.updateResults({}, reset=1) elif self.function == 'VERSION': # Version of ReqHandle. self.printLn("N", "Version: " + version) self.updateResults({}, reset=1) else: # Some type of function/subfunction invocation. if self.function in self.funcHandler: # Invoke the functions doIt routine to route to the # appropriate subfunction. self.funcHandler[self.function][3](self) else: # Unrecognized function msg = msgs.msg['0007'][1] % (modId, self.function) self.printLn("ES", msg) self.updateResults(msgs.msg['0007'][0]) return self.results def parseCmdline(self, requestData): """ Parse the request command string. Input: Self with request filled in. Output: Request Handle updated with the parsed information so that it is accessible via key/value pairs for later processing. Return code - 0: successful, non-zero: error """ self.printSysLog("Enter ReqHandle.parseCmdline") # Save the request data based on the type of operand. if isinstance(requestData, list): self.requestString = ' '.join(requestData) # Request as a string self.request = requestData # Request as a list elif isinstance(requestData, string_types): self.requestString = requestData # Request as a string self.request = shlex.split(requestData) # Request as a list else: # Request data type is not supported. msg = msgs.msg['0012'][1] % (modId, type(requestData)) self.printLn("ES", msg) self.updateResults(msgs.msg['0012'][0]) return self.results self.totalParms = len(self.request) # Number of parms in the cmd # Handle the request, parse it or return an error. if self.totalParms == 0: # Too few arguments. msg = msgs.msg['0009'][1] % modId self.printLn("ES", msg) self.updateResults(msgs.msg['0009'][0]) elif self.totalParms == 1: self.function = self.request[0].upper() if self.function == 'HELP' or self.function == 'VERSION': pass else: # Function is not HELP or VERSION. msg = msgs.msg['0008'][1] % (modId, self.function) self.printLn("ES", msg) self.updateResults(msgs.msg['0008'][0]) else: # Process based on the function operand. self.function = self.request[0].upper() if self.request[0] == 'HELP' or self.request[0] == 'VERSION': pass else: # Handle the function related parms by calling the function # parser. if self.function in ReqHandle.funcHandler: self.funcHandler[self.function][2](self) else: # Unrecognized function msg = msgs.msg['0007'][1] % (modId, self.function) self.printLn("ES", msg) self.updateResults(msgs.msg['0007'][0]) self.printSysLog("Exit ReqHandle.parseCmdline, rc: " + str(self.results['overallRC'])) return self.results def printLn(self, respType, respString): """ Add one or lines of output to the response list. Input: Response type: One or more characters indicate type of response. E - Error message N - Normal message S - Output should be logged W - Warning message """ if 'E' in respType: respString = '(Error) ' + respString if 'W' in respType: respString = '(Warning) ' + respString if 'S' in respType: self.printSysLog(respString) self.results['response'] = (self.results['response'] + respString.splitlines()) return def printSysLog(self, logString): """ Log one or more lines. Optionally, add them to logEntries list. Input: Strings to be logged. """ if zvmsdklog.LOGGER.getloglevel() <= logging.DEBUG: # print log only when debug is enabled if self.daemon == '': self.logger.debug(self.requestId + ": " + logString) else: self.daemon.logger.debug(self.requestId + ": " + logString) if self.captureLogs is True: self.results['logEntries'].append(self.requestId + ": " + logString) return def updateResults(self, newResults, **kwArgs): """ Update the results related to this request excluding the 'response' and 'logEntries' values. We specifically update (if present): overallRC, rc, rs, errno. Input: Dictionary containing the results to be updated or an empty dictionary the reset keyword was specified. Reset keyword: 0 - Not a reset. This is the default is reset keyword was not specified. 1 - Reset failure related items in the result dictionary. This exclude responses and log entries. 2 - Reset all result items in the result dictionary. Output: Request handle is updated with the results. """ if 'reset' in kwArgs.keys(): reset = kwArgs['reset'] else: reset = 0 if reset == 0: # Not a reset. Set the keys from the provided dictionary. for key in newResults.keys(): if key == 'response' or key == 'logEntries': continue self.results[key] = newResults[key] elif reset == 1: # Reset all failure related items. self.results['overallRC'] = 0 self.results['rc'] = 0 self.results['rs'] = 0 self.results['errno'] = 0 self.results['strError'] = '' elif reset == 2: # Reset all results information including any responses and # log entries. self.results['overallRC'] = 0 self.results['rc'] = 0 self.results['rs'] = 0 self.results['errno'] = 0 self.results['strError'] = '' self.results['logEntries'] = '' self.results['response'] = '' return zVMCloudConnector-1.4.1/doc/0000775000175000017510000000000013442723341015266 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/doc/source/0000775000175000017510000000000013442723341016566 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/doc/source/zvmsdk.conf.sample0000664000175000017510000002664413442676324022257 0ustar ruirui00000000000000[database] # # Directory to store database. # # SDK databases are used to store a set of tables which contain the # information of network, volume, image, etc. This option is used to # tell SDK where to store the database files, make sure the process # running SDK is able to read write and execute the directory. # # This param is optional #dir=/var/lib/zvmsdk/databases/ [file] # # Directory to store sdk imported or exported files. # # SDK file repository to store the imported files and the files that will be # exported, the imported files will be put into /imported # the files to be exported will be put into /exported # # This param is optional #file_repository=/var/lib/zvmsdk/files [guest] # # The maximum allowed console log size, in kilobytes. # # Console logs might be transferred to sdk user, this option 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. # # This param is optional #console_log_size=100 [image] # # Directory to store sdk images. # # SDK image repository to store the imported images and the staging images that # is in snapshotting. Once snapshot finished, the image will be removed to the # netboot directory accordingly. Two kinds of image repository looks like: # /var/lib/zvmsdk/images/netboot// # /var/lib/zvmsdk/images/staging// # # This param is optional #sdk_image_repository=/var/lib/zvmsdk/images [logging] # # Directory where log file to be put into. # # SDK has a set of logs to help administrator to debug # and aduit actions performed through SDK. Edit this option # if you want to put logs into specified place. # # Please ensure the service running on the consume which # consumes SDK has the authorization to write to the path. # # This param is optional #log_dir=/var/log/zvmsdk/ # # Level of the log. # # SDK utilize python logging package to help admin debug # or analyze issues. it's recommend to set this value # to logging.DEBUG to get more detailed logs and set it to # logging.INFO(default) in normal situation. # # recommend values: # logging.ERROR: level above ERROR will be written to log file. # logging.WARNINS: level above WARNING(ERROR, WARNING) # will be written to log file. # logging.INFO: level above INFO(ERROR, WARNING, INFO) # will be written to log file. # logging.DEBUG: All log level (ERROR, WARNING, INFO, DEBUG) # will be written to log file. # # This param is optional #log_level=logging.INFO [monitor] # # Cached monitor data update interval # # This is used to prevent excessive effort spent retrieving the # monitor data by calling the SDK backend utilities. When this cache # is enabled, a inspect call will only call the SDK backend utilities # when the inspected guest's info does not exist in the cache or # when the cache data is expired. And when an cache update is needed, # all the existing guests' data will be retrieved in a single call to # the backend. # # When this value is below or equal to zero, the cache # will be disabled and each inspect call will need to call the backend # utilities to get the inspected guest's monitor data. # # This param is optional #cache_interval=300 [network] # # IP address of the Linux machine which is running SDK on. # # Some remote copy operations need to be performed during guest creation, # this option tell the SDK the host ip which can be used to perform copy # from and copy to operations. # # This param is required #my_ip=None [sdkserver] # # The IP address that the SDK server is listen on. # # When the SDK server deamon starts, it will try to bind to # this address and port bind_port, and wait for the SDK client # connection to handle API request. # # This param is optional #bind_addr=127.0.0.1 # # The port that the SDK server is listen on. # # This will work as a pair with bind_addr when the SDK server daemon # starts, more info can be found in that configuration description. # # This param is optional #bind_port=2000 # # The maximum number of worker thread in SDK server to handle client requests. # # These worker threads would work concurrently to handle requests from client. # This value should be adjusted according to the system resource and workload. # # This param is optional #max_worker_count=64 # # The size of request queue in SDK server. # # SDK server maintains a queue to keep all the accepted but not handled requests, # and the SDK server workers fetch requests from this queue. # To some extend, this queue size decides the max socket opened in SDK server. # This value should be adjusted according to the system resource. # # This param is optional #request_queue_size=128 [volume] # # volume fcp list. # # SDK will only use the fcp devices in the scope of this value. # # This param is optional #fcp_list= [wsgi] # # Whether auth will be used. # # When sending http request from outside to running zvmsdk, # Client will be requested to input username/password in order # to authorize the call. # Set this to 'none' indicated no auth will be used and 'auth' # means username and password need to be specified. # # Possible value: # 'none': no auth will be required # 'auth': need auth, currently pyjwt is used to return a token # to caller if the username and password is correct. # # This param is optional #auth=none # # The max total number of concurrent deploy and capture requests allowed in a # single z/VM Cloud Connector process. # # If more requests than this value are revieved concurrently, the z/VM Cloud # Connector would reject the requests and return error to avoid resource # exhaustion. # . # # This param is optional #max_concurrent_deploy_capture=20 # # file path that contains admin-token to access sdk http server. # # Admin-token in order to get a user-token from zvm sdk, and the user-token # will be used to validate request before user-token expire. # # This param is optional #token_path=/etc/zvmsdk/token.dat # # How long the token is valid. # # If a token auth is used, the token return to user will be # expired after the period passed. This ensure an user who # get this token will not be authorized all the time, a new # token need to be recreated after certain time period. # # This param is optional #token_validation_period=3600 [zvm] # # 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 SDK 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 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. # # This param is optional #default_admin_userid=None # # Virtual device number for default NIC address. # # This value is the first NIC virtual device number, # each NIC needs 3 numbers for control/read/write, so by default # the first NIC's address is 1000, the second one is 1003 etc. # # Possible values: # An integer value in hex format, between 0 and 65536 (x'FFFF'). # It should not conflict with other device numbers in the z/VM guest's # configuration, for example device numbers of the root or ephemeral or # persistent disks. # # Sample NIC definitions in the z/VM user directory: # NICDEF 1000 TYPE QDIO LAN SYSTEM MACID # NICDEF 1003 TYPE QDIO LAN SYSTEM MACID # # This param is optional #default_nic_vdev=1000 # # zVM disk pool and type for root/ephemeral disks. # # The option is combined by 2 parts and use : as separator. # # The first part is the type of disks in the disk pool. # The disks in one disk pool must in same type (ECKD or FBA). # Possible values of the disk pool type: # A string, either ECKD or FBA. # # The second part is the volume group name defined in your directory manager # on your z/VM system, which will be used for allocating disks for # new guest. A dollar sign ($) is not allowed in the name. # # Sample disk_pool values: # ECKD:diskpo1 # FBA:testpool # # This param is required #disk_pool=None # # The name of a list containing names of virtual servers to be queried. The list # which contains the userid list by default is named: VSMWORK1 NAMELIST, see # DMSSICNF COPY key: NameListFileIdAny. The list has to be accessible to the # SMAPI servers. # # The length of namelist must no longer than 64. # # This param is optional #namelist=None # # The port number of remotehost sshd. # # This param is optional #remotehost_sshd_port=22 # # The default maximum number of virtual processers the user can define. # This value is used as the default value for maximum vcpu number when # create a guest with no max_cpu specified. # # The number must be a decimal value between 1 and 64. # # This param is optional #user_default_max_cpu=32 # # The default maximum size of memory the user can define. # This value is used as the default value for maximum memory size when # create a guest with no max_mem specified. # The value can be specified by 1-4 bits of number suffixed by either # M (Megabytes) or G (Gigabytes) and the number must be a whole number, # values such as 4096.8M or 32.5G are not supported. # # The value should be adjusted based on your system capacity. # # This param is optional #user_default_max_memory=64G # This param is optional #user_default_password=None # # PROFILE name to use when creating a z/VM guest. # # When SDK deploys an guest 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. # # This param is required #user_profile=None # # Virtual device number for root disk. # # When SDK deploys an guest, it creates a root disk and potentially # several data disks. This value is the virtual device number of the root # disk. # # 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 # # This param is optional #user_root_vdev=0100 zVMCloudConnector-1.4.1/CONTRIBUTING.md0000664000175000017510000000411713371225174016757 0ustar ruirui00000000000000### Welcome We welcome contributions to python-zvm-sdk! ### Repository The repository for python-zvm-sdk on GitHub: https://github.com/mfcloud/python-zvm-sdk ### Reporting bugs If you are a user and you find a bug, please submit a [bug](https://bugs.launchpad.net/python-zvm-sdk). Please try to provide sufficient information for someone else to reproduce the issue. One of the project's maintainers should respond to your issue within 24 hours. If not, please bump the issue and request that it be reviewed. ### Fixing bugs Review the [bug list](https://bugs.launchpad.net/python-zvm-sdk) and find something that interests you. We are using the [GerritHub](https://review.gerrithub.io/) process to manage code contributions. To work on something, whether a new feature or a bugfix: 1. Clone python-zvm-sdk locally ``` git clone https://github.com/mfcloud/python-zvm-sdk.git ``` 2. Add the GerritHub repository as a remote as gerrit ``` git remote add gerrit ssh://@review.gerrithub.io:29418/mfcloud/python-zvm-sdk ``` Where is your GerritHub username. And, you should add the public key of your workstation into your GerritHub SSH public keys. 3. Create a branch Create a descriptively-named branch off of your cloned repository ``` cd python-zvm-sdk git checkout -b fix-bug-xxxx ``` 4. Commit your code Commit to that branch locally 5. Commit messages Commit messages must have a short description no longer than 50 characters followed by a blank line and a longer, more descriptive message that includes reference to issue(s) being addressed so that they will be automatically closed on a merge e.g. ```Closes #1234``` or ```Fixes #1234```. 6. Run checks Run checks via issue: ``` tox -v ``` 7. Once all checks passed, you can submit your change for review: ``` git review ``` 8. Any code changes that affect documentation should be accompanied by corresponding changes (or additions) to the documentation and tests. This will ensure that if the merged PR is reversed, all traces of the change will be reversed as well. zVMCloudConnector-1.4.1/tox.ini0000664000175000017510000000211613442676317016046 0ustar ruirui00000000000000[tox] minversion = 1.6 envlist = pep8,py{27,35,36},docs skipsdist = True [testenv] usedevelop = True install_command = pip install -U {opts} {packages} setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python -m unittest discover -v -s {toxinidir}/zvmsdk/tests/unit python -m unittest discover -v -s {toxinidir}/smutLayer/tests/unit [testenv:pep8] deps = flake8 commands = flake8 flake8 {toxinidir}/scripts/sdkserver [flake8] ignore = E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E251,H405,W504,W605 exclude = .venv,.git,.tox,dist,doc,*openstack/common*,sample,*lib/python*,*egg,build,tools,*.py.*.py [testenv:docs] whitelist_externals = rm commands = rm -rf doc/build python tools/generate_conf.py python tools/generate_errcode_csv.py rm -fr doc/source/zvmsdk.conf.sample rm -fr doc/source/errcode.csv /bin/mv ./zvmsdk.conf.sample doc/source/zvmsdk.conf.sample /bin/mv ./errcode.csv doc/source/errcode.csv sphinx-build -W -b html -d doc/build/doctrees doc/source doc/build/html zVMCloudConnector-1.4.1/MANIFEST.in0000664000175000017510000000020113442676317016262 0ustar ruirui00000000000000include LICENSE include README.rst include CONTRIBUTING.md include requirements.txt include test-requirements.txt include tox.inizVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/0000775000175000017510000000000013442723341021571 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/requires.txt0000664000175000017510000000014213442723341024166 0ustar ruirui00000000000000jsonschema>=2.3.0 netaddr>=0.7.5 PyJWT>=1.0.1 requests>=2.6.0 Routes>=2.2 six>=1.9.0 WebOb>=1.2.3 zVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/top_level.txt0000664000175000017510000000003613442723341024322 0ustar ruirui00000000000000smutLayer zvmconnector zvmsdk zVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/PKG-INFO0000664000175000017510000000410013442723341022661 0ustar ruirui00000000000000Metadata-Version: 1.1 Name: zVMCloudConnector Version: 1.4.1 Summary: z/VM cloud management library in Python Home-page: https://github.com/mfcloud/python-zvm-sdk Author: IBM Author-email: UNKNOWN License: ASL 2.0 Description: z/VM Cloud Connector ******************** Description =========== z/VM cloud connector is a development sdk for manage z/VM. It provides a set of APIs to operate z/VM including guest, image, network, volume etc. Just like os-win for nova hyperv driver and oslo.vmware for nova vmware driver, z/VM cloud connector (zVMCloudConnector) is for nova z/vm driver and other z/VM related openstack driver such as neutron, ceilometer. Quickstart ========== Please refer to `Quick Start Guide `_. Documentation ============= Please refer to `Documentation of z/VM Cloud Connector `_. License ======= This package is licensed under the `Apache 2.0 License`_. .. _Apache 2.0 License: https://raw.githubusercontent.com/zhmcclient/python-zhmcclient/master/LICENSE Bug reporting ============= If you encounter any problem with this package, please open a bug against `cloud connector issue tracker`_ .. _cloud connector issue tracker: https://bugs.launchpad.net/python-zvm-sdk/+bug Keywords: zvm cloud library Platform: UNKNOWN Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 zVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/dependency_links.txt0000664000175000017510000000000113442723341025637 0ustar ruirui00000000000000 zVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/entry_points.txt0000664000175000017510000000007713442723341025073 0ustar ruirui00000000000000[wsgi_scripts] sdk_api = zvmsdk.sdkwsgi.wsgi:init_application zVMCloudConnector-1.4.1/zVMCloudConnector.egg-info/SOURCES.txt0000664000175000017510000000626713442723341023470 0ustar ruirui00000000000000CONTRIBUTING.md LICENSE MANIFEST.in README.rst requirements.txt setup.py test-requirements.txt tox.ini data/sdkserver.service data/setupDisk data/sudoers-zvmsdk data/uwsgi-zvmsdk.conf doc/source/zvmsdk.conf.sample scripts/sdkserver scripts/zvmsdk-gentoken smutLayer/ReqHandle.py smutLayer/__init__.py smutLayer/changeVM.py smutLayer/cmdVM.py smutLayer/deleteVM.py smutLayer/generalUtils.py smutLayer/getHost.py smutLayer/getVM.py smutLayer/makeVM.py smutLayer/migrateVM.py smutLayer/msgs.py smutLayer/powerVM.py smutLayer/smapi.py smutLayer/smut.py smutLayer/smutCmd.py smutLayer/smutTest.py smutLayer/vmUtils.py smutLayer/tests/__init__.py smutLayer/tests/unit/__init__.py smutLayer/tests/unit/base.py smutLayer/tests/unit/test_makeVM.py smutLayer/tests/unit/test_vmUtils.py zVMCloudConnector.egg-info/PKG-INFO zVMCloudConnector.egg-info/SOURCES.txt zVMCloudConnector.egg-info/dependency_links.txt zVMCloudConnector.egg-info/entry_points.txt zVMCloudConnector.egg-info/requires.txt zVMCloudConnector.egg-info/top_level.txt zvmconnector/__init__.py zvmconnector/connector.py zvmconnector/restclient.py zvmconnector/socketclient.py zvmsdk/__init__.py zvmsdk/api.py zvmsdk/config.py zvmsdk/configdrive.py zvmsdk/constants.py zvmsdk/database.py zvmsdk/dist.py zvmsdk/exception.py zvmsdk/hostops.py zvmsdk/imageops.py zvmsdk/log.py zvmsdk/monitor.py zvmsdk/networkops.py zvmsdk/returncode.py zvmsdk/sdkserver.py zvmsdk/smutclient.py zvmsdk/utils.py zvmsdk/version.py zvmsdk/vmops.py zvmsdk/volumeop.py zvmsdk/sdkwsgi/__init__.py zvmsdk/sdkwsgi/deploy.py zvmsdk/sdkwsgi/handler.py zvmsdk/sdkwsgi/requestlog.py zvmsdk/sdkwsgi/util.py zvmsdk/sdkwsgi/zvmsdk-wsgi zvmsdk/sdkwsgi/handlers/__init__.py zvmsdk/sdkwsgi/handlers/file.py zvmsdk/sdkwsgi/handlers/guest.py zvmsdk/sdkwsgi/handlers/host.py zvmsdk/sdkwsgi/handlers/image.py zvmsdk/sdkwsgi/handlers/tokens.py zvmsdk/sdkwsgi/handlers/version.py zvmsdk/sdkwsgi/handlers/volume.py zvmsdk/sdkwsgi/handlers/vswitch.py zvmsdk/sdkwsgi/schemas/__init__.py zvmsdk/sdkwsgi/schemas/guest.py zvmsdk/sdkwsgi/schemas/image.py zvmsdk/sdkwsgi/schemas/volume.py zvmsdk/sdkwsgi/schemas/vswitch.py zvmsdk/sdkwsgi/validation/__init__.py zvmsdk/sdkwsgi/validation/parameter_types.py zvmsdk/tests/__init__.py zvmsdk/tests/unit/__init__.py zvmsdk/tests/unit/base.py zvmsdk/tests/unit/test_api.py zvmsdk/tests/unit/test_config.py zvmsdk/tests/unit/test_database.py zvmsdk/tests/unit/test_dist.py zvmsdk/tests/unit/test_hostops.py zvmsdk/tests/unit/test_imageops.py zvmsdk/tests/unit/test_monitor.py zvmsdk/tests/unit/test_networkops.py zvmsdk/tests/unit/test_smutclient.py zvmsdk/tests/unit/test_utils.py zvmsdk/tests/unit/test_vmops.py zvmsdk/tests/unit/test_volumeop.py zvmsdk/tests/unit/sdkclientcases/__init__.py zvmsdk/tests/unit/sdkclientcases/test_restclient.py zvmsdk/tests/unit/sdkwsgi/__init__.py zvmsdk/tests/unit/sdkwsgi/test_handler.py zvmsdk/tests/unit/sdkwsgi/test_utils.py zvmsdk/tests/unit/sdkwsgi/handlers/__init__.py zvmsdk/tests/unit/sdkwsgi/handlers/test_guest.py zvmsdk/tests/unit/sdkwsgi/handlers/test_host.py zvmsdk/tests/unit/sdkwsgi/handlers/test_image.py zvmsdk/tests/unit/sdkwsgi/handlers/test_version.py zvmsdk/tests/unit/sdkwsgi/handlers/test_volume.py zvmsdk/tests/unit/sdkwsgi/handlers/test_vswitch.pyzVMCloudConnector-1.4.1/README.rst0000664000175000017510000000211013442676317016214 0ustar ruirui00000000000000z/VM Cloud Connector ******************** Description =========== z/VM cloud connector is a development sdk for manage z/VM. It provides a set of APIs to operate z/VM including guest, image, network, volume etc. Just like os-win for nova hyperv driver and oslo.vmware for nova vmware driver, z/VM cloud connector (zVMCloudConnector) is for nova z/vm driver and other z/VM related openstack driver such as neutron, ceilometer. Quickstart ========== Please refer to `Quick Start Guide `_. Documentation ============= Please refer to `Documentation of z/VM Cloud Connector `_. License ======= This package is licensed under the `Apache 2.0 License`_. .. _Apache 2.0 License: https://raw.githubusercontent.com/zhmcclient/python-zhmcclient/master/LICENSE Bug reporting ============= If you encounter any problem with this package, please open a bug against `cloud connector issue tracker`_ .. _cloud connector issue tracker: https://bugs.launchpad.net/python-zvm-sdk/+bug zVMCloudConnector-1.4.1/requirements.txt0000664000175000017510000000022313371225174020004 0ustar ruirui00000000000000jsonschema>=2.3.0 # MIT netaddr>=0.7.5 # BSD PyJWT>=1.0.1 # MIT requests>=2.6.0 # Apache-2.0 Routes>=2.2 # MIT six>=1.9.0 # MIT WebOb>=1.2.3 # MIT zVMCloudConnector-1.4.1/scripts/0000775000175000017510000000000013442723341016210 5ustar ruirui00000000000000zVMCloudConnector-1.4.1/scripts/zvmsdk-gentoken0000775000175000017510000000550213407042050021256 0ustar ruirui00000000000000#!/bin/python import getopt import os import random import stat import string import sys DEFAULT_TOKEN_PATH = '/etc/zvmsdk/token.dat' def usage(): print('Usage: zvmsdk-gentoken [-h] [-u] [token file path]') print('') print('Tool to Generate and Update Token') print('') print('Optional aruments:') print(' -h, --help\t\tshow this help message and exit') print(' -u, --update\tgenerate a random token and update into file') print(' \t\t\tif path not assigned, use default /etc/zvmsdk/token.dat.') def random_token(): # random token only include upper cases and digits token_random = ''.join( random.choice(string.ascii_uppercase +\ string.ascii_lowercase +\ string.digits) for _ in range(42)) return token_random def create_token_file(file_path): #if file not exits, create it if os.path.exists(file_path): print('Token File Already Existed!') print('') sys.exit(2) # create token file os.mknod(file_path) # generate random token token = random_token() # write token data with open(file_path, 'w') as fp: fp.write(token) # change file mode to 400 os.chmod(file_path, stat.S_IRUSR|stat.S_IWUSR) def update_token_file(file_path): #if file not exits, raise exception if not os.path.exists(file_path): print('Token File Not Exist!\n') print('') sys.exit(3) token = random_token() # write token data with open(file_path, 'w') as fp: fp.write(token) def main(argv): try: opts, args = getopt.gnu_getopt(argv, 'hu', ["help", "update"]) # if none arguments and options means initialization if opts == [] and args == []: create_token_file(DEFAULT_TOKEN_PATH) sys.exit(0) if args != [] and opts == []: create_token_file(args[0]) sys.exit(0) # print help first and exit for o, a in opts: # print help message if o in ('-h', '--help'): usage() sys.exit(0) # process options for o, a in opts: # update token file if o in ('-u', '--update'): if args == []: file_path = DEFAULT_TOKEN_PATH else: file_path = args[0] update_token_file(file_path) msg = "token updated, please remeber" +\ "to update the token data on client side!" print msg except getopt.GetoptError: print('syntax ERROR!') usage() sys.exit(1) # exist code: # 0: success # 1: sytax error # 2: file not exist # 3: file already existed if "__main__" == __name__: main(sys.argv[1:]) sys.exit(0) zVMCloudConnector-1.4.1/scripts/sdkserver0000664000175000017510000000144513371225174020151 0ustar ruirui00000000000000#!/usr/bin/python # 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. from zvmsdk import config from zvmsdk import log if __name__ == '__main__': config.load_config() log.setup_log() from zvmsdk import sdkserver sdkserver.start_daemon() zVMCloudConnector-1.4.1/setup.cfg0000664000175000017510000000004613442723341016342 0ustar ruirui00000000000000[egg_info] tag_build = tag_date = 0 zVMCloudConnector-1.4.1/LICENSE0000664000175000017510000002363513424251652015540 0ustar ruirui00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.