Esempio n. 1
0
File: tag.py Progetto: D3DeFi/vmcli
    def associate_tag(self, stub_config, name, tags):
        """Associates tags with specific VM."""
        if not name or not tags:
            raise VmCLIException(
                'Arguments name or tags are missing, cannot continue!')

        vm = self.get_vm_obj(name, fail_missing=True)
        # Get vmware ID representation in form 'vm-XXX' for later association
        vm_id = vm._GetMoId()
        vm_dynid = DynamicID(type='VirtualMachine', id=vm_id)
        # Create API services for Tag and TagAssociation backends
        tag_svc = Tag(stub_config)
        tag_asoc = TagAssociation(stub_config)
        # Search for tag object(s)
        tags_found = []
        if ',' in tags:
            tags = tags.split(',')
        else:
            tags = [tags]

        for t in tag_svc.list():
            tag = tag_svc.get(t)
            if tag.name in tags:
                tags_found.append(tag)

        if len(tags_found) != len(tags):
            raise VmCLIException('One or more tags were not found')

        # Asosociate tags with VM
        for tag in tags_found:
            tag_asoc.attach(tag_id=tag.id, object_id=vm_dynid)
        self.logger.info('All tags have been attached to the VM')
Esempio n. 2
0
    def get_obj(self, vimtype, name, default=False):
        """Gets the vsphere object associated with a given text name.
        If default is set to True and name does not match, return first object found."""
        vimtype = VMWARE_TYPES.get(vimtype, None)
        if not vimtype:
            raise VmCLIException(
                'Provided type does not match any existing VMware object types!'
            )

        container = self.content.viewManager.CreateContainerView(
            self.content.rootFolder, [vimtype], True)
        if name is not None:
            for item in container.view:
                if item.name == name:
                    return item

        # If searched object is not found and default is True, provide first instance found
        if default:
            # Order result list before selecting default
            list = builtins.list
            view_items = list({x.name: x for x in container.view}.items())
            sorted_view = OrderedDict(sorted(view_items, key=lambda t: t[0]))
            sorted_view = list(sorted_view.values())
            try:
                return sorted_view[0]
            except IndexError:
                return None
        return None
Esempio n. 3
0
 def register(name, class_name):
     """Registers class itself as a subcommand with a provided name."""
     global COMMANDS
     if COMMANDS.get(name, None):
         raise VmCLIException(
             'Subcommand with the name {} already registered!'.format(name))
     else:
         COMMANDS[name] = class_name
Esempio n. 4
0
    def create_snapshot(self, vm, snapshot, desc, memory, quiesce):
        """Creates new snapshot on the VM."""
        if desc is None:
            raise VmCLIException('Argument --desc is required with "create" operation!')

        self.logger.info('Creating snapshot of the virtual machine...')
        task = vm.CreateSnapshot_Task(name=snapshot, description=desc, memory=memory, quiesce=quiesce)
        self.wait_for_tasks([task])
Esempio n. 5
0
 def execute(self, args):
     if args.mem or args.cpu:
         self.change_hw_resource(args.name, args.mem, args.cpu)
     elif args.net:
         self.change_network(args.name, args.net, args.dev)
     elif args.vHWversion:
         self.change_vHWversion(args.name, args.vHWversion)
     else:
         raise VmCLIException('Too few arguments. Aborting...')
Esempio n. 6
0
def normalize_memory(value):
    """Function converts passed value to integer, which will represent size in megabytes
    as well as performs control whether the value sits between global limits."""

    value = convert_to_mb(value)
    if value < VM_MIN_MEM or value > VM_MAX_MEM:
        raise VmCLIException('Memory must be between {}-{} megabytes'.format(VM_MIN_MEM, VM_MAX_MEM))
    else:
        return value
Esempio n. 7
0
    def change_network(self, name, net, dev):
        """Changes network associated with a specifc VM's network interface."""
        vm = self.get_vm_obj(name, fail_missing=True)
        # locate network, which should be assigned to device
        network = self.get_obj('network', net)
        if not network:
            raise VmCLIException('Unable to find provided network {}! Aborting...'.format(net))

        # search for Ethernet devices
        self.logger.info('Searching for ethernet devices attached to vm...')
        nic_counter = 1
        for device in vm.config.hardware.device:
            # Search for a specific network interfaces
            if isinstance(device, vim.vm.device.VirtualEthernetCard):
                if nic_counter != dev:
                    nic_counter += 1
                    continue

                if isinstance(network, vim.dvs.DistributedVirtualPortgroup):
                    # specify backing that connects device to a DVS switch portgroup
                    dvs_port_conn = vim.dvs.PortConnection(
                            portgroupKey=network.key, switchUuid=network.config.distributedVirtualSwitch.uuid)
                    backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo(port=dvs_port_conn)
                else:
                    # expect simple vim.Network if DistributedVirtualPortgroup was not used
                    backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo(
                        useAutoDetect=False, network=network, deviceName=net)

                device.backing = backing
                # specify power status for nic
                device.connectable = vim.vm.device.VirtualDevice.ConnectInfo(
                        connected=True, startConnected=True, allowGuestControl=True)

                # build object with change specifications
                nicspec = vim.vm.device.VirtualDeviceSpec(device=device)
                nicspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.edit

                config_spec = vim.vm.ConfigSpec(deviceChange=[nicspec])
                self.logger.info("Attaching network {} to {}. network device on VM...".format(net, dev))
                task = vm.ReconfigVM_Task(config_spec)
                self.wait_for_tasks([task])
                return

        raise VmCLIException('Unable to find ethernet device on a specified target!')
Esempio n. 8
0
    def execute(self, args):
        """Creates empty vm without disk."""
        if not (args.name or args.folder or args.resource_pool
                or args.datastore):
            raise VmCLIException(
                'Missing arguments! Make sure name, folder, resource_pool and datastore are present.'
            )

        datastore = self.get_obj('datastore', args.datastore)
        if not datastore:
            raise VmCLIException(
                'Specified datastore not found! Datastore clusters are not supported with this operation. Aborting...'
            )

        # Store logs and snapshots withing same directory as ds_path, which is [datastore]vm_name
        ds_path = '[{}]{}'.format(args.datastore, args.name)
        vm_files = vim.vm.FileInfo(logDirectory=None,
                                   snapshotDirectory=None,
                                   suspendDirectory=None,
                                   vmPathName=ds_path)
        # Load cpu and memory configuration
        if not args.mem:
            args.mem = c.VM_MIN_MEM
        args.mem = normalize_memory(args.mem)

        if not args.cpu:
            args.cpu = c.VM_MIN_CPU
        elif args.cpu < c.VM_MIN_CPU or args.cpu > c.VM_MAX_CPU:
            raise VmCLIException('CPU count must be between {}-{}'.format(
                c.VM_MIN_CPU, c.VM_MAX_CPU))

        # configuration specification for the new vm, if no mem and cpu is provided, minimal values will be used
        config_spec = vim.vm.ConfigSpec()
        config_spec.name = args.name
        config_spec.memoryMB = args.mem
        config_spec.numCPUs = args.cpu
        config_spec.files = vm_files
        config_spec.guestId = 'otherLinux64Guest'

        folder = self.get_obj('folder', args.folder)
        resource_pool = self.get_obj('resource_pool', args.resource_pool)
        task = folder.CreateVM_Task(config=config_spec, pool=resource_pool)
        self.wait_for_tasks([task])
Esempio n. 9
0
def automationSDKConnect(vcenter=None,
                         username=None,
                         password=None,
                         insecure=None):
    """Creates stub_config with connection object for advanced features like VM Tagging present
    in vsphere-automation-sdk-python library, which is required to be installed:
    https://github.com/vmware/vsphere-automation-sdk-python"""
    vcenter = vcenter or conf.VCENTER
    username = username or conf.USERNAME
    password = password or conf.PASSWORD
    insecure = insecure or conf.INSECURE_CONNECTION

    if not HAS_AUTOMAT_SDK_INSTALLED:
        raise VmCLIException(
            'Required vsphere-automation-sdk-python not installed. Exiting...')
        sys.exit(1)

    if (vcenter and username) and not password:
        password = getpass.getpass()
    elif not (vcenter and username and password):
        logger.error('No authentication credentials provided!')
        sys.exit(1)

    if not vcenter.startswith('http'):
        vcenter = 'https://{}/api'.format(vcenter)

    session = requests.Session()
    if insecure:
        requests.packages.urllib3.disable_warnings(
            requests.packages.urllib3.exceptions.InsecureRequestWarning)
        session.verify = False

    connector = get_requests_connector(session=session, url=vcenter)
    stub_config = StubConfigurationFactory.new_std_configuration(connector)

    # Pass user credentials (user/password) in the security context to authenticate.
    # login to vAPI endpoint
    user_password_security_context = create_user_password_security_context(
        username, password)
    stub_config.connector.set_security_context(user_password_security_context)

    # Create the stub for the session service and login by creating a session.
    session_svc = Session(stub_config)
    try:
        session_id = session_svc.create()
    except Unauthenticated:
        logger.error('Unable to connect. Check your credentials!')
        sys.exit(1)

    # Successful authentication.  Store the session identifier in the security
    # context of the stub and use that for all subsequent remote requests
    session_security_context = create_session_security_context(session_id)
    stub_config.connector.set_security_context(session_security_context)

    return stub_config
Esempio n. 10
0
    def change_vHWversion(self, name, vHWversion=None):
        """Changes VM HW version. If version is None, then VM is set to the latest version."""
        vm = self.get_vm_obj(name, fail_missing=True)
        if vHWversion == 'latest':
            version = None      # None will default to latest so we don't need to search for it
        else:
            try:
                version = 'vmx-{:02d}'.format(vHWversion)
            except ValueError:
                raise VmCLIException('VM version must be integer or \'latest\'! Aborting...')

        if vm.runtime.powerState != 'poweredOff':
            raise VmCLIException('VM hardware version change cannot be performed on running VM! Aborting...')

        self.logger.info('Updating VM hardware version...')
        try:
            task = vm.UpgradeVM_Task(version=version)
            self.wait_for_tasks([task])
        except vim.fault.AlreadyUpgraded:
            pass
Esempio n. 11
0
    def get_vm_obj(self, name, fail_missing=False):
        """Checks if passed object is of vim.VirtualMachine type, if not retrieves it from container view"""
        if isinstance(name, vim.VirtualMachine):
            return name
        else:
            self.logger.info('Loading required VMware resources...')
            vm = self.get_obj('vm', name)

            if not vm and fail_missing:
                raise VmCLIException(
                    'Unable to find specified VM {}! Aborting...'.format(name))
            return vm
Esempio n. 12
0
    def change_hw_resource(self, name, mem=None, cpu=None):
        """Changes hardware resource of a specific VM."""
        if not mem and not cpu:
            raise VmCLIException('Neither memory or cpu specified! Cannot run hardware reconfiguration.')

        vm = self.get_vm_obj(name, fail_missing=True)
        config_spec = vim.vm.ConfigSpec()
        if mem:
            mem = normalize_memory(mem)
            self.logger.info("Increasing memory to {} megabytes...".format(mem))
            config_spec.memoryMB = mem

        if cpu:
            if cpu < c.VM_MIN_CPU or cpu > c.VM_MAX_CPU:
                raise VmCLIException('CPU count must be between {}-{}'.format(c.VM_MIN_CPU, c.VM_MAX_CPU))
            else:
                self.logger.info("Increasing cpu count to {} cores...".format(cpu))
                config_spec.numCPUs = cpu

        task = vm.ReconfigVM_Task(config_spec)
        self.wait_for_tasks([task])
Esempio n. 13
0
    def exec_inside_vm(self,
                       name,
                       commands,
                       guest_user=None,
                       guest_pass=None,
                       wait_for_tools=False):
        """Runs provided command inside guest's operating system."""
        if not commands:
            raise VmCLIException('No command provided for execution!')

        vm = self.get_vm_obj(name, fail_missing=True)
        self.logger.info("Checking if guest's OS has vmtools installed ...")
        if wait_for_tools:
            self.wait_for_guest_vmtools(vm)

        if vm.guest.toolsStatus in ['toolsNotInstalled', 'toolsNotRunning']:
            raise VmCLIException(
                "Guest's VMware tools are not installed or not running. Aborting..."
            )

        try:
            credentials = vim.vm.guest.NamePasswordAuthentication(
                username=guest_user, password=guest_pass)
            for cmd in commands:
                executable = cmd.split()[0].lstrip()
                arguments = ' '.join(cmd.split()[1:])
                try:
                    self.logger.info(
                        'Running command "{} {}" inside guest'.format(
                            executable, arguments))
                    progspec = vim.vm.guest.ProcessManager.ProgramSpec(
                        programPath=executable, arguments=arguments)
                    self.content.guestOperationsManager.processManager.StartProgramInGuest(
                        vm, credentials, progspec)
                except vim.fault.FileNotFound as e:
                    raise VmCLIException(
                        e.msg + '. Try providing absolute path to the binary.')
        except vim.fault.InvalidGuestLogin as e:
            raise VmCLIException(e.msg)
Esempio n. 14
0
File: tag.py Progetto: D3DeFi/vmcli
    def execute(self, args):
        if not HAS_AUTOMAT_SDK_INSTALLED:
            raise VmCLIException(
                'Required vsphere-automation-sdk-python not installed. Exiting...'
            )
            sys.exit(1)

        stub_config = automationSDKConnect(args.vcenter, args.username,
                                           args.password, args.insecure)

        if args.name:
            self.associate_tag(stub_config, args.name, args.tags)
        else:
            self.print_tags(stub_config)
Esempio n. 15
0
    def execute(self, args):
        if args.operation != 'list' and not args.snapshot:
            raise VmCLIException('Argument --snapshot is required with "{}" operation!'.format(args.operation))

        vm = self.get_vm_obj(args.name, fail_missing=True)

        if args.operation == 'list':
            self.list_snapshots(vm)
        elif args.operation == 'create':
            self.create_snapshot(vm, args.snapshot, args.desc, args.memory, args.quiesce)
        elif args.operation == 'delete':
            self.delete_snapshot(vm, args.snapshot)
        elif args.operation == 'revert':
            self.revert_snapshot(vm, args.snapshot)
Esempio n. 16
0
def convert_to_mb(value):
    """Converts size to megabytes."""

    if isinstance(value, str):
        if value.endswith('T'):
            value = int(value.strip('T')) * 1024 * 1024
        elif value.endswith('G'):
            value = int(value.strip('G')) * 1024
        elif value.endswith('M'):
            value = int(value.strip('M'))
        elif value.endswith('K'):
            value = int(value.strip('K')) / 1024
        # Assume bytes otherwise
        else:
            value = int(value) / 1024 / 1024

    try:
        value = int(value)
    except ValueError:
        raise VmCLIException('Unable to convert memory size to gigabytes. Aborting...')

    return value
Esempio n. 17
0
    def attach_net_adapter(self, name, net):
        """Attaches virtual network adapter to the vm associated with a VLAN passed via argument."""
        vm = self.get_vm_obj(name, fail_missing=True)
        # locate network, which should be assigned to device
        network = self.get_obj('network', net)
        if not network:
            raise VmCLIException(
                'Unable to find provided network {}! Aborting...'.format(net))

        # build virtual device
        device = vim.vm.device.VirtualVmxnet3(deviceInfo=vim.Description())

        if isinstance(network, vim.dvs.DistributedVirtualPortgroup):
            # specify backing that connects device to a DVS switch portgroup
            dvs_port_conn = vim.dvs.PortConnection(
                portgroupKey=network.key,
                switchUuid=network.config.distributedVirtualSwitch.uuid)
            backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo(
                port=dvs_port_conn)
        else:
            # expect simple vim.Network if DistributedVirtualPortgroup was not used
            backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo(
                useAutoDetect=False, network=network, deviceName=net)

        device.backing = backing
        # specify power status for nic
        device.connectable = vim.vm.device.VirtualDevice.ConnectInfo(
            connected=False, startConnected=True, allowGuestControl=True)

        # build object with change specifications
        nicspec = vim.vm.device.VirtualDeviceSpec(device=device)
        nicspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add

        config_spec = vim.vm.ConfigSpec(deviceChange=[nicspec])
        self.logger.info(
            'Attaching network device to the virtual machine {}...'.format(
                name))
        task = vm.ReconfigVM_Task(config_spec)
        self.wait_for_tasks([task])
Esempio n. 18
0
    def exec_callbacks(self, args, callback_args):
        """Runs any executable present inside project/callbacks/ directory on host with provided arguments.
        First argument to executable is always JSON object containing all arguments passed to vmcli and its
        subcommands via cli. Following are arguments passed as a value via command line argument callback.
        For example, this --callback 'var1; var2; multi word var' will be passed as:
        ./callbacks/script.sh '{"name": "..", "template": ...}' 'var1' 'var2' 'multi word var'
        """
        # Parse additional callback arguments passed from command line
        if callback_args:
            callback_args = [
                x.lstrip() for x in callback_args.rstrip(';').split(';')
            ]
        else:
            callback_args = []
        # Get all callback scripts
        callbacks_dir = sorted(os.listdir('callbacks/'))
        callbacks = [
            os.path.realpath('callbacks/' + x) for x in callbacks_dir
            if not x.startswith('.')
        ]
        # Prepare JSON serializable object from args namespace
        arguments = {}
        for argument in [x for x in dir(args) if not x.startswith('_')]:
            arguments[argument] = getattr(args, argument, None)
        arguments = json.dumps(arguments)

        for executable in callbacks:
            self.logger.info('Running callback "{}" ...'.format(executable))
            command = [executable, arguments]
            command.extend(callback_args)
            try:
                subprocess.Popen(command).communicate()
            except OSError:
                raise VmCLIException(
                    'Unable to execute callback {}! Check it for errors'.
                    format(executable))
Esempio n. 19
0
    def attach_hdd(self, name, size):
        """Attaches disk to a virtual machine. If no SCSI controller is present, then it is attached as well."""
        if not size or size < VM_MIN_HDD or size > VM_MAX_HDD:
            raise VmCLIException('Hdd size must be between {}-{}'.format(
                VM_MIN_HDD, VM_MAX_HDD))

        vm = self.get_vm_obj(name, fail_missing=True)

        disks = []
        controller = None
        # iterate over existing devices and try to find disks and controllerKey
        self.logger.info(
            'Searching for already existing disks and SCSI controllers...')
        for device in vm.config.hardware.device:
            # search for existing SCSI controller or create one if none found
            # TODO: provide flag when to create new controller
            if isinstance(
                    device,
                    vim.vm.device.VirtualSCSIController) and not controller:
                controller = device
            elif isinstance(device, vim.vm.device.VirtualDisk):
                disks.append(device)

        disk_unit_number = 0
        controller_unit_number = 7
        scsispec = None
        # if controller exists, calculate next unit number for disks otherwise create new controller and use defaults
        if controller:
            self.logger.info(
                'Using existing SCSI controller(id:{}) to attach disk'.format(
                    controller.key))
            controller_unit_number = int(controller.key)
            for disk in disks:
                if disk.controllerKey == controller.key and disk_unit_number <= int(
                        device.unitNumber):
                    disk_unit_number = int(device.unitNumber) + 1
        else:
            self.logger.info(
                'No existing SCSI controller found. Creating new one...')
            scsispec = vim.vm.device.VirtualDeviceSpec()
            scsispec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
            scsispec.device = vim.vm.device.ParaVirtualSCSIController(
                deviceInfo=vim.Description())
            scsispec.device.slotInfo = vim.vm.device.VirtualDevice.PciBusSlotInfo(
            )
            # if there is no controller on the device present, assign it default values
            scsispec.device.controllerKey = 100
            scsispec.device.unitNumber = 3
            scsispec.device.busNumber = 0
            scsispec.device.hotAddRemove = True
            scsispec.device.sharedBus = 'noSharing'
            scsispec.device.scsiCtlrUnitNumber = controller_unit_number
            controller = scsispec.device
            controller.key = 100

        if disk_unit_number >= 16:
            raise VmCLIException(
                'The SCSI controller does not support any more disks!')
        elif disk_unit_number == 7:
            disk_unit_number = +1  # 7 is reserved for SCSI controller itself

        self.logger.info('Creating new empty disk with size {}G'.format(size))
        diskspec = vim.vm.device.VirtualDeviceSpec()
        diskspec.fileOperation = "create"
        diskspec.operation = vim.vm.device.VirtualDeviceSpec.Operation.add
        diskspec.device = vim.vm.device.VirtualDisk()
        diskspec.device.backing = vim.vm.device.VirtualDisk.FlatVer2BackingInfo(
        )
        diskspec.device.backing.diskMode = 'persistent'
        diskspec.device.backing.thinProvisioned = True
        diskspec.device.unitNumber = disk_unit_number
        diskspec.device.capacityInBytes = size * 1024 * 1024 * 1024
        diskspec.device.capacityInKB = size * 1024 * 1024
        diskspec.device.controllerKey = controller.key

        if scsispec:
            dev_change = [scsispec, diskspec]
        else:
            dev_change = [diskspec]

        config_spec = vim.vm.ConfigSpec(deviceChange=dev_change)
        self.logger.info('Attaching device to the virtual machine...')
        task = vm.ReconfigVM_Task(config_spec)
        self.wait_for_tasks([task])
Esempio n. 20
0
    def execute(self, args):
        """Clones VM, assigns it proper hardware devices, powers it on ad prepares it for further configuration."""
        if not args.name or not args.template:
            raise VmCLIException(
                'Arguments name or template are missing, cannot continue!')

        clone = CloneCommands(self.connection)
        clone.clone_vm(args.name,
                       args.template,
                       args.datacenter,
                       args.folder,
                       args.datastore,
                       args.cluster,
                       args.resource_pool,
                       False,
                       args.mem,
                       args.cpu,
                       flavor=args.flavor)

        vm = self.get_vm_obj(args.name, fail_missing=True)

        modify = ModifyCommands(self.connection)
        # Upgrade VM hardware version to the latest
        modify.change_vHWversion(vm, vHWversion='latest')
        # Change network assigned to the first interface on the VM
        if args.net:
            modify.change_network(vm, args.net, dev=1)
        if args.hdd:
            # Attach additional hard drive
            attach = AttachCommands(self.connection)
            attach.attach_hdd(vm, args.hdd)

        power = PowerCommands(self.connection)
        power.poweron_vm(vm)
        self.wait_for_guest_os(vm)

        if args.tags:
            args_copy = copy.deepcopy(args)
            args_copy.name = vm
            tag_cmd = TagCommands(self.connection)
            tag_cmd.execute(args_copy)

        execute = ExecCommands(self.connection)
        # Configure first ethernet device on the host, assumes traditional naming scheme
        if args.net_cfg:
            # assume prefix 24 if user forgots
            if len(args.net_cfg.split('/')) == 1:
                args.net_cfg += '/24'

            try:
                ip = netaddr.IPNetwork(args.net_cfg)
                gateway = list(ip)[1]
            except netaddr.core.AddrFormatError as e:
                ip, gateway = None, None
                self.logger.warning(
                    str(e.message) + '. Skipping network configuration')

            if ip and gateway:
                # expects script inside template
                commands = [
                    '/bin/bash /usr/share/vmcli/provision-interfaces.sh {} {} {} {} {}'
                    .format(ip.ip, ip.netmask, gateway, ip.network,
                            ip.broadcast)
                ]
                execute.exec_inside_vm(vm,
                                       commands,
                                       args.guest_user,
                                       args.guest_pass,
                                       wait_for_tools=True)

        if conf.VM_ADDITIONAL_CMDS:
            execute.exec_inside_vm(vm,
                                   conf.VM_ADDITIONAL_CMDS,
                                   args.guest_user,
                                   args.guest_pass,
                                   wait_for_tools=True)

        self.logger.info('Deployed vm {}'.format(args.name))

        # Execute callbacks from callbacks/ directory
        if args.callback:
            execute.exec_callbacks(args, args.callback)