Esempio n. 1
0
    def set_user_gl_name(self, id, gl_name):
        self.__insert_user(id)

        try:
            with self.__conn:
                self.__conn.execute(
                    'update users set gl_name = ? where id = ?', (gl_name, id))
        except sqlite3.IntegrityError:
            # Assume duplicate gl_name
            if (conflicting_id :=
                    self.try_user_id_from_gl_name(gl_name)) is not None:
                raise common.Error(
                    f'That name is already taken by <@{conflicting_id}>.')
            raise common.Error('Someone else has taken that name already.')
def get_or_create_net(prop, name, resources, gw_dependencies,
                      private_google_access=False, create_firewall=False):
    net_cidr = prop.get(name + '-cidr')

    if net_cidr:
        net_name = '{}-{}'.format(prop['deployment'][:22], name)
        subnet_name = '{}-subnet'.format(net_name)
        net = make_net(net_name)
        subnet = make_subnet(
            prop, subnet_name, net_name, net_cidr, private_google_access)

        resources += [net, subnet]
        gw_dependencies.append(subnet_name)
    else:
        net_name = prop.get(name + '-name')
        subnet_name = prop.get(name + '-subnetwork-name')
        if not subnet_name:
            raise common.Error(
                'Network {} is missing.'.format(net_name.split('-')))

    if create_firewall:
        firewall_rules = create_firewall_rules(prop, name, net_name, net_cidr)
        if firewall_rules:
            resources.extend(firewall_rules)

    return net_name, subnet_name
Esempio n. 3
0
def GenerateMultipleComputeVMs(context):
    """Generates multiple VMs that are copies of a single VM spec."""
    prop = context.properties
    if VM_COPIES not in prop:
        raise common.Error('%s property is needed for multiple VM generation' %
                           VM_COPIES)
    use_endpoint = ENDPOINT_NAME in prop
    n_of_copies = prop[VM_COPIES]
    resources = []
    new_disks = []
    for idx in range(1, n_of_copies + 1):
        ctx = copy.deepcopy(context)
        idx_prop = ctx.properties
        ctx.env[default.NAME] += AddIdx(idx)
        # Do something with the instance name and with the disk names
        disk_prefix = ctx.env[default.NAME]
        if default.INSTANCE_NAME in idx_prop:
            idx_prop[default.INSTANCE_NAME] += AddIdx(idx)
            disk_prefix = idx_prop[default.INSTANCE_NAME]
        if default.DISKS in idx_prop:
            # Modifies the disks in idx_prop to have a unique name
            # Adding the vm extension to match the final hostname
            NameTheDisks(idx_prop[default.DISKS], disk_prefix)
        if use_endpoint:
            idx_prop[ENDPOINT_NAME] += AddIdx(idx)
        resources += vm_instance.GenerateComputeVM(ctx)
        resources += vm_instance.AddServiceEndpointIfNeeded(ctx)
        new_disks += common.AddDiskResourcesIfNeeded(ctx)
    AddDisksToContext(context, new_disks)
    return resources
Esempio n. 4
0
def validate_region(test_zone, valid_region):
    test_region = common.ZoneToRegion(test_zone)
    if test_region != valid_region:
        err_msg = '{} is in region {}. All subnets must be ' + \
                  'in the same region ({})'
        raise common.Error(err_msg.format(test_zone, test_region,
                                          valid_region))
Esempio n. 5
0
 def get_user_gl_name(self, id):
     for row in self.__conn.execute(
             'select gl_name from users where id = ?', (id, )):
         if row[0] is not None:
             return row[0]
     raise common.Error(
         f'I don\'t know what name <@{id}> goes by in the GL spreadsheet. '
         f'<@{id}> please tell me your name with **!iam Whoever**.')
Esempio n. 6
0
def _get_locs(rows):
    locs = {field.name: _Loc(field) for field in _fields}
    cont_loc = None
    for col, heading in enumerate(rows[_heading_row]):
        if heading:
            cont_loc = None
            for loc in locs.values():
                heading_re = loc.field.heading_re
                if heading_re.search(heading):
                    if loc.begin_col is not None:
                        raise common.Error(
                            f'I found multiple headings matching "{heading_re.pattern}" in the GL spreadsheet.')
                    loc.begin_col = col
                    loc.end_col = col + 1
                    if loc.field.sub_heading_row is not None:
                        cont_loc = loc
                    break
        elif cont_loc is not None:
            if rows[cont_loc.field.sub_heading_row][col]:
                cont_loc.end_col = col + 1
            else:
                cont_loc = None

    for loc in locs.values():
        if loc.begin_col is None:
            raise common.Error(
                f'I couldn\'t find a heading matching "{loc.field.heading_re.pattern}" in the GL spreadsheet.')

        if loc.field.sub_heading_row is not None:
            loc.sub_headings = rows[loc.field.sub_heading_row][loc.begin_col:loc.end_col]

            seen = set()
            for sub_heading in loc.sub_headings:
                if sub_heading in seen:
                    raise common.Error(
                        'There are multiple columns in the GL spreadsheet under '
                        f'{rows[_heading_row][loc.begin_col]} with the same '
                        f'sub-heading ({sub_heading}).')
                seen.add(sub_heading)

    return locs
def GenerateDisks(context, disk_list, new_disks):
    """Generates as many disks as passed in the disk_list."""
    project = context.env[default.PROJECT]
    prop = context.properties
    zone = prop.setdefault(ZONE, DEFAULT_ZONE)
    sourced_disks = []
    disk_names = []
    for disk in disk_list:
        d_name = (disk[default.DEVICE_NAME] if default.DISK_NAME not in disk
                  else disk[default.DISK_NAME])
        if default.DISK_SOURCE in disk:  # Existing disk, expect disk api link
            source = disk[default.DISK_SOURCE]
        else:  # The disks should be create separately
            if default.DEVICE_NAME not in disk and default.DISK_NAME not in disk:
                raise common.Error(
                    'deviceName or diskName is needed for each disk in '
                    'this module implemention of multiple disks per vm.')
            disk_init = disk.setdefault(default.INITIALIZEP, dict())
            disk_size = disk_init.setdefault(default.DISK_SIZE,
                                             DEFAULT_DATADISKSIZE)
            passed_disk_type = disk_init.setdefault(default.TYPE,
                                                    DEFAULT_DISKTYPE)
            disk_type = common.LocalComputeLink(project, zone, 'diskTypes',
                                                passed_disk_type)
            new_disks.append({
                'name': d_name,
                'type': default.DISK,
                'properties': {
                    'type': disk_type,
                    'sizeGb': disk_size,
                    'zone': zone
                }
            })  # pyformat: disable
            disk_names.append(d_name)
            source = common.Ref(d_name)
        sourced_disks.append({
            'deviceName': d_name,
            'autoDelete': True,
            'boot': False,
            'source': source,
            'type': DEFAULT_PERSISTENT,
        })

    items = prop[METADATA].setdefault('items', list())
    items.append({'key': ATTACHED_DISKS, 'value': ','.join(disk_names)})
    return sourced_disks, new_disks
def GenerateDisks(context, disk_list, new_disks):
    """Generates as many disks as passed in the disk_list."""
    prop = context.properties
    zone = prop.setdefault(ZONE, DEFAULT_ZONE)
    sourced_disks = []
    disk_names = []
    for disk in disk_list:
        if default.DISK_SOURCE in disk or disk[default.TYPE] == SCRATCH:
            # These disks do not need to be created as separate resources
            sourced_disks.append(disk)
        else:
            # Extract disk parameters and create as separate resource
            disk_init = disk[default.INITIALIZEP]
            if default.DEVICE_NAME in disk:
                d_name = disk[default.DEVICE_NAME]
            elif default.DISK_NAME in disk_init:
                d_name = disk_init[default.DISK_NAME]
            else:
                raise common.Error(
                    'deviceName or diskName is needed for each disk in '
                    'this module implemention of multiple disks per vm.')
            new_disks.append({
                'name': d_name,
                'type': default.DISK,
                'properties': {
                    'type': disk_init[default.DISKTYPE],
                    'sizeGb': disk_init[default.DISK_SIZE],
                    'zone': zone
                }
            })
            disk_names.append(d_name)
            source = common.Ref(d_name)
            sourced_disks.append({
                'deviceName': d_name,
                'autoDelete': disk[default.AUTO_DELETE],
                'boot': False,
                'source': source,
                'type': disk[default.TYPE],
            })
    items = prop[METADATA].setdefault('items', list())
    items.append({'key': ATTACHED_DISKS, 'value': ','.join(disk_names)})
    return sourced_disks, new_disks
Esempio n. 9
0
def GenerateDisks(context, disk_list, new_disks):
    """Generates as many disks as passed in the disk_list."""
    prop = context.properties
    zone = prop.setdefault(ZONE, DEFAULT_ZONE)
    sourced_disks = []
    disk_names = []
    for disk in disk_list:
        if default.DISK_SOURCE in disk or disk[default.TYPE] == SCRATCH:
            # These disks do not need to be created as separate resources
            sourced_disks.append(disk)
        else:
            # Extract disk parameters and create as separate resource
            disk_init = disk[default.INITIALIZEP]
            if default.DEVICE_NAME in disk:
                d_name = disk[default.DEVICE_NAME]
            elif default.DISK_NAME in disk_init:
                d_name = disk_init[default.DISK_NAME]
            else:
                raise common.Error(
                    "deviceName or diskName is needed for each disk in "
                    "this module implemention of multiple disks per vm.")
            new_disks.append({
                "name": d_name,
                "type": default.DISK,
                "properties": {
                    "type": disk_init[default.DISKTYPE],
                    "sizeGb": disk_init[default.DISK_SIZE],
                    "zone": zone,
                },
            })
            disk_names.append(d_name)
            source = common.Ref(d_name)
            sourced_disks.append({
                "deviceName": d_name,
                "autoDelete": disk[default.AUTO_DELETE],
                "boot": False,
                "source": source,
                "type": disk[default.TYPE],
            })
    items = prop[METADATA].setdefault("items", list())
    items.append({"key": ATTACHED_DISKS, "value": ",".join(disk_names)})
    return sourced_disks, new_disks
def GetNetworkInterfaces(context):
  """Extracts the network interfaces to be used in the VM creation."""
  props = context.properties

  networks = props.setdefault(NETWORKS, DEFAULT_NETWORKS)
  subnetworks = props.get(SUBNETWORKS, [])
  external_ips = props.get(EXTERNAL_IPS, DEFAULT_EXTERNAL_IPS)

  network_interfaces = []
  for i in range(len(networks)):
    name = 'Interface %d' % i
    network_interface = {
        'network': common.MakeNetworkComputeLink(context, networks[i]),
        'name': name,
    }

    if subnetworks and i < len(subnetworks) and subnetworks[i]:
      network_interface['subnetwork'] = common.MakeSubnetworkComputeLink(
          context, subnetworks[i])

    ip_value = external_ips[i] if i < len(external_ips) else 'NONE'
    is_static_ip = VALID_IP_RE.match(ip_value) is not None
    if ip_value != 'EPHEMERAL' and ip_value != 'NONE' and not is_static_ip:
      raise common.Error(
          ('External IP value "%s" is invalid. Valid values '
           'are: a valid IP Address, EPHEMERAL or NONE.') % ip_value)

    if ip_value == 'EPHEMERAL' or is_static_ip:
      access_config = {
          'name': '%s %s' % (name, default.EXTERNAL),
          'type': default.ONE_NAT,
      }

      if is_static_ip:
        access_config[default.NAT_IP] = ip_value

      network_interface['accessConfigs'] = [access_config]

    network_interfaces.append(network_interface)

  return network_interfaces
Esempio n. 11
0
def gather_messages(MessageClass, tcpdir):
    '''
    Attempts to construct a series of MessageClass objects from the data. The
    basic idea comes from pyper's function, HTTPFlow.analyze.gather_messages.
    Args:
    * MessageClass = class, Request or Response
    * tcpdir = TCPDirection, from which will be extracted the data
    Returns:
    [MessageClass]

    If the first message fails to construct, the flow is considered to be
    invalid. After that, all messages are stored and returned. The end of the
    data is an invalid message. This is designed to handle partially valid HTTP
    flows semi-gracefully: if the flow is bad, the application probably bailed
    on it after that anyway.
    '''
    messages = []  # [MessageClass]
    pointer = 0  # starting index of data that MessageClass should look at
    # while there's data left
    while pointer < len(tcpdir.data):
        curr_data = tcpdir.data[pointer:pointer + 200]  # debug var
        try:
            msg = MessageClass(tcpdir, pointer)
        except (dpkt.Error, ):  # if the message failed
            error = sys.exc_info()[1]
            if pointer == 0:  # if this is the first message
                raise http.Error('Invalid http')
            else:  # we're done parsing messages
                logging.warning("We got a dpkt.Error %s, but we are done." %
                                error)
                break  # out of the loop
        except:
            raise
        # ok, all good
        messages.append(msg)
        pointer += msg.data_consumed
    return messages
def GenerateComputeVM(context):
    """Generates one VM instance resource."""
    name = context.env['name']
    prop = context.properties
    if SRCIMAGE not in prop:
        raise common.Error('"%s" is a mandatory property' % SRCIMAGE)
    boot_disk_type = prop.setdefault(BOOTDISKTYPE, DEFAULT_DISKTYPE)
    prop[default.DISKTYPE] = boot_disk_type
    can_ip_fwd = prop.setdefault(CAN_IP_FWD, DEFAULT_IP_FWD)
    disks = prop.setdefault(default.DISKS, list())
    # Temporary alternative while multiple disks on creation is not allowed
    if disks:
        new_disks = prop.setdefault(default.DISK_RESOURCES, list())
        disks, prop[DISK_RESOURCES] = GenerateDisks(context, disks, new_disks)
    machine_type = prop.setdefault(MACHINETYPE, DEFAULT_MACHINETYPE)
    metadata = prop.setdefault(METADATA, dict())
    network = prop.setdefault(NETWORK, DEFAULT_NETWORK)
    named = INSTANCE_NAME in prop
    provide_boot = prop.setdefault(PROVIDE_BOOT, DEFAULT_PROVIDE_BOOT)
    tags = prop.setdefault(TAGS, dict([('items', [])]))
    vm_name = prop[INSTANCE_NAME] if named else common.AutoName(
        name, default.INSTANCE)
    zone = prop.setdefault(ZONE, DEFAULT_ZONE)
    if provide_boot:
        dev_mode = DEVIMAGE in prop and prop[DEVIMAGE]
        src_image = common.MakeC2DImageLink(prop[SRCIMAGE], dev_mode)
        boot_name = common.AutoName(context.env['name'], default.DISK, 'boot')
        disk_size = (prop[BOOTDISKSIZE]
                     if BOOTDISKSIZE in prop else DEFAULT_BOOTDISKSIZE)
        disk_type = common.MakeLocalComputeLink(context, default.DISKTYPE)
        disks = PrependBootDisk(disks, boot_name, disk_type, disk_size,
                                src_image)
    machine_type = common.MakeLocalComputeLink(context, default.MACHINETYPE)
    network = common.MakeGlobalComputeLink(context, default.NETWORK)
    # To be consistent with Dev console and gcloud, service accounts need to be
    #  explicitly disabled
    remove_scopes = prop[NO_SCOPE] if NO_SCOPE in prop else False
    if remove_scopes and SERVICE_ACCOUNTS in prop:
        prop.pop(SERVICE_ACCOUNTS)
    else:  # Make sure there is a default service account
        prop.setdefault(SERVICE_ACCOUNTS,
                        copy.deepcopy(DEFAULT_SERVICE_ACCOUNT))

    # pyformat: disable
    resource = [{
        'name': vm_name,
        'type': default.INSTANCE,
        'properties': {
            'zone':
            zone,
            'machineType':
            machine_type,
            'canIpForward':
            can_ip_fwd,
            'disks':
            disks,
            'networkInterfaces': [{
                'network':
                network,
                'accessConfigs': [{
                    'name': default.EXTERNAL,
                    'type': default.ONE_NAT
                }]
            }],
            'tags':
            tags,
            'metadata':
            metadata,
        }
    }]
    # pyformat: enable

    # Pass through any additional property to the VM
    if SERVICE_ACCOUNTS in prop:
        resource[0]['properties'].update(
            {SERVICE_ACCOUNTS: prop[SERVICE_ACCOUNTS]})
    return resource
Esempio n. 13
0
File: ips.py Progetto: qguv/binjgb-1
 def ReadBytes(self, length):
     if self.offset + length > len(self.data):
         raise common.Error('Bad IPS file.')
     result = self.data[self.offset:self.offset + length]
     self.offset += length
     return result
Esempio n. 14
0
File: ips.py Progetto: qguv/binjgb-1
 def __init__(self, data):
     self.data = data
     if self.data[:5] != b'PATCH':
         raise common.Error('Bad IPS file.')
     self.offset = 5
def validate_same_region(zone_a, zone_b):
    if not common.ZoneToRegion(zone_a) == common.ZoneToRegion(zone_b):
        raise common.Error('Member A Zone ({}) and Member B Zone ({}) '
                           'are not in the same region'.format(zone_a, zone_b))
Esempio n. 16
0
def validate_networks(internal, external):
    if external == internal:
        err_msg = 'Each network must be different, ' + \
                  'but both External and Internal networks are set to {}'
        raise common.Error(err_msg.format(external))
def GenerateDisks(context, disk_list, new_disks):
    """Generates as many disks as passed in the disk_list."""
    project = context.env[default.PROJECT]
    prop = context.properties
    zone = prop.setdefault(ZONE, DEFAULT_ZONE)
    sourced_disks = []
    disk_names = []
    for disk in disk_list:
        d_name = (disk[default.DEVICE_NAME] if default.DISK_NAME not in disk
                  else disk[default.DISK_NAME])
        d_autodelete = (True if default.AUTO_DELETE not in disk else
                        disk[default.AUTO_DELETE])
        d_type = (DEFAULT_PERSISTENT
                  if default.TYPE not in disk else disk[default.TYPE])

        if default.DISK_SOURCE in disk:  # Existing disk, expect disk api link
            source = disk[default.DISK_SOURCE]
        elif d_type == SCRATCH:  # No special treatment needed for SSD disk
            if default.INITIALIZEP in disk:
                disk_type = disk[default.INITIALIZEP][DISKTYPE]
            else:
                disk_type = 'local-ssd'
            disk[default.INITIALIZEP] = {
                DISKTYPE:
                common.LocalComputeLink(project, zone, 'diskTypes', disk_type)
            }
            sourced_disks.append(disk)
            continue
        else:
            if default.DEVICE_NAME not in disk and default.DISK_NAME not in disk:
                raise common.Error(
                    'deviceName or diskName is needed for each disk in '
                    'this module implemention of multiple disks per vm.')
            # In the Instance API reference, size and type are within this property
            if default.INITIALIZEP in disk:
                disk_init = disk[default.INITIALIZEP]
                disk_size = disk_init.setdefault(default.DISK_SIZE,
                                                 DEFAULT_DATADISKSIZE)
                passed_disk_type = disk_init.setdefault(
                    DISKTYPE, DEFAULT_DISKTYPE)
            # You can also simply pass the size and type properties directly
            else:
                disk_size = disk.setdefault(default.DISK_SIZE,
                                            DEFAULT_DATADISKSIZE)
                passed_disk_type = disk.setdefault(DISKTYPE, DEFAULT_DISKTYPE)
            disk_type = common.LocalComputeLink(project, zone, 'diskTypes',
                                                passed_disk_type)
            new_disks.append({
                'name': d_name,
                'type': default.DISK,
                'properties': {
                    'type': disk_type,
                    'sizeGb': disk_size,
                    'zone': zone
                }
            })  # pyformat: disable
            disk_names.append(d_name)
            source = common.Ref(d_name)
        sourced_disks.append({
            'deviceName': d_name,
            'autoDelete': d_autodelete,
            'boot': False,
            'source': source,
            'type': d_type,
        })

    items = prop[METADATA].setdefault('items', list())
    items.append({'key': ATTACHED_DISKS, 'value': ','.join(disk_names)})
    return sourced_disks, new_disks
Esempio n. 18
0
def GenerateComputeVM(context, create_disks_separately=True):
  """Generates one VM instance resource.

  Args:
    context: Template context dictionary.
    create_disks_separately: When true (default), all new disk resources are
      created as separate resources. This is legacy behaviour from when multiple
      disks creation was not allowed in the disks property.
  Returns:
    dictionary representing instance resource.
  """
  prop = context.properties
  boot_disk_type = prop.setdefault(BOOTDISKTYPE, DEFAULT_DISKTYPE)
  prop[default.DISKTYPE] = boot_disk_type
  can_ip_fwd = prop.setdefault(CAN_IP_FWD, DEFAULT_IP_FWD)
  disks = prop.setdefault(default.DISKS, list())
  local_ssd = prop.setdefault(default.LOCAL_SSD, 0)

  if disks:
    if create_disks_separately:
      # Legacy alternative from when multiple disks on creation were not allowed
      new_disks = prop.setdefault(default.DISK_RESOURCES, list())
      SetDiskProperties(context, disks)
      disks, prop[DISK_RESOURCES] = GenerateDisks(context, disks, new_disks)
    else:
      # All new disks (except local ssd) must provide a sourceImage or existing
      # source. Add blank source image if non provided.
      SetDiskProperties(context, disks, add_blank_src_img=True)

  machine_type = prop.setdefault(MACHINETYPE, DEFAULT_MACHINETYPE)
  metadata = prop.setdefault(METADATA, dict())
  SetMetadataDefaults(metadata)
  network = prop.setdefault(NETWORK, DEFAULT_NETWORK)
  vm_name = MakeVMName(context)
  provide_boot = prop.setdefault(PROVIDE_BOOT, DEFAULT_PROVIDE_BOOT)
  tags = prop.setdefault(TAGS, dict([('items', [])]))
  zone = prop.setdefault(ZONE, DEFAULT_ZONE)
  has_external_ip = prop.get(HAS_EXTERNAL_IP, DEFAULT_HAS_EXTERNAL_IP)
  static_ip = prop.get(STATIC_IP, DEFAULT_STATIC_IP)
  nat_ip = prop.get(NAT_IP, None)

  if provide_boot:
    dev_mode = DEVIMAGE in prop and prop[DEVIMAGE]
    src_image = common.MakeC2DImageLink(prop[SRCIMAGE], dev_mode)
    boot_name = common.AutoName(context.env['name'], default.DISK, 'boot')
    disk_size = prop.get(BOOTDISKSIZE, DEFAULT_BOOTDISKSIZE)
    disk_type = common.MakeLocalComputeLink(context, DISKTYPE)
    autodelete = prop.get(AUTODELETE_BOOTDISK, DEFAULT_AUTODELETE_BOOTDISK)
    disks = PrependBootDisk(disks, boot_name, disk_type, disk_size, src_image,
                            autodelete)
  if local_ssd:
    disks = AppendLocalSSDDisks(context, disks, local_ssd)
  machine_type = common.MakeLocalComputeLink(context, default.MACHINETYPE)
  network = common.MakeGlobalComputeLink(context, default.NETWORK)
  subnetwork = ''
  if default.SUBNETWORK in prop:
    subnetwork = common.MakeSubnetworkComputeLink(context, default.SUBNETWORK)

  # To be consistent with Dev console and gcloud, service accounts need to be
  #  explicitly disabled
  remove_scopes = prop[NO_SCOPE] if NO_SCOPE in prop else False
  if remove_scopes and SERVICE_ACCOUNTS in prop:
    prop.pop(SERVICE_ACCOUNTS)
  else:  # Make sure there is a default service account
    prop.setdefault(SERVICE_ACCOUNTS, copy.deepcopy(DEFAULT_SERVICE_ACCOUNT))

  resource = []

  access_configs = []
  if has_external_ip:
    access_config = {'name': default.EXTERNAL, 'type': default.ONE_NAT}
    access_configs.append(access_config)
    if static_ip and nat_ip:
      raise common.Error(
          'staticIP=True and natIP cannot be specified at the same time')
    if static_ip:
      address_resource, nat_ip = MakeStaticAddress(vm_name, zone)
      resource.append(address_resource)
    if nat_ip:
      access_config['natIP'] = nat_ip
  else:
    if static_ip:
      raise common.Error('staticIP cannot be True when hasExternalIP is False')
    if nat_ip:
      raise common.Error(
          'natIP must not be specified when hasExternalIP is False')

  network_interfaces = []
  if subnetwork:
    network_interfaces.insert(0, {
        'network': network,
        'subnetwork': subnetwork,
        'accessConfigs': access_configs
    })
  else:
    network_interfaces.insert(0, {
        'network': network,
        'accessConfigs': access_configs
    })

  resource.insert(0, {
      'name': vm_name,
      'type': default.INSTANCE,
      'properties': {
          'zone': zone,
          'machineType': machine_type,
          'canIpForward': can_ip_fwd,
          'disks': disks,
          'networkInterfaces': network_interfaces,
          'tags': tags,
          'metadata': metadata,
      }
  })

  # Pass through any additional properties to the VM
  if SERVICE_ACCOUNTS in prop:
    resource[0]['properties'].update({SERVICE_ACCOUNTS: prop[SERVICE_ACCOUNTS]})
  if GUEST_ACCELERATORS in prop:
    for accelerators in prop[GUEST_ACCELERATORS]:
      accelerators['acceleratorType'] = common.MakeAcceleratorTypeLink(
          context, accelerators['acceleratorType'])
    resource[0]['properties'].update(
        {GUEST_ACCELERATORS: prop[GUEST_ACCELERATORS]})
    # GPUs cannot be attached to live migratable instances. See:
    # https://cloud.google.com/compute/docs/gpus/#restrictions
    resource[0]['properties'].update(
        {'scheduling': {'onHostMaintenance': 'terminate'}})
  return resource
def GenerateComputeVM(context):
    """Generates one VM instance resource."""
    prop = context.properties
    boot_disk_type = prop.setdefault(BOOTDISKTYPE, DEFAULT_DISKTYPE)
    prop[default.DISKTYPE] = boot_disk_type
    can_ip_fwd = prop.setdefault(CAN_IP_FWD, DEFAULT_IP_FWD)
    disks = prop.setdefault(default.DISKS, list())
    local_ssd = prop.setdefault(default.LOCAL_SSD, 0)
    # Temporary alternative while multiple disks on creation is not allowed
    if disks:
        new_disks = prop.setdefault(default.DISK_RESOURCES, list())
        disks, prop[DISK_RESOURCES] = GenerateDisks(context, disks, new_disks)
    machine_type = prop.setdefault(MACHINETYPE, DEFAULT_MACHINETYPE)
    metadata = prop.setdefault(METADATA, dict())
    network = prop.setdefault(NETWORK, DEFAULT_NETWORK)
    vm_name = MakeVMName(context)
    provide_boot = prop.setdefault(PROVIDE_BOOT, DEFAULT_PROVIDE_BOOT)
    tags = prop.setdefault(TAGS, dict([('items', [])]))
    zone = prop.setdefault(ZONE, DEFAULT_ZONE)
    has_external_ip = prop.get(HAS_EXTERNAL_IP, DEFAULT_HAS_EXTERNAL_IP)
    static_ip = prop.get(STATIC_IP, DEFAULT_STATIC_IP)
    nat_ip = prop.get(NAT_IP, None)
    if provide_boot:
        dev_mode = DEVIMAGE in prop and prop[DEVIMAGE]
        src_image = common.MakeC2DImageLink(prop[SRCIMAGE], dev_mode)
        boot_name = common.AutoName(context.env['name'], default.DISK, 'boot')
        disk_size = prop.get(BOOTDISKSIZE, DEFAULT_BOOTDISKSIZE)
        disk_type = common.MakeLocalComputeLink(context, DISKTYPE)
        autodelete = prop.get(AUTODELETE_BOOTDISK, DEFAULT_AUTODELETE_BOOTDISK)
        disks = PrependBootDisk(disks, boot_name, disk_type, disk_size,
                                src_image, autodelete)
    if local_ssd:
        disks = AppendLocalSSDDisks(context, disks, local_ssd)
    machine_type = common.MakeLocalComputeLink(context, default.MACHINETYPE)
    network = common.MakeGlobalComputeLink(context, default.NETWORK)
    subnetwork = ''
    if default.SUBNETWORK in prop:
        subnetwork = common.MakeSubnetworkComputeLink(context,
                                                      default.SUBNETWORK)

    # To be consistent with Dev console and gcloud, service accounts need to be
    #  explicitly disabled
    remove_scopes = prop[NO_SCOPE] if NO_SCOPE in prop else False
    if remove_scopes and SERVICE_ACCOUNTS in prop:
        prop.pop(SERVICE_ACCOUNTS)
    else:  # Make sure there is a default service account
        prop.setdefault(SERVICE_ACCOUNTS,
                        copy.deepcopy(DEFAULT_SERVICE_ACCOUNT))

    resource = []

    access_configs = []
    if has_external_ip:
        access_config = {'name': default.EXTERNAL, 'type': default.ONE_NAT}
        access_configs.append(access_config)
        if static_ip and nat_ip:
            raise common.Error(
                'staticIP=True and natIP cannot be specified at the same time')
        if static_ip:
            address_resource, nat_ip = MakeStaticAddress(vm_name, zone)
            resource.append(address_resource)
        if nat_ip:
            access_config['natIP'] = nat_ip
    else:
        if static_ip:
            raise common.Error(
                'staticIP cannot be True when hasExternalIP is False')
        if nat_ip:
            raise common.Error(
                'natIP must not be specified when hasExternalIP is False')

    network_interfaces = []
    if subnetwork:
        network_interfaces.insert(
            0, {
                'network': network,
                'subnetwork': subnetwork,
                'accessConfigs': access_configs
            })
    else:
        network_interfaces.insert(0, {
            'network': network,
            'accessConfigs': access_configs
        })

    resource.insert(
        0, {
            'name': vm_name,
            'type': default.INSTANCE,
            'properties': {
                'zone': zone,
                'machineType': machine_type,
                'canIpForward': can_ip_fwd,
                'disks': disks,
                'networkInterfaces': network_interfaces,
                'tags': tags,
                'metadata': metadata,
            }
        })

    # Pass through any additional property to the VM
    if SERVICE_ACCOUNTS in prop:
        resource[0]['properties'].update(
            {SERVICE_ACCOUNTS: prop[SERVICE_ACCOUNTS]})
    return resource