def _CreateInstance(self): """Creates IBM Cloud VM instance.""" cmd = ibm.IbmAPICommand(self) cmd.flags.update({ 'name': self.name, 'imageid': self.imageid, 'profile': self.profile, 'vpcid': self.vpcid, 'subnet': self.subnet, 'key': self.key, 'zone': self.zone }) cmd.user_data = self.user_data if self.boot_volume_size > 0: cmd.flags['capacity'] = self.boot_volume_size cmd.flags['iops'] = self.boot_volume_iops if self.boot_encryption_key: cmd.flags['encryption_key'] = self.boot_encryption_key logging.info('Creating instance, flags: %s', cmd.flags) resp = json.loads(cmd.CreateInstance()) if 'id' not in resp: raise errors.Error(f'IBM Cloud ERROR: Failed to create instance: {resp}') self.vmid = resp['id'] self.vm_created = True logging.info('Instance created, id: %s', self.vmid) logging.info('Waiting for instance to start, id: %s', self.vmid) cmd.flags['instanceid'] = self.vmid status = cmd.InstanceStatus() assert status == ibm.States.RUNNING if status != ibm.States.RUNNING: logging.error('Instance start failed, status: %s', status) logging.info('Instance %s status %s', self.vmid, status)
def _DeleteInstance(self): """Deletes a IBM Cloud VM instance.""" cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = self.vmid cmd.InstanceDelete() self.vm_deleted = True logging.info('Instance deleted: %s', cmd.flags['instanceid'])
def _Create(self): """Creates and starts a IBM Cloud VM instance.""" self._CreateInstance() if self.subnet: # this is for the primary vnic and fip self.fip_address, self.fip_id = self.network.CreateFip( self.name + 'fip', self.vmid) self.ip_address = self.fip_address self.internal_ip = self._WaitForIPAssignment(self.subnet) logging.info('Fip: %s, ip: %s', self.ip_address, self.internal_ip) if self.subnets: # create the extra vnics cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = self.vmid for subnet_name in self.subnets.keys(): cmd.flags['name'] = subnet_name cmd.flags['subnet'] = self.subnets[subnet_name]['id'] # subnet id logging.info('Creating extra vnic for vmid: %s, subnet: %s', \ self.vmid, cmd.flags['subnet']) vnicid, ip_addr = cmd.InstanceVnicCreate() logging.info('Extra vnic created for vmid: %s, vnicid: %s, ip_addr: %s', self.vmid, vnicid, ip_addr) self.subnets[subnet_name]['vnicid'] = vnicid self.subnets[subnet_name]['ip_addr'] = ip_addr logging.info('Extra vnics created for vmid: %s, subnets: %s', self.vmid, self.subnets)
def _WaitForVolumeAttachment(self, vm): """Waits and checks until the volume status is attached.""" if self.vol_id is None: return volcmd = ibm.IbmAPICommand(self) volcmd.flags.update({ 'instanceid': self.attached_vm.vmid, 'volume': self.vol_id }) logging.info('Checking volume on instance %s, volume: %s', vm.vmid, self.vol_id) status = None endtime = time.time() + _MAX_DISK_ATTACH_SECONDS while time.time() < endtime: resp = volcmd.InstanceShowVolume() if resp: resp = json.loads(resp) status = resp.get('status', 'unknown') logging.info('Checking instance volume status: %s', status) if status == ibm.States.ATTACHED: logging.info('Remote volume %s has been attached to %s', self.name, vm.name) break time.sleep(2) if status != ibm.States.ATTACHED: logging.error('Failed to attach the volume') raise errors.Error('IBMCLOUD ERROR: failed to attach a volume.')
def DeleteFip(self, vmid: str, fip_address: str, fipid: str): """Deletes fip in a IBM Cloud VM instance.""" logging.info( 'Deleting FIP, instanceid: %s, fip address: %s, fip id: %s', vmid, fip_address, fipid) cmd = ibm.IbmAPICommand(self) cmd.flags['fipid'] = fipid cmd.InstanceFipDelete()
def _StopInstance(self): """Stops a IBM Cloud VM instance.""" cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = self.vmid status = cmd.InstanceStop() logging.info('stop_instance_poll: last status is %s', status) if status != ibm.States.STOPPED: logging.error('Instance stop failed, status: %s', status)
def _StartInstance(self): """Starts a IBM Cloud VM instance.""" cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = self.vmid status = cmd.InstanceStart() logging.info('start_instance_poll: last status is %s', status) assert status == ibm.States.RUNNING if status != ibm.States.RUNNING: logging.error('Instance start failed, status: %s', status)
def _Delete(self): """Deletes the vpc and subnets.""" with self._lock: if (ibmcloud_virtual_machine.IbmCloudVirtualMachine. validated_subnets > 0 and self.subnet): cmd = ibm.IbmAPICommand(self) cmd.flags.update({ 'prefix': self.prefix, 'zone': self.zone, 'id': self.subnet, 'items': 'subnets' }) logging.info('Deleting subnet: %s', self.subnet) cmd.DeleteResource() time_to_end = time.time() + _DEFAULT_TIMEOUT while cmd.GetResource( ) is not None and time_to_end < time.time(): logging.info( 'Subnet still exists, waiting to delete subnet: %s', self.subnet) time.sleep(5) cmd.DeleteResource() ibmcloud_virtual_machine.IbmCloudVirtualMachine.validated_subnets -= 1 # different lock so all threads get a chance to delete its subnet first with self._lock_vpc: if (self.subnet and self.vpcid not in ibmcloud_virtual_machine. IbmCloudVirtualMachine.validated_resources_set): cmd = ibm.IbmAPICommand(self) # check till all subnets are gone time_to_end = time.time() + _DEFAULT_TIMEOUT while ibmcloud_virtual_machine.IbmCloudVirtualMachine.validated_subnets > 0: logging.info('Subnets not empty yet') if time_to_end < time.time(): break time.sleep(10) cmd.flags['items'] = 'vpcs' cmd.flags['id'] = self.vpcid logging.info('Waiting to delete vpc') time.sleep(10) (ibmcloud_virtual_machine.IbmCloudVirtualMachine. validated_resources_set.add(self.vpcid)) cmd.DeleteResource() self.vpcid = None
def _Delete(self): """Deletes an external block volume.""" if self.vol_id is None: logging.info('Volume %s was not created. Skipping deletion.', self.name) return volcmd = ibm.IbmAPICommand(self) volcmd.flags['volume'] = self.vol_id logging.info('Volume delete, volcmd.flags %s', volcmd.flags) volcmd.DeleteVolume() logging.info('Volume deleted: %s', self.vol_id) self.vol_id = None
def _CreateRiasKey(self): """Creates a ibmcloud key from the generated ssh key.""" logging.info('Creating rias key') with open(vm_util.GetPublicKeyPath(), 'r') as keyfile: pubkey = keyfile.read() logging.info('ssh private key file: %s, public key file: %s', \ vm_util.GetPrivateKeyPath(), vm_util.GetPublicKeyPath()) cmd = ibm.IbmAPICommand(self) cmd.flags['name'] = self.prefix + str(flags.FLAGS.run_uri) + 'key' cmd.flags['pubkey'] = pubkey return cmd.CreateKey()
def _DeleteKey(self): """Deletes the rias key.""" with self._lock: # key is not dependent on vpc, one key is used if self.key not in IbmCloudVirtualMachine.validated_resources_set: time.sleep(5) cmd = ibm.IbmAPICommand(self) cmd.flags['items'] = 'keys' cmd.flags['id'] = self.key cmd.DeleteResource() IbmCloudVirtualMachine.validated_resources_set.add(self.key)
def _CheckImage(self): """Verifies we have an imageid to use.""" cmd = ibm.IbmAPICommand(self) cmd.flags['image_name'] = self.image or self._GetDefaultImageName() logging.info('Looking up image: %s', cmd.flags['image_name']) self.imageid = cmd.GetImageId() if self.imageid is None: logging.info('Failed to find valid image id') sys.exit(1) else: logging.info('Image id found: %s', self.imageid)
def _GetDecodedPasswordData(self): # Retrieve a base64 encoded, encrypted password for the VM. cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = self.vmid resp = cmd.InstanceInitializationShow() logging.info('Instance %s, resp %s', self.vmid, resp) encrypted = None if resp and 'password' in resp and 'encrypted_password' in resp['password']: encrypted = resp['password']['encrypted_password'] if encrypted is None: raise ValueError('Failed to retrieve encrypted password') return base64.b64decode(encrypted)
def _Exists(self): """Returns true if the VPC exists.""" cmd = ibm.IbmAPICommand(self) cmd.flags.update({ 'prefix': self.prefix, 'zone': self.zone, 'items': 'vpcs' }) self.vpcid = cmd.GetResource() if self.vpcid: return True return False
def _SetupResources(self): """Looks up the resources needed, if not found, creates new.""" logging.info('Checking resources') cmd = ibm.IbmAPICommand(self) cmd.flags.update({ 'prefix': self.prefix, 'zone': self.zone, 'items': 'vpcs' }) self.vpcid = cmd.GetResource() logging.info('Vpc found: %s', self.vpcid) cmd.flags['items'] = 'subnets' self.subnet = cmd.GetResource() logging.info('Subnet found: %s', self.subnet) if not self.vpcid: logging.info('Creating a vpc') self.network.Create() self.vpcid = self.network.vpcid if not self.subnet: logging.info('Creating a subnet') self.network.CreateSubnet(self.vpcid) self.subnet = self.network.subnet if FLAGS.ibmcloud_subnets_extra > 0: # these are always created outside perfkit cmd.flags['prefix'] = ibmcloud_network.SUBNET_SUFFIX_EXTRA self.subnets = cmd.ListSubnetsExtra() logging.info('Extra subnets found: %s', self.subnets) # look up for existing key that matches this run uri cmd.flags['items'] = 'keys' cmd.flags['prefix'] = self.prefix + FLAGS.run_uri self.key = cmd.GetResource() logging.info('Key found: %s', self.key) if self.key is None: cmd.flags['items'] = 'keys' cmd.flags['prefix'] = self.prefix + FLAGS.run_uri self.key = self._CreateRiasKey() if self.key is None: raise errors.Error( 'IBM Cloud ERROR: Failed to create a rias key') logging.info('Created a new key: %s', self.key) logging.info('Looking up the image: %s', self.imageid) cmd.flags['imageid'] = self.imageid self.os_data = util.GetOsInfo(cmd.ImageShow()) logging.info('Image os: %s', self.os_data) logging.info('Checking resources finished')
def Detach(self): """Deletes the volume from instance.""" if self.attached_vm is None: logging.warning('Cannot detach remote volume from a non-existing VM.') return if self.attached_vdisk_uri is None: logging.warning('Cannot detach remote volume from a non-existing VDisk.') return volcmd = ibm.IbmAPICommand(self) volcmd.flags['volume'] = self.attached_vdisk_uri resp = volcmd.InstanceDeleteVolume() logging.info('Deleted volume: %s', resp) self.attached_vdisk_uri = None self.device_path = None
def CreateFip(self, name: str, vmid: str): """Creates a VNIC in a IBM Cloud VM instance.""" cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = vmid vnicid = cmd.InstanceGetPrimaryVnic() cmd.flags['name'] = name cmd.flags['target'] = vnicid logging.info('Creating FIP for instanceid: %s', vmid) resp = json.loads(cmd.InstanceFipCreate()) if resp: logging.info('FIP create resp: %s', resp) assert resp['address'] is not None return resp['address'], resp['id'] else: raise errors.Error( f'IBM Cloud ERROR: Failed to create fip for instance {vmid}')
def setUp(self): super(IbmCloudVirtualMachineTest, self).setUp() self.mock_create_instance = self.enter_context( mock.patch.object(ibm_api.IbmAPICommand, 'CreateInstance')) self.mock_instance_status = self.enter_context( mock.patch.object(ibm_api.IbmAPICommand, 'InstanceStatus')) self.mock_get_resource = self.enter_context( mock.patch.object(ibm_api.IbmAPICommand, 'GetResource')) self.mock_create_vpc = self.enter_context( mock.patch.object(ibm_api.IbmAPICommand, 'CreateVpc')) self.mock_create_subnet = self.enter_context( mock.patch.object(ibm_api.IbmAPICommand, 'CreateSubnet')) self.mock_check_environment = self.enter_context( mock.patch.object(ibm_api.IbmAPICommand, '_CheckEnvironment')) with mock.patch.object(ibm_api.IbmAPICommand, '__init__', lambda self: None): self.cmd = ibm_api.IbmAPICommand() self.vm = self._CreateTestIbmCloudVirtualMachine()
def _Create(self): """Creates an external block volume.""" volcmd = ibm.IbmAPICommand(self) volcmd.flags.update({ 'name': self.name, 'zone': self.zone, 'iops': FLAGS.ibmcloud_volume_iops, 'profile': FLAGS.ibmcloud_volume_profile, 'capacity': self.disk_size }) if self.encryption_key is not None: volcmd.flags['encryption_key'] = self.encryption_key if FLAGS.ibmcloud_rgid: volcmd.flags['resource_group'] = FLAGS.ibmcloud_rgid logging.info('Volume create, volcmd.flags %s', volcmd.flags) resp = json.loads(volcmd.CreateVolume()) self.vol_id = resp['id'] logging.info('Volume id: %s', self.vol_id)
def Attach(self, vm): """Attaches the disk to a VM. Args: vm: instance of the vm to which the disk will be attached. """ if self.vol_id is None: raise errors.Error(f'Cannot attach remote volume {self.name}') if vm.vmid is None: raise errors.Error(f'Cannot attach remote volume {self.name} ' 'to non-existing {vm.name} VM') volcmd = ibm.IbmAPICommand(self) volcmd.flags.update({ 'name': self.name, 'instanceid': vm.vmid, 'volume': self.vol_id, 'delete': True }) # wait till volume is PROVISIONED before attach status = None endtime = time.time() + _MAX_DISK_ATTACH_SECONDS while time.time() < endtime: status = json.loads(volcmd.ShowVolume())['status'] logging.info('Checking volume status: %s', status) if status == ibm.States.AVAILABLE: logging.info('Volume is available') break time.sleep(2) if status != ibm.States.AVAILABLE: logging.error('Failed to create a volume') raise errors.Error('IBMCLOUD ERROR: failed to provision a volume.') resp = json.loads(volcmd.InstanceCreateVolume()) logging.info('Attached volume, volcmd.flags %s', volcmd.flags) volume = resp['id'] logging.info('VDISK ID: %s, vdiskname: %s, instanceid: %s', volume, self.name, vm.vmid) self.attached_vdisk_uri = volume self.attached_vm = vm self._WaitForVolumeAttachment(vm) self._GetDeviceFromVDisk(vm)
def _Create(self): """Creates a IBM Cloud VPC with address prefixes for all zones.""" cmd = ibm.IbmAPICommand(self) cmd.flags['name'] = self.name logging.info('Creating vpc: %s', cmd.flags) self.vpcid = cmd.CreateVpc() if self.vpcid: for zone in FLAGS.zones: cmd.flags.update({'vpcid': self.vpcid, 'zone': zone}) index = int(zone[len(zone) - 1]) # get the ending -1 cmd.flags['cidr'] = VPC_PREFIX_RANGES[index - 1] cmd.flags[ 'name'] = self.prefix + VPC_NAME + util.DELIMITER + zone resp = cmd.CreatePrefix() if resp: logging.info('Created vpc prefix range: %s', resp.get('id')) else: raise errors.Error('IBM Cloud ERROR: Failed to create ' 'vpc address prefix') else: raise errors.Error('IBM Cloud ERROR: Failed to create vpc')
def _WaitForIPAssignment(self, networkid: str): """Finds the IP address assigned to the vm.""" ip_v4_address = '0.0.0.0' count = 0 while (ip_v4_address == '0.0.0.0' and count * FLAGS.ibmcloud_polling_delay < 240): time.sleep(FLAGS.ibmcloud_polling_delay) count += 1 cmd = ibm.IbmAPICommand(self) cmd.flags['instanceid'] = self.vmid logging.info('Looking for IP for instance %s, networkid: %s', self.vmid, networkid) resp = cmd.InstanceShow() for network in resp['network_interfaces']: if network['subnet']['id'] == networkid: ip_v4_address = network['primary_ipv4_address'] break logging.info('Waiting on ip assignment: %s', ip_v4_address) if ip_v4_address == '0.0.0.0': raise ValueError('Failed to retrieve ip address') return ip_v4_address
def CreateSubnet(self, vpcid: str): """Creates a IBM Cloud subnet on the given vpc.""" self.vpcid = vpcid subnet_index = int(self.zone[len(self.zone) - 1]) # get the ending -1 cmd = ibm.IbmAPICommand(self) cmd.flags.update({ 'vpcid': self.vpcid, 'zone': self.zone, 'name': (self.prefix + VPC_NAME + util.SUBNET_SUFFIX + str(subnet_index) + util.DELIMITER + self.zone), 'cidr': VPC_SUBNETS[subnet_index - 1] }) logging.info('Creating subnet: %s', cmd.flags) resp = cmd.CreateSubnet() if resp: self.subnet = resp.get('id') ibmcloud_virtual_machine.IbmCloudVirtualMachine.validated_subnets += 1 logging.info('Created subnet: %s, zone %s', self.subnet, self.zone) else: raise errors.Error('IBM Cloud ERROR: Failed to create subnet')