def _PostCreate(self): """Get the instance's data and tag it.""" describe_cmd = util.AWS_PREFIX + [ 'ec2', 'describe-instances', '--region=%s' % self.region, '--instance-ids=%s' % self.id] logging.info('Getting instance %s public IP. This will fail until ' 'a public IP is available, but will be retried.', self.id) stdout, _ = util.IssueRetryableCommand(describe_cmd) response = json.loads(stdout) instance = response['Reservations'][0]['Instances'][0] self.ip_address = instance['PublicIpAddress'] self.internal_ip = instance['PrivateIpAddress'] if util.IsRegion(self.zone): self.zone = str(instance['Placement']['AvailabilityZone']) assert self.group_id == instance['SecurityGroups'][0]['GroupId'], ( self.group_id, instance['SecurityGroups'][0]['GroupId']) if FLAGS.aws_efa and FLAGS.aws_efa_version: # Download EFA then call InstallEfa method so that subclass can override self.InstallPackages('curl') url = _EFA_URL.format(version=FLAGS.aws_efa_version) tarfile = posixpath.basename(url) self.RemoteCommand(f'curl -O {url}; tar -xvzf {tarfile}') self._InstallEfa() # Run test program to confirm EFA working self.RemoteCommand('cd aws-efa-installer; ./efa_test.sh')
def __init__(self, spec): super(EksCluster, self).__init__(spec) # EKS requires a region and optionally a list of one or zones. # Interpret the zone as a comma separated list of zones or a region. self.control_plane_zones = self.zone and self.zone.split(',') if not self.control_plane_zones: raise errors.Config.MissingOption( 'container_cluster.vm_spec.AWS.zone is required.') elif len(self.control_plane_zones) == 1 and util.IsRegion(self.zone): self.region = self.zone self.control_plane_zones = [] logging.info("Interpreting zone '%s' as a region", self.zone) else: self.region = util.GetRegionFromZones(self.control_plane_zones) # control_plane_zones must be a superset of the node zones for nodepool in self.nodepools.values(): if nodepool.vm_config.zone not in self.control_plane_zones: self.control_plane_zones.append(nodepool.vm_config.zone) if len(self.control_plane_zones) == 1: # eksctl essentially requires you pass --zones if you pass --node-zones # and --zones must have at least 2 zones # https://github.com/weaveworks/eksctl/issues/4735 self.control_plane_zones.append( self.region + ('b' if self.zone.endswith('a') else 'a')) self.cluster_version = FLAGS.container_cluster_version # TODO(user) support setting boot disk type if EKS does. self.boot_disk_type = self.vm_config.DEFAULT_ROOT_DISK_TYPE
def _Create(self): """Create a VM instance.""" placement = [] if not util.IsRegion(self.zone): placement.append('AvailabilityZone=%s' % self.zone) if IsPlacementGroupCompatible(self.machine_type): placement.append('GroupName=%s' % self.network.placement_group.name) placement = ','.join(placement) block_device_map = GetBlockDeviceMap(self.machine_type) create_cmd = util.AWS_PREFIX + [ 'ec2', 'run-instances', '--region=%s' % self.region, '--subnet-id=%s' % self.network.subnet.id, '--associate-public-ip-address', '--image-id=%s' % self.image, '--instance-type=%s' % self.machine_type, '--key-name=%s' % 'perfkit-key-%s' % FLAGS.run_uri ] if block_device_map: create_cmd.append('--block-device-mappings=%s' % block_device_map) if placement: create_cmd.append('--placement=%s' % placement) if self.user_data: create_cmd.append('--user-data=%s' % self.user_data) stdout, _, _ = vm_util.IssueCommand(create_cmd) response = json.loads(stdout) self.id = response['Instances'][0]['InstanceId']
def __init__(self, vm_spec): """Initialize a AWS virtual machine. Args: vm_spec: virtual_machine.BaseVirtualMachineSpec object of the vm. """ super(AwsVirtualMachine, self).__init__(vm_spec) self.region = util.GetRegionFromZone(self.zone) self.user_name = FLAGS.aws_user_name if self.machine_type in NUM_LOCAL_VOLUMES: self.max_local_disks = NUM_LOCAL_VOLUMES[self.machine_type] self.user_data = None self.network = aws_network.AwsNetwork.GetNetwork(self) self.firewall = aws_network.AwsFirewall.GetFirewall() self.use_dedicated_host = vm_spec.use_dedicated_host self.use_spot_instance = vm_spec.use_spot_instance self.spot_price = vm_spec.spot_price self.boot_disk_size = vm_spec.boot_disk_size self.client_token = str(uuid.uuid4()) self.host = None self.id = None if self.use_dedicated_host and util.IsRegion(self.zone): raise ValueError( 'In order to use dedicated hosts, you must specify an availability ' 'zone, not a region ("zone" was %s).' % self.zone) if self.use_spot_instance and self.spot_price <= 0.0: raise ValueError( 'In order to use spot instances you must specify a spot price ' 'greater than 0.0.')
def __init__(self, vm_spec): """Initialize a AWS virtual machine. Args: vm_spec: virtual_machine.BaseVirtualMachineSpec object of the vm. Raises: ValueError: If an incompatible vm_spec is passed. """ super(AwsVirtualMachine, self).__init__(vm_spec) self.region = util.GetRegionFromZone(self.zone) self.user_name = FLAGS.aws_user_name or self.DEFAULT_USER_NAME if self.machine_type in aws_disk.NUM_LOCAL_VOLUMES: self.max_local_disks = aws_disk.NUM_LOCAL_VOLUMES[self.machine_type] self.user_data = None self.network = aws_network.AwsNetwork.GetNetwork(self) self.placement_group = getattr(vm_spec, 'placement_group', self.network.placement_group) self.firewall = aws_network.AwsFirewall.GetFirewall() self.use_dedicated_host = vm_spec.use_dedicated_host self.num_vms_per_host = vm_spec.num_vms_per_host self.use_spot_instance = vm_spec.use_spot_instance self.spot_price = vm_spec.spot_price self.spot_block_duration_minutes = vm_spec.spot_block_duration_minutes self.boot_disk_size = vm_spec.boot_disk_size self.client_token = str(uuid.uuid4()) self.host = None self.id = None self.metadata.update({ 'spot_instance': self.use_spot_instance, 'spot_price': self.spot_price, 'spot_block_duration_minutes': self.spot_block_duration_minutes, 'placement_group_strategy': self.placement_group.strategy if self.placement_group else placement_group.PLACEMENT_GROUP_NONE, 'aws_credit_specification': FLAGS.aws_credit_specification if FLAGS.aws_credit_specification else 'none' }) self.spot_early_termination = False self.spot_status_code = None # See: # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking-os.html self._smp_affinity_script = 'smp_affinity.sh' if self.use_dedicated_host and util.IsRegion(self.zone): raise ValueError( 'In order to use dedicated hosts, you must specify an availability ' 'zone, not a region ("zone" was %s).' % self.zone) if self.use_dedicated_host and self.use_spot_instance: raise ValueError( 'Tenancy=host is not supported for Spot Instances')
def __init__(self, vm_spec): """Initialize a AWS virtual machine. Args: vm_spec: virtual_machine.BaseVirtualMachineSpec object of the vm. Raises: ValueError: If an incompatible vm_spec is passed. """ super(AwsVirtualMachine, self).__init__(vm_spec) self.region = util.GetRegionFromZone(self.zone) self.user_name = FLAGS.aws_user_name if self.machine_type in NUM_LOCAL_VOLUMES: self.max_local_disks = NUM_LOCAL_VOLUMES[self.machine_type] self.user_data = None self.network = aws_network.AwsNetwork.GetNetwork(self) self.placement_group = getattr(vm_spec, 'placement_group', self.network.placement_group) self.firewall = aws_network.AwsFirewall.GetFirewall() self.use_dedicated_host = vm_spec.use_dedicated_host # TODO(buggay): implement num_vms_per_host functionality self.num_vms_per_host = vm_spec.num_vms_per_host if self.num_vms_per_host: raise NotImplementedError( 'Num vms per host for AWS is not supported.') self.use_spot_instance = vm_spec.use_spot_instance self.spot_price = vm_spec.spot_price self.boot_disk_size = vm_spec.boot_disk_size self.client_token = str(uuid.uuid4()) self.host = None self.id = None self.metadata.update({ 'spot_instance': self.use_spot_instance, 'spot_price': self.spot_price, 'placement_group_strategy': self.placement_group.strategy if self.placement_group else 'none', 'aws_credit_specification': FLAGS.aws_credit_specification if FLAGS.aws_credit_specification else 'none' }) self.early_termination = False self.spot_status_code = None if self.use_dedicated_host and util.IsRegion(self.zone): raise ValueError( 'In order to use dedicated hosts, you must specify an availability ' 'zone, not a region ("zone" was %s).' % self.zone) if self.use_dedicated_host and self.use_spot_instance: raise ValueError( 'Tenancy=host is not supported for Spot Instances')
def _Create(self): """Creates the subnet.""" create_cmd = util.AWS_PREFIX + [ 'ec2', 'create-subnet', '--region=%s' % self.region, '--vpc-id=%s' % self.vpc_id, '--cidr-block=%s' % self.cidr_block] if not util.IsRegion(self.zone): create_cmd.append('--availability-zone=%s' % self.zone) stdout, _, _ = vm_util.IssueCommand(create_cmd) response = json.loads(stdout) self.id = response['Subnet']['SubnetId'] util.AddDefaultTags(self.id, self.region)
def _Create(self): """Creates the disk.""" create_cmd = util.AWS_PREFIX + [ 'ec2', 'create-volume', '--region=%s' % self.region, '--size=%s' % self.disk_size, '--volume-type=%s' % self.disk_type ] if not util.IsRegion(self.zone): create_cmd.append('--availability-zone=%s' % self.zone) if self.disk_type == IO1: create_cmd.append('--iops=%s' % self.iops) stdout, _, _ = vm_util.IssueCommand(create_cmd) response = json.loads(stdout) self.id = response['VolumeId'] util.AddDefaultTags(self.id, self.region)
def _CreateOnDemand(self): """Create an OnDemand VM instance.""" placement = [] if not util.IsRegion(self.zone): placement.append('AvailabilityZone=%s' % self.zone) if self.use_dedicated_host: placement.append('Tenancy=host,HostId=%s' % self.host.id) num_hosts = len(self.host_list) elif IsPlacementGroupCompatible(self.machine_type): placement.append('GroupName=%s' % self.network.placement_group.name) placement = ','.join(placement) block_device_map = GetBlockDeviceMap(self.machine_type, self.boot_disk_size, self.image, self.region) create_cmd = util.AWS_PREFIX + [ 'ec2', 'run-instances', '--region=%s' % self.region, '--subnet-id=%s' % self.network.subnet.id, '--associate-public-ip-address', '--client-token=%s' % self.client_token, '--image-id=%s' % self.image, '--instance-type=%s' % self.machine_type, '--key-name=%s' % 'perfkit-key-%s' % FLAGS.run_uri ] if block_device_map: create_cmd.append('--block-device-mappings=%s' % block_device_map) if placement: create_cmd.append('--placement=%s' % placement) if self.user_data: create_cmd.append('--user-data=%s' % self.user_data) _, stderr, _ = vm_util.IssueCommand(create_cmd) if self.use_dedicated_host and 'InsufficientCapacityOnHost' in stderr: logging.warning( 'Creation failed due to insufficient host capacity. A new host will ' 'be created and instance creation will be retried.') with self._lock: if num_hosts == len(self.host_list): host = AwsDedicatedHost(self.machine_type, self.zone) self.host_list.append(host) host.Create() self.host = self.host_list[-1] self.client_token = str(uuid.uuid4()) raise errors.Resource.RetryableCreationError() if 'InsufficientInstanceCapacity' in stderr: raise errors.Benchmarks.InsufficientCapacityCloudFailure(stderr)
def _PostCreate(self): """Get the instance's data and tag it.""" describe_cmd = util.AWS_PREFIX + [ 'ec2', 'describe-instances', '--region=%s' % self.region, '--instance-ids=%s' % self.id] logging.info('Getting instance %s public IP. This will fail until ' 'a public IP is available, but will be retried.', self.id) stdout, _ = util.IssueRetryableCommand(describe_cmd) response = json.loads(stdout) instance = response['Reservations'][0]['Instances'][0] self.ip_address = instance['PublicIpAddress'] self.internal_ip = instance['PrivateIpAddress'] if util.IsRegion(self.zone): self.zone = str(instance['Placement']['AvailabilityZone']) util.AddDefaultTags(self.id, self.region) assert self.group_id == instance['SecurityGroups'][0]['GroupId'], ( self.group_id, instance['SecurityGroups'][0]['GroupId'])
def __init__(self, spec): super(EksCluster, self).__init__(spec) # EKS requires a region and optionally a list of zones. # Interpret the zone as a comma separated list of zones or a region. self.zones = sorted(FLAGS.eks_zones) or (self.zone and self.zone.split(',')) if not self.zones: raise errors.Config.MissingOption( 'container_cluster.vm_spec.AWS.zone is required.') elif len(self.zones) > 1: self.region = util.GetRegionFromZone(self.zones[0]) self.zone = ','.join(self.zones) elif util.IsRegion(self.zones[0]): self.region = self.zone = self.zones[0] self.zones = [] logging.info("Interpreting zone '%s' as a region", self.zone) else: raise errors.Config.InvalidValue( 'container_cluster.vm_spec.AWS.zone must either be a comma separated ' 'list of zones or a region.') self.cluster_version = FLAGS.container_cluster_version
def _PostCreate(self): """Get the instance's data and tag it.""" describe_cmd = util.AWS_PREFIX + [ 'ec2', 'describe-instances', '--region=%s' % self.region, '--instance-ids=%s' % self.id] logging.info('Getting instance %s public IP. This will fail until ' 'a public IP is available, but will be retried.', self.id) stdout, _ = util.IssueRetryableCommand(describe_cmd) response = json.loads(stdout) instance = response['Reservations'][0]['Instances'][0] self.ip_address = instance['PublicIpAddress'] self.internal_ip = instance['PrivateIpAddress'] if util.IsRegion(self.zone): self.zone = str(instance['Placement']['AvailabilityZone']) assert self.group_id == instance['SecurityGroups'][0]['GroupId'], ( self.group_id, instance['SecurityGroups'][0]['GroupId']) if FLAGS.aws_efa: self.InstallPackages('curl') url = _EFA_URL.format(version=FLAGS.aws_efa_version) self.RemoteCommand( _EFA_INSTALL_CMD.format(url=url, tarfile=posixpath.basename(url)))
def testRegion(self): self.assertTrue(util.IsRegion('eu-central-1'))
def testZone(self): self.assertFalse(util.IsRegion('us-east-1a'))
def testBadFormat(self): with self.assertRaises(ValueError): util.IsRegion('us-east')
def _Create(self): """Create a VM instance.""" placement = [] if not util.IsRegion(self.zone): placement.append('AvailabilityZone=%s' % self.zone) if self.use_dedicated_host: placement.append('Tenancy=host,HostId=%s' % self.host.id) num_hosts = len(self.host_list) elif self.placement_group: if IsPlacementGroupCompatible(self.machine_type): placement.append('GroupName=%s' % self.placement_group.name) else: logging.warn( 'VM not placed in Placement Group. VM Type %s not supported', self.machine_type) placement = ','.join(placement) block_device_map = GetBlockDeviceMap(self.machine_type, self.boot_disk_size, self.image, self.region) tags = {} tags.update(self.vm_metadata) tags.update(util.MakeDefaultTags()) create_cmd = util.AWS_PREFIX + [ 'ec2', 'run-instances', '--region=%s' % self.region, '--subnet-id=%s' % self.network.subnet.id, '--client-token=%s' % self.client_token, '--image-id=%s' % self.image, '--instance-type=%s' % self.machine_type, '--key-name=%s' % AwsKeyFileManager.GetKeyNameForRun(), '--tag-specifications=%s' % util.FormatTagSpecifications('instance', tags)] if FLAGS.aws_efa: create_cmd.extend([ '--network-interfaces', ','.join(['%s=%s' % item for item in sorted(_EFA_PARAMS.items())]) ]) else: create_cmd.append('--associate-public-ip-address') if block_device_map: create_cmd.append('--block-device-mappings=%s' % block_device_map) if placement: create_cmd.append('--placement=%s' % placement) if FLAGS.aws_credit_specification: create_cmd.append('--credit-specification=%s' % FLAGS.aws_credit_specification) if self.user_data: create_cmd.append('--user-data=%s' % self.user_data) if self.capacity_reservation_id: create_cmd.append( '--capacity-reservation-specification=CapacityReservationTarget=' '{CapacityReservationId=%s}' % self.capacity_reservation_id) if self.use_spot_instance: instance_market_options = collections.OrderedDict() spot_options = collections.OrderedDict() spot_options['SpotInstanceType'] = 'one-time' spot_options['InstanceInterruptionBehavior'] = 'terminate' if self.spot_price: spot_options['MaxPrice'] = str(self.spot_price) instance_market_options['MarketType'] = 'spot' instance_market_options['SpotOptions'] = spot_options create_cmd.append( '--instance-market-options=%s' % json.dumps(instance_market_options)) _, stderr, retcode = vm_util.IssueCommand(create_cmd, raise_on_failure=False) machine_type_prefix = self.machine_type.split('.')[0] host_arch = _MACHINE_TYPE_PREFIX_TO_HOST_ARCH.get(machine_type_prefix) if host_arch: self.host_arch = host_arch if self.use_dedicated_host and 'InsufficientCapacityOnHost' in stderr: if self.num_vms_per_host: raise errors.Resource.CreationError( 'Failed to create host: %d vms of type %s per host exceeds ' 'memory capacity limits of the host' % (self.num_vms_per_host, self.machine_type)) else: logging.warning( 'Creation failed due to insufficient host capacity. A new host will ' 'be created and instance creation will be retried.') with self._lock: if num_hosts == len(self.host_list): host = AwsDedicatedHost(self.machine_type, self.zone) self.host_list.append(host) host.Create() self.host = self.host_list[-1] self.client_token = str(uuid.uuid4()) raise errors.Resource.RetryableCreationError() if 'InsufficientInstanceCapacity' in stderr: if self.use_spot_instance: self.spot_status_code = 'InsufficientSpotInstanceCapacity' self.early_termination = True raise errors.Benchmarks.InsufficientCapacityCloudFailure(stderr) if 'SpotMaxPriceTooLow' in stderr: self.spot_status_code = 'SpotMaxPriceTooLow' self.early_termination = True raise errors.Resource.CreationError(stderr) if 'InstanceLimitExceeded' in stderr: raise errors.Benchmarks.QuotaFailure(stderr) if retcode: raise errors.Resource.CreationError( 'Failed to create VM: %s return code: %s' % (retcode, stderr))
def _Create(self): """Create a VM instance.""" placement = [] if not util.IsRegion(self.zone): placement.append('AvailabilityZone=%s' % self.zone) if self.use_dedicated_host: placement.append('Tenancy=host,HostId=%s' % self.host.id) num_hosts = len(self.host_list) elif IsPlacementGroupCompatible(self.machine_type): placement.append('GroupName=%s' % self.network.placement_group.name) placement = ','.join(placement) block_device_map = GetBlockDeviceMap(self.machine_type, self.boot_disk_size, self.image, self.region) create_cmd = util.AWS_PREFIX + [ 'ec2', 'run-instances', '--region=%s' % self.region, '--subnet-id=%s' % self.network.subnet.id, '--associate-public-ip-address', '--client-token=%s' % self.client_token, '--image-id=%s' % self.image, '--instance-type=%s' % self.machine_type, '--key-name=%s' % 'perfkit-key-%s' % FLAGS.run_uri] if block_device_map: create_cmd.append('--block-device-mappings=%s' % block_device_map) if placement: create_cmd.append('--placement=%s' % placement) if self.user_data: create_cmd.append('--user-data=%s' % self.user_data) if self.use_spot_instance: instance_market_options = OrderedDict() spot_options = OrderedDict() spot_options['SpotInstanceType'] = 'one-time' spot_options['InstanceInterruptionBehavior'] = 'terminate' if self.spot_price: spot_options['MaxPrice'] = str(self.spot_price) instance_market_options['MarketType'] = 'spot' instance_market_options['SpotOptions'] = spot_options create_cmd.append( '--instance-market-options=%s' % json.dumps(instance_market_options)) _, stderr, retcode = vm_util.IssueCommand(create_cmd) if self.use_dedicated_host and 'InsufficientCapacityOnHost' in stderr: logging.warning( 'Creation failed due to insufficient host capacity. A new host will ' 'be created and instance creation will be retried.') with self._lock: if num_hosts == len(self.host_list): host = AwsDedicatedHost(self.machine_type, self.zone) self.host_list.append(host) host.Create() self.host = self.host_list[-1] self.client_token = str(uuid.uuid4()) raise errors.Resource.RetryableCreationError() if 'InsufficientInstanceCapacity' in stderr: if self.use_spot_instance: self.spot_status_code = 'InsufficientSpotInstanceCapacity' self.early_termination = True raise errors.Benchmarks.InsufficientCapacityCloudFailure(stderr) if 'SpotMaxPriceTooLow' in stderr: self.spot_status_code = 'SpotMaxPriceTooLow' self.early_termination = True raise errors.Resource.CreationError(stderr) if retcode: raise errors.Resource.CreationError( '%s return code: %s' % (retcode, stderr))
def _Create(self): """Creates the AWS CapacaityReservation. A reservation will be created given the VM shape in self.vm_groups. Count is determined by the number of VMs in said group. The reservation will have a lifetime determined by the general PKB concept of timeout_minutes. If the reservation exceeds this timeout, AWS will cancel it automatically. The VMs in the reservation will not be deleted. Note that an empty capacity reservation will encur costs for the VM shape / count, even if no VMs are using it. After the reservation is created, this method updates all the VMs in self.vm_groups by setting the capacity_reservation_id, as well as the zone attributes on the VM, and the VM's network instance. Raises: UnsupportedOsTypeError: If creating a capacity reservation for the given os type is not supported. CreationError: If a capacity reservation cannot be created in the region (typically indicates a stockout). """ if self.os_type in os_types.LINUX_OS_TYPES: instance_platform = 'Linux/UNIX' elif self.os_type in os_types.WINDOWS_OS_TYPES: instance_platform = 'Windows' else: raise UnsupportedOsTypeError( 'Unsupported os_type for AWS CapacityReservation: %s.' % self.os_type) # If the user did not specify an AZ, we need to try to create the # CapacityReservation in a specifc AZ until it succeeds. # Then update the zone attribute on all the VMs in the group, # as well as the zone attribute on the VMs' network instance. if util.IsRegion(self.zone_or_region): zones_to_try = util.GetZonesInRegion(self.region) else: zones_to_try = [self.zone_or_region] end_date = ( datetime.datetime.utcnow() + datetime.timedelta(minutes=FLAGS.timeout_minutes)) for zone in zones_to_try: cmd = util.AWS_PREFIX + [ 'ec2', 'create-capacity-reservation', '--instance-type=%s' % self.machine_type, '--instance-platform=%s' % instance_platform, '--availability-zone=%s' % zone, '--instance-count=%s' % self.vm_count, '--instance-match-criteria=targeted', '--region=%s' % self.region, '--end-date-type=limited', '--end-date=%s' % end_date, ] stdout, stderr, retcode = vm_util.IssueCommand(cmd) if retcode: logging.info('Unable to create CapacityReservation in %s. ' 'This may be retried. Details: %s', zone, stderr) continue json_output = json.loads(stdout) self.capacity_reservation_id = ( json_output['CapacityReservation']['CapacityReservationId']) self._UpdateVmsInGroup(self.capacity_reservation_id, zone) return raise CreationError('Unable to create CapacityReservation in any of the ' 'following zones: %s.' % zones_to_try)
def _CreateSpot(self): """Create a Spot VM instance.""" placement = OrderedDict() if not util.IsRegion(self.zone): placement['AvailabilityZone'] = self.zone if self.use_dedicated_host: raise errors.Resource.CreationError( 'Tenancy=host is not supported for Spot Instances') elif IsPlacementGroupCompatible(self.machine_type): placement['GroupName'] = self.network.placement_group.name block_device_map = GetBlockDeviceMap(self.machine_type, self.boot_disk_size, self.image, self.region) network_interface = [ OrderedDict([('DeviceIndex', 0), ('AssociatePublicIpAddress', True), ('SubnetId', self.network.subnet.id)]) ] launch_specification = OrderedDict([ ('ImageId', self.image), ('InstanceType', self.machine_type), ('KeyName', 'perfkit-key-%s' % FLAGS.run_uri), ('Placement', placement) ]) if block_device_map: launch_specification['BlockDeviceMappings'] = json.loads( block_device_map, object_pairs_hook=collections.OrderedDict) launch_specification['NetworkInterfaces'] = network_interface create_cmd = util.AWS_PREFIX + [ 'ec2', 'request-spot-instances', '--region=%s' % self.region, '--spot-price=%s' % self.spot_price, '--client-token=%s' % self.client_token, '--launch-specification=%s' % json.dumps(launch_specification, separators=(',', ':')) ] stdout, _, _ = vm_util.IssueCommand(create_cmd) create_response = json.loads(stdout) self.spot_instance_request_id = ( create_response['SpotInstanceRequests'][0]['SpotInstanceRequestId'] ) util.AddDefaultTags(self.spot_instance_request_id, self.region) while True: describe_sir_cmd = util.AWS_PREFIX + [ '--region=%s' % self.region, 'ec2', 'describe-spot-instance-requests', '--spot-instance-request-ids=%s' % self.spot_instance_request_id ] stdout, _, _ = vm_util.IssueCommand(describe_sir_cmd) sir_response = json.loads(stdout)['SpotInstanceRequests'] assert len( sir_response) == 1, 'Expected exactly 1 SpotInstanceRequest' status_code = sir_response[0]['Status']['Code'] if (status_code in SPOT_INSTANCE_REQUEST_HOLDING_STATUSES or status_code in SPOT_INSTANCE_REQUEST_TERMINAL_STATUSES): message = sir_response[0]['Status']['Message'] raise errors.Resource.CreationError(message) elif status_code == 'fulfilled': self.id = sir_response[0]['InstanceId'] break time.sleep(2)