Source code for cloudmesh_client.cloud.vm

from __future__ import print_function
from cloudmesh_client.common.ConfigDict import ConfigDict

from cloudmesh_client.common.todo import TODO
# add imports for other cloud providers in future
from cloudmesh_client.shell.console import Console
from cloudmesh_client.cloud.ListResource import ListResource
from cloudmesh_client.common.Printer import Printer
from cloudmesh_client.db import CloudmeshDatabase
from cloudmesh_client.cloud.iaas.CloudProvider import CloudProvider
from cloudmesh_client.common.Error import Error
from uuid import UUID
from cloudmesh_client.common.dotdict import dotdict
from builtins import input
from pprint import pprint
from cloudmesh_client.cloud.network import Network
from cloudmesh_client.default import (Names, Default)
import traceback


# noinspection PyPep8Naming
[docs]class Vm(ListResource): cm = CloudmeshDatabase()
[docs] @classmethod def generate_vm_name(cls, prefix=None, fill=3): return Default.generate_name(Names.VM_COUNTER, display_name=Default.user, prefix=prefix, fill=fill)
[docs] @classmethod def uuid(cls, name, category=None): vm = cls.get(name, category=category) if vm is None: return None return vm.uuid
[docs] @classmethod def get(cls, key, category=None): """ returns the value of the first objects matching the key with the given category. :param key: The dictionary key :param category: The category :return: """ if category is None: o = cls.cm.find(kind='vm', output='dict', scope='first', name=key) else: o = cls.cm.find(category=category, kind='vm', output='dict', scope='first', name=key) return o
[docs] @classmethod def construct_ip_dict(cls, ip_addr, name=None): # TODO kilo cloud as defualt should be avoided if name is None: Console.error("cloud name not set") return None try: d = ConfigDict("cloudmesh.yaml") cloud_details = d["cloudmesh"]["clouds"][name] # Handle Openstack Specific Output if cloud_details["cm_type"] == "openstack": ipaddr = {} for network in ip_addr: index = 0 for ip in ip_addr[network]: ipaddr[index] = {} ipaddr[index]["network"] = network ipaddr[index]["version"] = ip["version"] ipaddr[index]["addr"] = ip["addr"] index += 1 return ipaddr # Handle EC2 Specific Output if cloud_details["cm_type"] == "ec2": # Console.TODO("ec2 ip dict yet to be implemented") # TODO.implement() # :type str: ip_addr index = 0 ipaddr = {} ipaddr[index] = {} ipaddr[index]['addr'] = ip_addr return ipaddr # Handle Azure Specific Output if cloud_details["cm_type"] == "azure": index = 0 ipaddr = {} for ip in ip_addr: ipaddr[index] = {} ipaddr[index]["network"] = ip ipaddr[index]["version"] = 'ipv4' ipaddr[index]["addr"] = ip index += 1 return ipaddr except Exception as e: Console.error("error in vm construct dict %s" % e, traceflag=True)
[docs] @classmethod def isUuid(cls, name): try: UUID(name, version=4) return True except ValueError: return False
[docs] @classmethod def boot(cls, **kwargs): arg = dotdict(kwargs) for a in ["key", "name", "image", "flavor"]: if a not in kwargs: raise ValueError(a + " not in arguments to vm boot") conf = ConfigDict("cloudmesh.yaml") arg.user = arg.user or conf["cloudmesh"]["profile"]["user"] arg.group = arg.group or Default.group cloud_provider = CloudProvider(arg.cloud).provider if "nics" in arg: nics = arg.nics else: nics = None basic_dict = { "cloud": arg.cloud, "name": arg.name, "image": arg.image, "flavor": arg.flavor, "key": arg.key, "secgroup": [arg.secgroup], "nics": nics, "meta": { 'kind': 'cloudmesh', 'group': arg.group, 'cluster': arg.get('cluster', None), 'image': arg.image, 'flavor': arg.flavor, 'key': arg.key, 'category': arg.cloud } } # Special case for Azure where certificate details needs to be added if arg.cloud == "azure": kwargs = dict() kwargs['kind'] = "key_azure" db_result = cls.cm.find(**kwargs) # pprint("Key DB results") key_result = None try: for key in db_result: if key['name'] == arg.key: pprint("Found the key") key_result = key break if key_result is not None: new_dict_items = dict() new_dict_items['cert_thumbprint'] = key_result['fingerprint'] new_dict_items['pub_key_path'] = key_result['key_path'] new_dict_items['cert_path'] = key_result['certificate'] new_dict_items['pfx_path'] = key_result['pfx_path'] basic_dict.update(new_dict_items) else: pprint("None found in DB") except: traceback.print_exc() pprint("Exception while processing azure boot arguments") d = dotdict(basic_dict) Console.ok("Machine {name} is being booted on cloud {cloud} ..." .format(**arg)) print(Printer.attribute(d)) vm = cloud_provider.boot_vm(**d) if vm is not None: cls.refresh(cloud=arg.cloud) try: # TODO: Repair db schema for vm_azure, vm_libcloud, # vm_openstack. The following set only works with # openstack, no libcloud, no azure cls.cm.set(d.name, "key", d.key, scope="first", kind="vm") cls.cm.set(d.name, "image", d.image, scope="first", kind="vm") cls.cm.set(d.name, "flavor", d.flavor, scope="first", kind="vm") cls.cm.set(d.name, "group", arg.group, scope="first", kind="vm") cls.cm.set(d.name, "user", arg.user, scope="first", kind="vm") cls.cm.set(d.name, 'username', arg.username, scope='first', kind='vm') cls.cm.set(d.name, 'cluster', arg.cluster, scope='first', kind='vm') except: # cm.set error is identified as a warning, not an error import sys Console.warning("cls.cm.set error: %s" % (sys.exc_info()[0])) # update group and key # # cls.cm.update("vm", name=data.name) return vm
[docs] @classmethod def start(cls, **kwargs): arg = dotdict(kwargs) cloud_provider = CloudProvider(arg.cloud).provider for server in kwargs["servers"]: cloud_provider.start_vm(server) Console.ok("Machine {:} is being started on {:} Cloud..." .format(server, cloud_provider.cloud))
# Explicit refresh called after VM start, to update db. # cls.refresh(cloud=kwargs["cloud"])
[docs] @classmethod def stop(cls, **kwargs): arg = dotdict(kwargs) cloud_provider = CloudProvider(arg.cloud).provider for server in kwargs["servers"]: cloud_provider.stop_vm(server) Console.ok("Machine {:} is being stopped on {:} Cloud..." .format(server, cloud_provider.cloud))
# Explicit refresh called after VM stop, to update db. # cls.refresh(cloud=kwargs["cloud"])
[docs] @classmethod def delete(cls, **kwargs): arg = dotdict(kwargs) force = kwargs.get("force", Default.purge) if "cloud" in arg: cloud_provider = CloudProvider(arg.cloud).provider for server in kwargs["servers"]: vm = cls.cm.find(name=server, kind="vm", cloud=arg.cloud, scope="first") #vm_by_id = cls.cm.find(cm_id=server, kind="vm", cloud=arg.cloud, scope="first") #print (vm) #print(vm_by_id) #vm = vm or vm_by_id if vm: provider = vm["provider"] cloud = vm["category"] # If server has a floating ip associated, release it server_dict = Network.get_instance_dict( cloudname=arg.cloud, instance_id=server) floating_ip = server_dict["floating_ip"] if floating_ip is not None: Network.disassociate_floating_ip( cloudname=arg.cloud, instance_name=server, floating_ip=floating_ip) cloud_provider.delete_vm(server) if force: cls.cm.delete(kind="vm", provider=provider, category=cloud, name=server) # delete the record from db Console.ok( "VM record {:} is being deleted from the local database..." .format(server)) else: cls.cm.set(server, "status", "deleted", kind="vm", scope="first") # Console.ok("VM {:} is being deleted on {:} cloud...".format(server, cloud_provider.cloud)) else: Console.error("VM {:} can not be found." .format(server), traceflag=False) else: clouds = set() for server in arg.servers: vm = cls.cm.find(kind="vm", name=server, scope="first") if vm: cloud = vm["category"] provider = vm["provider"] cloud_provider = CloudProvider(cloud).provider clouds.add(cloud) cloud_provider.delete_vm(server) if force: cls.cm.delete(kind="vm", provider=provider, category=cloud, name=server) Console.ok( "VM record {:} is being deleted from the local database..." .format(server)) else: cls.cm.set(server, "status", "deleted", kind="vm", scope="first") # Console.ok("VM {:} is being deleted on {:} cloud...".format(server, cloud)) else: Console.error("VM {:} can not be found." .format(server), traceflag=False)
[docs] @classmethod def get_vms_by_name(cls, name, cloud): vm_data = cls.cm.find(kind="vm", name=name, category=cloud) if vm_data is None or len(vm_data) == 0: raise RuntimeError("VM data not found in database.") return vm_data
[docs] @classmethod def get_vms_by_group(cls, name): group = cls.cm.find(kind="group", name=name) return group
[docs] @classmethod def get_vm(cls, name): vm = cls.cm.find(kind="vm", name=name) return vm
[docs] @classmethod def rename(cls, **kwargs): arg = dotdict(kwargs) cloud_provider = CloudProvider(kwargs["cloud"]).provider # Check for vms with duplicate names in DB. vms = cls.get_vms_by_name(name=arg.oldname, cloud=arg.cloud) if len(vms) > 1: users_choice = "y" if not arg.force: print("More than 1 vms found with the same name as {}." .format(server)) users_choice = input( "Would you like to auto-order the new names? (y/n): ") if users_choice.strip() == "y": count = 1 for index in vms: count_new_name = "{0}{1}".format(arg.newname, count) # print(vms[index]) cloud_provider.rename_vm(vms[index]["uuid"], count_new_name) print( "Machine {0} with UUID {1} renamed to {2} on {3} cloud" .format(vms[index]["name"], vms[index]["uuid"], count_new_name, cloud_provider.cloud)) count += 1 elif users_choice.strip() == "n": cloud_provider.rename_vm(arg.oldname, arg.newname) print( "Machine {0} renamed to {1} on {2} Cloud..." .format(arg.oldname, arg.newname, cloud_provider.cloud)) else: Console.error("Invalid Choice.") return else: cloud_provider.rename_vm(arg.oldname, arg.newname) print("Machine {0} renamed to {1} on {2} Cloud..." .format(arg.oldname, arg.newname, cloud_provider.cloud)) # Explicit refresh called after VM rename, to update db. cls.refresh(cloud=arg.cloud)
[docs] @classmethod def info(cls, **kwargs): raise NotImplementedError()
[docs] @classmethod def list(cls, **kwargs): """ This method lists all VMs of the cloud """ arg = dotdict(kwargs) if "name" in arg: arg.name = arg.name arg.output = arg.output or 'table' # pprint (kwargs) # prevent circular dependency def vm_groups(vm): """ :param vm: name of the vm :return: a list of groups the vm is in """ try: query = { 'kind': "group", 'provider': 'general', "species": "vm", "member": vm, "scope": 'all', "output": 'dict' } d = cls.cm.find(**query) groups_vm = set() if d is not None and len(d) > 0: for vm in d: groups_vm.add(vm['name']) return list(groups_vm) except Exception as ex: Console.error(ex.message) return [] try: if "name" in arg and arg.name is not None: if cls.isUuid(arg.name): elements = cls.cm.find(kind="vm", category=arg.category, uuid=arg.name) else: elements = cls.cm.find(kind="vm", category=arg.category, label=arg.name) else: elements = cls.cm.find(kind="vm", category=arg.category) if elements is None or len(elements) == 0: return None for elem in elements: element = elem name = element["name"] groups = vm_groups(name) element["group"] = ','.join(groups) # print(elements) # order = ['id', 'uuid', 'name', 'cloud'] (order, header) = CloudProvider(arg.category).get_attributes("vm") # order = None if "name" in arg and arg.name is not None: return Printer.attribute(elements[0], output=arg.output) else: return Printer.write(elements, order=order, output=arg.output) except Exception as ex: Console.error(ex.message)
[docs] @classmethod def clear(cls, **kwargs): raise NotImplementedError()
[docs] @classmethod def refresh(cls, **kwargs): # print("Inside refresh") refreshed = cls.cm.refresh("vm", kwargs["cloud"]) # update counter vms = cls.cm.find(kind='vm') me = Default.user for vm in vms: name = vm['name'] if not name.startswith(me): continue number = name.split('-')[-1] try: # +1 as the stored counter is the next available counter new_counter = int(number) + 1 except ValueError: # name is not formatted correctly, possibly due to not # being started using cloudmesh continue old_counter = Default.get_counter(Names.VM_COUNTER) counter = max(new_counter, old_counter) Default.set_counter(Names.VM_COUNTER, counter) Console.debug_msg('Set counter ' + Names.VM_COUNTER + ' to ' + str(Default.get_counter(Names.VM_COUNTER))) return refreshed
[docs] @classmethod def status_from_cloud(cls, **kwargs): cloud_provider = CloudProvider(kwargs["cloud"]).provider vm = cloud_provider.get_vm(name=kwargs["name"]) return vm["status"]
[docs] @classmethod def set_login_user(cls, name=None, cloud=None, username=None): # cls.cm.set(name, "username", username, kind="vm", scope="first") vm = Vm.get(name, category=cloud) if vm is None: Console.error("VM could not be found", traceflag=False) return else: cls.cm.update(kind="vm", provider=vm["provider"], filter={'name': name}, update={"username": username} )
[docs] @classmethod def get_login_user(cls, name, cloud): print(name, cloud) Console.error("this method is wrong implemented") ''' if cls.isUuid(name): uuid = name else: vm_data = cls.cm.find(kind="vm", category=cloud, label=name) if vm_data is None or len(vm_data) == 0: raise RuntimeError("VM with label {} not found in database.".format(name)) uuid = list(vm_data.values())[0]["uuid"] # print(uuid) user_map_entry = cls.cm.find("VMUSERMAP", vm_uuid=uuid) # print(user_map_entry) if user_map_entry is None or len(user_map_entry) == 0: return None else: return list(user_map_entry.values())[0]["username"] '''
[docs] @classmethod def get_vm_public_ip(cls, vm_name, cloud): """ :param vm_name: Name of the VM instance whose Public IP has to be retrieved from the DB :param cloud: Libcloud supported Cloud provider name :return: Public IP as a list """ public_ip_list = [] vms = cls.get_vms_by_name(vm_name, cloud) keys = vms.keys() if keys is not None and len(keys) > 0: public_ip = vms[keys[0]]["public_ips"] if public_ip is not None and public_ip != "": public_ip_list.append(public_ip) return public_ip_list