def test_error(self, fake_time):
        """``consume_task`` - raises RuntimeError if the task complete with an error"""
        fake_task = MagicMock()
        fake_task.info.error.msg = 'someError'
        fake_task.info.result = 'woot'

        with self.assertRaises(RuntimeError):
            task_lib.consume_task(fake_task, timeout=2)
    def test_timeout(self, fake_time):
        """``consume_task`` raises RuntimeError if the task does not complete within the timeout"""
        fake_task = MagicMock()
        fake_task.info.completeTime = None
        fake_task.info.error = None
        fake_task.info.result = 'woot'

        with self.assertRaises(RuntimeError):
            task_lib.consume_task(fake_task, timeout=2)
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))
    def test_happy_path(self, fake_time):
        """``consume_task`` - Works as expected when there are no issues"""
        fake_task = MagicMock()
        fake_task.info.error = None
        fake_task.info.result = 'woot'

        result = task_lib.consume_task(fake_task, timeout=2)
        expected = 'woot'

        self.assertEqual(result, expected)
    def test_blocks(self, fake_time):
        """``consume_task`` - Blocks until the task is complete"""
        fake_task = MagicMock()
        fake_task.info.error = None
        fake_task.info.result = 'woot'
        fake_task.info.completeTime.side_effect = [None, 'someTimestamp']

        result = task_lib.consume_task(fake_task, timeout=5)
        expected = 'woot'

        self.assertEqual(result, expected)
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))
def configure_network(the_vm, ip_config):
    """Set a static IP and related network settings for a Linux VM with VMware Tools installed.

    The ``ip_config`` dictionary MUST have the following keys:
        - static-ip
        - netmask
        - default-gateway
        - dns
        - domain

    :Returns: None

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

    :param ip_config: The dictionary containing the static network information.
    :type ip_config: Dictionary

    """
    # Boilerplate...
    adaptermap = vim.vm.customization.AdapterMapping()
    globalip = vim.vm.customization.GlobalIPSettings()
    adaptermap.adapter = vim.vm.customization.IPSettings()
    # Configure IP & DNS
    adaptermap.adapter.ip = vim.vm.customization.FixedIp()
    adaptermap.adapter.ip.ipAddress = ip_config['static-ip']
    adaptermap.adapter.subnetMask = ip_config['netmask']
    adaptermap.adapter.gateway = ip_config['default-gateway']
    globalip.dnsServerList = ip_config['dns']
    adaptermap.adapter.dnsDomain = ip_config['domain']
    ident = vim.vm.customization.LinuxPrep()
    ident.domain = ip_config['domain']
    ident.hostName = vim.vm.customization.FixedName()
    ident.hostName.name = the_vm.name
    # Create the configuration spec
    spec = vim.vm.customization.Specification()
    spec.nicSettingMap = [adaptermap]
    spec.globalIPSettings = globalip
    spec.identity = ident
    task = the_vm.Customize(spec=spec)
    consume_task(task)
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))
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 add_vmdk(the_vm, disk_size):
    """Add a new VMDK to an existing Virtual Machine.

    :Returns: None

    :Rasies: RuntimeError

    :param the_vm: The pyVmomi Virtual machine object
    :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('VMs cannot have more than 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.issubset(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)