Beispiel #1
0
def _take_snapshot(the_vm, dump_memory=True, quiesce=False, description=''):
    """Take a new snapshot of the virtual machine.

    :Returns: Tuple (snap_id, created_timestamp, expires_timesampt)

    :param the_vm: The virtual machine with snapshots to delete
    :type the_vm: vim.VirtualMachine

    :param dump_memory: When True, includes the running memory state of the VM in
                        the snapshot. Default True.
    :type dump_memory: Boolean

    :param quiesce: When the VM  has VMwareTools installed, setting this flag to
                    True waits to create a snapshot until after buffers in the VM
                    are flushed to disk. Useless to use when ``dump_memory`` is
                    set to True. Default False
    :type quiesce: Boolean
    """
    created = int(time.time())
    snap_id = '{}'.format(uuid.uuid4())[:6]
    expires = created + const.VLAB_SNAPSHOT_EXPIRES_AFTER
    snap_name = '{}_{}_{}'.format(snap_id, created, expires)
    consume_task(the_vm.CreateSnapshot(snap_name, description, dump_memory,
                                       quiesce),
                 timeout=1800)
    return snap_id, created, expires
Beispiel #2
0
def reap_snapshots(vcenter, logger):
    """Walk the VMs owned by users in vLab, and delete all expired VM snapshots.

    :Returns: None

    :param vcenter: The vCenter server that hosts the user's Virtual Machines
    :type vcenter: vlab_inf_common.vmware.vCenter

    :param logger: Handles logging messages while the reaper runs
    :type logger: logging.Logger
    """
    all_users = vcenter.get_by_name(name=const.INF_VCENTER_USERS_DIR,
                                    vimtype=vim.Folder)
    for username in all_users.childEntity:
        vms = vcenter.get_by_name(name=username.name, vimtype=vim.Folder)
        for vm in vms.childEntity:
            if vm.snapshot:
                vm_snaps = _get_snapshots(vm.snapshot.rootSnapshotList)
                for snap in vm_snaps:
                    if is_expired(snap.name):
                        logger.info(
                            "deleteing snap {} of VM {} owned by {}".format(
                                snap, vm.name, username))
                        consume_task(
                            snap.snapshot.RemoveSnapshot_Task(
                                removeChildren=False))
Beispiel #3
0
def add_unique_params(the_vm, ip_config):
    """Superna cannot boot without some required network parameters configured.

    :Returns: None

    :param the_vm: The newly created Superna server.
    :type the_vm: vim.VirtualMachine

    :param ip_config: The IPv4 network configuration for the Avamar instance.
    :type ip_config: Dictionary
    """
    vapp_spec = vim.vApp.VmConfigSpec()
    mapping = {
        'eth0.ipv4.ip': ip_config['static-ip'],
        'eth0.ipv4.netmask': ip_config['netmask'],
        'eth0.ipv4.gateway': ip_config['default-gateway'],
        'hostname': the_vm.name,
        'nameservers': ' '.join(ip_config['dns'])
    }
    for prop in the_vm.config.vAppConfig.property:
        if prop.id in mapping:
            config = vim.vApp.PropertySpec()
            config.operation = 'edit'
            config.info = prop
            config.info.value = mapping[prop.id]
            vapp_spec.property.append(config)

    spec = vim.vm.ConfigSpec()
    spec.vAppConfig = vapp_spec
    task = the_vm.ReconfigVM_Task(spec)
    consume_task(task)
Beispiel #4
0
def delete_deployment(username, machine_name, logger):
    """Unregister and destroy a user's Deployment

    :Returns: None

    :param username: The user who wants to delete their jumpbox
    :type username: String

    :param machine_name: The name of the VM to delete
    :type machine_name: String

    :param logger: An object for logging messages
    :type logger: logging.LoggerAdapter
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER, \
                 password=const.INF_VCENTER_PASSWORD) as vcenter:
        folder = vcenter.get_by_name(name=username, vimtype=vim.Folder)
        tasks = []
        for entity in folder.childEntity:
            info = virtual_machine.get_info(vcenter, entity, username)
            if info['meta'].get('deployment', False) == True:
                logger.debug('powering off VM %s', entity.name)
                virtual_machine.power(entity, state='off')
                delete_task = entity.Destroy_Task()
                tasks.append(delete_task)
        if tasks:
            logger.debug('blocking while VMs are being destroyed')
            for task in tasks:
                consume_task(task)

        else:
            raise ValueError('No {} named {} found'.format('deployment', machine_name))
Beispiel #5
0
def create_network(name, vlan_id, switch_name):
    """Create a new network for VMs.

    :Returns: String (error message)

    :param name: The name of the new distributed virtual portgroup
    :type name: String

    :param vlan_id: The vLAN tag id of the new dv portgroup
    :type vlan_id: Integer

    :param switch_name: The name of the switch to add the new vLAN network to
    :type switch_name: String
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER,\
                password=const.INF_VCENTER_PASSWORD) as vcenter:
        try:
            switch = vcenter.dv_switches[switch_name]
        except KeyError:
            available = list(vcenter.dv_switches.keys())
            msg = 'No such switch: {}, Available: {}'.format(
                switch_name, available)
            raise ValueError(msg)
        spec = get_dv_portgroup_spec(name, vlan_id)
        task = switch.AddDVPortgroup_Task([spec])
        try:
            consume_task(task, timeout=300)
            error = ''
        except RuntimeError as doh:
            error = '{}'.format(doh)
        return error
Beispiel #6
0
def delete_windows(username, machine_name, logger):
    """Unregister and destroy a user's Windows

    :Returns: None

    :param username: The user who wants to delete their jumpbox
    :type username: String

    :param machine_name: The name of the VM to delete
    :type machine_name: String

    :param logger: An object for logging messages
    :type logger: logging.LoggerAdapter
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER, \
                 password=const.INF_VCENTER_PASSWORD) as vcenter:
        folder = vcenter.get_by_name(name=username, vimtype=vim.Folder)
        for entity in folder.childEntity:
            if entity.name == machine_name:
                info = virtual_machine.get_info(vcenter, entity, username)
                if info['meta']['component'] == 'Windows':
                    logger.debug('powering off VM')
                    virtual_machine.power(entity, state='off')
                    delete_task = entity.Destroy_Task()
                    logger.debug('blocking while VM is being destroyed')
                    consume_task(delete_task)
                    break
        else:
            raise ValueError('No {} named {} found'.format(
                'windows', machine_name))
Beispiel #7
0
def nuke_folder(folder, timeout=300):
    """Delete a user's folder

    :Returns: None

    :param folder: **Required** The user's folder to delete
    :type folder: vim.Folder

    :param timeout: How long to wait for the operation to complete
    :type timeout: Integer
    """
    task = folder.Destroy()
    consume_task(task, timeout=timeout)
Beispiel #8
0
def config_vm(the_vm):
    """Enable hardware-assisted virtualization so 64-bit OSes can run on the
    virtual ESXi host.

    :Returns: None

    :param the_vm: The new ESXi virtual machine object
    :type the_vm: vim.VirtualMachine
    """
    spec = vim.vm.ConfigSpec()
    spec.nestedHVEnabled = True
    task = the_vm.ReconfigVM_Task(spec)
    consume_task(task)
Beispiel #9
0
def _setup_jumpbox(vcenter, the_vm, username):
    """Configure the Jumpbox for the end user

    :Returns: None

    :param vcenter: The instantiated connection to vCenter
    :type vcenter: vlab_inf_common.vmware.vCenter

    :param the_vm: The new gateway
    :type the_vm: vim.VirtualMachine
    """
    # Add the note about the type & version of Jumpbox being used
    spec = vim.vm.ConfigSpec()
    spec.annotation = 'ubuntu=18.04'
    task = the_vm.ReconfigVM_Task(spec)
    consume_task(task)
    # Create an admin user with the same username as the end-user
    cmd = '/usr/bin/sudo'
    # SHA 512 version of the letter 'a' (plus the $6$ to denote SHA 512)
    pw = '$6$qM5mj4O0$x8l6R4T4sH1HJgYt9dw3n2pYO8E0Rs/sqlCfts5/p8o8ZK8aBjfHRlh37xnxIfPZBp.ErfBgnSJcauzP2mxBx.'
    args = "/usr/sbin/useradd --shell /bin/bash --password '{1}' --create-home --groups sudo --home-dir /home/{0} {0} ".format(
        username, pw)
    result = virtual_machine.run_command(vcenter,
                                         the_vm,
                                         cmd,
                                         user='******',
                                         password='******',
                                         arguments=args)
    if result.exitCode:
        error = 'Failed to create user {} in newly deployed jumpbox'.format(
            username)
        raise RuntimeError(error)

    # Make the Ubuntu GNOME desktop the default used by xRDP
    # https://www.hiroom2.com/2018/04/29/ubuntu-1804-xrdp-gnome-en/
    cmd2 = '/usr/bin/sudo'
    args2 = '/bin/cp /etc/xrdp/xsessionrc /home/{}/.xsessionrc'.format(
        username)
    result2 = virtual_machine.run_command(vcenter,
                                          the_vm,
                                          cmd2,
                                          user='******',
                                          password='******',
                                          arguments=args2)
    if result2.exitCode:
        error = 'Failed to create .xsessionrc file in {} homedir'.format(
            username)
        raise RuntimeError(error)
def adjust_cpu(the_vm, cpu_count):
    """Set the number of CPUs for a VM

    **IMPORTANT**
    Make sure your VM is powered off before calling this function, otherwise
    it'll fail.

    :param the_vm: The virtual machine to adjust CPU count on
    :type the_vm: vim.VirtualMachine

    :param cpu_count: The number of CPU cores to allocate to the VM
    :type cpu_count: Integer
    """
    config_spec = vim.vm.ConfigSpec()
    config_spec.numCPUs = cpu_count
    consume_task(the_vm.Reconfigure(config_spec))
Beispiel #11
0
def delete_jumpbox(username):
    """Unregister and destroy the user's jumpbox

    :Returns: None

    :param username: The user who wants to delete their jumpbox
    :type username: String
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER, \
                 password=const.INF_VCENTER_PASSWORD) as vcenter:
        folder = vcenter.get_by_name(name=username, vimtype=vim.Folder)
        for entity in folder.childEntity:
            if entity.name == COMPONENT_NAME:
                logger.debug('powering off VM')
                virtual_machine.power(entity, state='off')
                delete_task = entity.Destroy_Task()
                logger.debug('blocking while VM is being destroyed')
                consume_task(delete_task)
Beispiel #12
0
def _deleted_old_snaps(the_vm, logger):
    """Delete all snapshots such that const.VLAB_SNAP_CREATED is not exceeded.
    Returns the number of snapshots deleted.

    :Returns: Integer

    :param the_vm: The virtual machine with snapshots to delete
    :type the_vm: vim.VirtualMachine
    """
    all_snaps = _get_snapshots(the_vm.snapshot.rootSnapshotList)
    all_snaps = sorted(
        all_snaps,
        key=lambda x: int(x.name.split('_')[const.VLAB_SNAP_CREATED]))
    delete_count = len(all_snaps) - const.VLAB_MAX_SNAPSHOTS
    to_delete = []
    for _ in range(delete_count):
        to_delete.append(all_snaps.pop(0))
    for snap in to_delete:
        consume_task(snap.snapshot.RemoveSnapshot_Task(removeChildren=False))
    return delete_count
def adjust_ram(the_vm, mb_of_ram):
    """Set the amount of RAM for a VM

    **IMPORTANT**
    Most VMs are required to be powered off in order to adjust RAM.
    Unless you know that your guest OS supports hot-swap RAM, power your VM off
    before changing how much RAM it has.

    :Returns: None

    :param the_vm: The virtual machine to adjust RAM on
    :type the_vm: vim.VirtualMachine

    :param mb_of_ram: The number of MB of RAM/memory to give the virtual machine
    :type mb_of_ram: Integer
    """
    config_spec = vim.vm.ConfigSpec()
    config_spec.memoryMB = mb_of_ram

    consume_task(the_vm.Reconfigure(config_spec))
Beispiel #14
0
def delete_snapshot(username, snap_id, machine_name, logger):
    """Destroy a snapshot

    :Returns: None

    :param username: The user who wants to delete their jumpbox
    :type username: String

    :param snap_id: The snapshot to destroy
    :type snap_id: Integer

    :param machine_name: The name of the virtual machine which owns the snapshot
    :type machine_name: String

    :param logger: An object for logging messages
    :type logger: logging.LoggerAdapter
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER, \
                 password=const.INF_VCENTER_PASSWORD) as vcenter:
        folder = vcenter.get_by_name(name=username, vimtype=vim.Folder)
        for entity in folder.childEntity:
            if entity.name == machine_name:
                if entity.snapshot:
                    for snap in _get_snapshots(
                            entity.snapshot.rootSnapshotList):
                        snap_data = snap.name.split('_')
                        if snap_data[const.VLAB_SNAP_ID] == snap_id:
                            logger.info('Deleting snapshot {} from {}'.format(
                                snap.name, machine_name))
                            consume_task(
                                snap.snapshot.RemoveSnapshot_Task(
                                    removeChildren=False))
                            # return exits nested for-loop; break just stop immediate parent loop
                            return None
                    else:
                        error = 'VM has no snapshot by ID {}'.format(snap_id)
                        raise ValueError(error)
        else:
            error = 'No VM named {} found in inventory'.format(machine_name)
            logger.info(error)
            raise ValueError(error)
Beispiel #15
0
def delete_gateway(username, logger):
    """Unregister and destroy the defaultGateway virtual machine

    :Returns: None

    :param username: The user who wants to delete their defaultGateway
    :type username: String

    :param logger: An object for logging messages
    :type logger: logging.LoggerAdapter
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER, \
                 password=const.INF_VCENTER_PASSWORD) as vcenter:
        folder = vcenter.get_by_name(name=username, vimtype=vim.Folder)
        for entity in folder.childEntity:
            if entity.name == COMPONENT_NAME:
                logger.debug('powering off VM')
                virtual_machine.power(entity, state='off')
                delete_task = entity.Destroy_Task()
                logger.debug('blocking while VM is being destroyed')
                consume_task(delete_task)
def power(the_vm, state, timeout=600):
    """Turn on/off/restart a given virtual machine.

    Turning off and restarting do **not** perform graceful shutdowns; it's like
    pulling the power cable. This method blocks until the VM is in the requested
    power state.

    :Returns: Boolean

    :param the_vm: The pyVmomi Virtual machine object
    :type the_vm: vim.VirtualMachine

    :param state: The power state to put the VM into. Valid values are "on" "off" and "restart"
    :type state: Enum/String

    :param timeout: Optional - How long (in milliseconds) to block waiting on a given power state
    :type timeout: Integer
    """
    valid_states = {'on', 'off', 'restart'}
    if state not in valid_states:
        error = 'state must be one of {}, supplied {}'.format(
            valid_states, state)
        raise ValueError(error)

    vm_power_state = the_vm.runtime.powerState.lower().replace('powered', '')
    if vm_power_state == state:
        return True
    elif (state == 'on') or (vm_power_state == 'off' and state == 'restart'):
        task = the_vm.PowerOn()
    elif state == 'off':
        task = the_vm.PowerOff()
    elif state == 'restart':
        task = the_vm.ResetVM_Task()

    try:
        consume_task(task, timeout=timeout)
    except RuntimeError:
        # task failed or timed out
        return False
    return True
def change_network(the_vm, network, adapter_label='Network adapter 1'):
    """Update the VM; replace existing network with the supplied network.

    :Returns: None

    :param the_vm: The virtual machine to update
    :type the_vm: vim.VirtualMachine

    :param network: The new network the VM should be connected to
    :type network: vim.Network

    :param adapter_label: The name of the virtual NIC to connect to a new device
    :type adapter_label: String
    """
    devices = [
        x for x in the_vm.config.hardware.device
        if x.deviceInfo.label == adapter_label
    ]
    if not devices:
        error = "VM has no network adapter named {}".format(adapter_label)
        raise RuntimeError(error)
    else:
        device = devices[0]

    nicspec = vim.vm.device.VirtualDeviceSpec()
    nicspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit
    nicspec.device = device
    nicspec.device.wakeOnLanEnabled = True
    dvs_port_connection = vim.dvs.PortConnection()
    dvs_port_connection.portgroupKey = network.key
    dvs_port_connection.switchUuid = network.config.distributedVirtualSwitch.uuid
    nicspec.device.backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo(
    )
    nicspec.device.backing.port = dvs_port_connection
    nicspec.device.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
    nicspec.device.connectable.startConnected = True
    nicspec.device.connectable.allowGuestControl = True
    nicspec.device.connectable.connected = True
    config_spec = vim.vm.ConfigSpec(deviceChange=[nicspec])
    consume_task(the_vm.ReconfigVM_Task(config_spec))
Beispiel #18
0
def _add_database_disk(the_vm, disk_size):
    """Add a VMDK to the new DataIQ instance to store it's database.

    :Returns: None

    :Rasies: RuntimeError

    :param the_vm: The new DataIQ machine
    :type the_vm: vim.VirtualMachine

    :param disk_size: The number of GB to make the disk
    :type disk_size: Integer
    """
    spec = vim.vm.ConfigSpec()
    unit_number = 0
    for dev in the_vm.config.hardware.device:
        if hasattr(dev.backing, 'fileName'):
            unit_number = int(dev.unitNumber) + 1
            # unitNumber 7 is reserved for the SCSI controller
            if unit_number == 7:
                unit_number += 1
            if unit_number >= 16:
                raise RuntimeError('VM cannot have 16 VMDKs')
    if unit_number == 0:
        raise RuntimeError('Unable to find any VMDKs for VM')

    dev_changes = []
    disk_spec = vim.vm.device.VirtualDeviceSpec()
    disk_spec.fileOperation = "create"
    disk_spec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
    disk_spec.device = vim.vm.device.VirtualDisk()
    disk_spec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo()
    disk_spec.device.backing.thinProvisioned = True
    disk_spec.device.backing.diskMode = 'persistent'
    disk_spec.device.unitNumber = unit_number
    disk_spec.device.capacityInKB = int(disk_size) * 1024 * 1024
    disk_spec.device.controllerKey = 1000
    dev_changes.append(disk_spec)
    spec.deviceChange = dev_changes
    consume_task(the_vm.ReconfigVM_Task(spec=spec))
def set_meta(the_vm, meta_data):
    """Truncate and replace the meta data associated with a given virtual machine.

    :Returns: None

    :Raises: ValueError - when invalid meta data supplied

    :param the_vm: The virtual machine to assign the meta data to
    :type the_vm: vim.VirtualMachine

    :param meta_data: The extra information to associate to the virtual machine
    :type meta_data: Dictionary
    """
    expected = {'component', 'created', 'version', 'generation', 'configured'}
    provided = set(meta_data.keys())
    if not expected == provided:
        error = "Invalid meta data schema. Supplied: {}, Required: {}".format(
            provided, expected)
        raise ValueError(error)
    spec = vim.vm.ConfigSpec()
    spec_info = ujson.dumps(meta_data)
    spec.annotation = spec_info
    task = the_vm.ReconfigVM_Task(spec)
    consume_task(task)
Beispiel #20
0
def delete_network(name):
    """Destroy a vLAN network

    :Returns: None

    :Raises: ValueError

    :param name: The name of the network to destroy
    :type name: String
    """
    with vCenter(host=const.INF_VCENTER_SERVER, user=const.INF_VCENTER_USER, \
                password=const.INF_VCENTER_PASSWORD) as vcenter:
        try:
            network = vcenter.networks[name]
        except KeyError:
            msg = 'No such vLAN exists: {}'.format(name)
            raise ValueError(msg)
        try:
            task = network.Destroy_Task()
            consume_task(task, timeout=300)
        except RuntimeError:
            msg = "Network {} in use. Must delete VMs using network before deleting network.".format(
                name)
            raise ValueError(msg)