def details(self, kind, cloud, id, format="table"): from cloudmesh_client.db.CloudmeshDatabase import CloudmeshDatabase try: cm = CloudmeshDatabase() if kind not in self.kind: raise ValueError('{} not defined'.format(kind)) elements = None for idkey in ["name", "uuid", "id"]: s = {idkey: id} try: elements = cm.find(kind, cloud=cloud, **s) except: pass if len(elements) > 0: break if len(elements) == 0: return None if format == "table": element = elements.values()[0] return attribute_printer(element) else: return dict_printer(elements, output=format) except Exception as ex: Console.error(ex.message, ex)
def details(self, kind, cloud, id, format="table"): from cloudmesh_client.db.CloudmeshDatabase import CloudmeshDatabase try: cm = CloudmeshDatabase() if kind not in self.kind: raise ValueError('{} not defined'.format(kind)) elements = None for idkey in ["name", "uuid", "id"]: s = {idkey: id} try: elements = cm.find(kind, category=cloud, **s) except: pass if len(elements) > 0: break if len(elements) == 0: return None if format == "table": element = list(elements.values())[0] return attribute_printer(element) else: return dict_printer(elements, output=format) except Exception as ex: Console.error(ex.message, ex)
def get_instance_dict(cls, **kwargs): """ Method to get instance dict :param kwargs: :return: instance dict """ cloudname = kwargs["cloudname"] instance_id = kwargs["instance_id"] # Cloudmesh database instance db = CloudmeshDatabase() # Lookup instance details from db if cls.isUuid(instance_id): instance_dict = db.find(kind="vm", cloud=cloudname, uuid=instance_id) else: instance_dict = db.find(kind="vm", cloud=cloudname, name=instance_id) # Instance not found in DB if cls.isDictEmpty(instance_dict): # auto detect instance_id feature vms = db.find("vm", cloud=cloudname) # check for each instance in db for vm in vms.values(): # if match found in either name/id if vm["uuid"].startswith(instance_id) or vm["name"].startswith(instance_id): # confirm choice with user print("Did you mean instance [{}] ? (y/n)".format(vm["name"])) choice = raw_input().lower() # if yes, return dict if choice == "y": return vm return None else: return instance_dict.values()[0]
def details(self, kind, category, id, format="table"): from cloudmesh_client.db.CloudmeshDatabase import CloudmeshDatabase cm = CloudmeshDatabase() try: if kind not in self.kind: raise ValueError('{} not defined'.format(kind)) elements = None for idkey in ["cm_id", "name", "uuid", "id", "cm_id"]: s = {idkey: id} try: elements = cm.find(kind=kind, category=category, **s) except: pass if elements is not None: break if elements is None: return None if len(elements) > 0: element = elements[0] if format == "table": return Printer.attribute(element) else: return Printer.write(element, output=format) else: return None except Exception as ex: Console.error(ex.message)
def add_db(cls, **kwargs): cm = CloudmeshDatabase() kwargs['name'] = kwargs.get('script_name') db_obj = {0: {"batchjob": kwargs}} cm.add_obj(db_obj) cm.save()
def test_002_find_all(self): """testing cm list --cloud ... default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") n = cm.find("default", scope="all", name='hallo') print(n.keys()) assert (len(n.keys()) > 0)
def test_003_find_filter(self): """testing cm list --cloud india default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") n = cm.find("default", scope="all", name="hallo") print(n.keys()) assert len(n.keys()) > 0
def test_003_find_filter(self): """testing cm list --cloud ... default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") n = cm.find("default", scope="all", name='hallo') print(list(n)) assert (len(list(n)) > 0)
def test_002_find_first(self): """testing cm list --cloud ... default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") n = cm.find("default", scope="first", name='hallo') first = list(n)[0] pprint(n) assert n["name"] == 'hallo' assert n["value"] == 'world'
def test_002_find_first(self): """testing cm list --cloud india default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") n = cm.find("default", scope="first", name="hallo") first = n.keys()[0] pprint(n) assert n["name"] == "hallo" assert n["value"] == "world"
def delete(cls, cluster, job, group=None): """ This method is used to terminate a job with the specified or a group of jobs job_id or job_name in a given cluster :param group: :param cluster: the cluster like comet :param job: the job id or name :return: success message or error """ try: if group is not None: # get the job ids from the db cm = CloudmeshDatabase() arguments = {'cluster': cluster, 'group': group} db_jobs = cm.find('batchjob', **arguments) list1 = [] for i in db_jobs: list1.append(db_jobs[i]['job_id']) # read active jobs active_jobs = json.loads(cls.queue(cluster)) list2 = [] for i in active_jobs: list2.append(active_jobs[i]['jobid']) # find intersection res = set(list1).intersection(set(list2)) if res is not None: for j in res: cmd = 'scancel {}'.format(str(j)) Shell.ssh(cluster, cmd) print("Deleted {}".format(j)) return "All jobs for group {} killed successfully".format(group) else: args = 'scancel ' if job.isdigit(): args += job else: args += "-n {}".format(job) Shell.ssh(cluster, args) return "Job {} killed successfully".format(job) except Exception as ex: print("in exceptio") print(ex) return ex
def add_db(cls, **kwargs): cm = CloudmeshDatabase() # replace options with correct values kwargs['dir'] = kwargs.pop('-D') kwargs['nodes'] = kwargs.pop('-N') kwargs['output_file'] = kwargs.pop('-o') kwargs['output_file'] = kwargs.pop('-t') kwargs['name'] = kwargs.get('script_name') db_obj = {0: {"batchjob": kwargs}} cm.add_obj(db_obj) cm.save()
def test_001_query(self): """testing cm list --cloud ... default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") cm.add(m) n = cm.query(DEFAULT).filter_by(name='hallo').first() print(n.__dict__) #assert n.__dict__["name"] == 'hallo' #assert n.__dict__["value"] == 'world' pprint(n.__dict__)
def test_001_query(self): """testing cm list --cloud india default""" HEADING() cm = CloudmeshDatabase(user="******") m = DEFAULT("hallo", "world") cm.add(m) n = cm.query(DEFAULT).filter_by(name="hallo").first() print(n.__dict__) # assert n.__dict__["name"] == 'hallo' # assert n.__dict__["value"] == 'world' pprint(n.__dict__)
class Ip(ListResource): cm = CloudmeshDatabase() @classmethod def list(cls, cloud=None, names=None, output='table', live=False): try: if live: cls.refresh(cloud) elements = cls.cm.find(kind="vm", category=cloud) result = [] if "all" in names: for element in elements: result.append(element) elif names is not None: for element in elements: if element["name"] in names: result.append(element) (order, header) = CloudProvider(cloud).get_attributes("ip") return Printer.write(result, order=order, header=header, output=output) except Exception as ex: Console.error(ex.message)
def test_001(self): HEADING() d = self.d banner("VM Data") pprint(d.__dict__) banner("Add VM") cm = CloudmeshDatabase() name = "vm1" uuid = d.id vm = VM(name=name, uuid=uuid, user="******", type="VM", category="mycloud", **d) banner("VM added") pprint(vm.__dict__) vm.bla = "bla" cm.add(vm) cm.save() banner("Get VM from Database") o = cm.find(VM, name=name) #o = cm.find_by_name(VM, name) pprint(o) assert True
def find_instance_name(cls, **kwargs): """ Method to find instance name :param kwargs: :return: instance_name """ cloudname = kwargs["cloudname"] instance_id = kwargs["instance_id"] # Cloudmesh database instance db = CloudmeshDatabase() # Lookup instance details from db instance_dict = db.find(kind="vm", cloud=cloudname, uuid=instance_id) # Get instance_name for vm if len(instance_dict) > 0: instance_name = instance_dict.values()[0]["name"] return instance_name
def get_instance_dict(cls, **kwargs): """ Method to get instance dict :param kwargs: :return: instance dict """ cloudname = kwargs["cloudname"] instance_id = kwargs["instance_id"] # Cloudmesh database instance db = CloudmeshDatabase() # Lookup instance details from db if cls.isUuid(instance_id): instance_dict = db.find(kind="vm", category=cloudname, uuid=instance_id) else: instance_dict = db.find(kind="vm", category=cloudname, name=instance_id) # Instance not found in DB if cls.isDictEmpty(instance_dict): # auto detect instance_id feature vms = db.find("vm", category=cloudname) # check for each instance in db for vm in list(vms.values()): # if match found in either name/id if vm["uuid"].startswith(instance_id) or \ vm["name"].startswith(instance_id): # confirm choice with user print("Did you mean instance [{}] ? (y/n)".format( vm["name"])) choice = input().lower() # if yes, return dict if choice == 'y': return vm return None else: return list(instance_dict.values())[0]
def test_001(self): HEADING() d = self.d banner("VM Data") pprint(d.__dict__) banner("Add VM") cm = CloudmeshDatabase() name = "vm1" uuid = d.id vm = VM(name=name, uuid=uuid, type="VM", cloud="mycloud", **d) pprint (vm.__dict__) vm.bla = "bla" cm.add(vm) cm.save() banner("Get VM from Database") o = cm.find(VM, name=name).first() #o = cm.find_by_name(VM, name) pprint (o.__dict__) assert True
def find_instance_name(cls, **kwargs): """ Method to find instance name :param kwargs: :return: instance_name """ cloudname = kwargs["cloudname"] instance_id = kwargs["instance_id"] # Cloudmesh database instance db = CloudmeshDatabase() # Lookup instance details from db instance_dict = db.find(kind="vm", category=cloudname, uuid=instance_id) # Get instance_name for vm if len(instance_dict) > 0: instance_name = list(instance_dict.values())[0]["name"] return instance_name
class Flavor(ListResource): cm = CloudmeshDatabase() @classmethod def refresh(cls, cloud): """ This method would refresh the flavor list by first clearing the database, then inserting new data :param cloud: the cloud name """ return cls.cm.refresh('flavor', cloud) @classmethod def list(cls, cloud, live=False, format="table"): """ This method lists all flavors of the cloud :param cloud: the cloud name """ # cm = CloudmeshDatabase() try: if live: cls.refresh(cloud) elements = cls.cm.find("flavor", category=cloud) # pprint(elements) (order, header) = CloudProvider(cloud).get_attributes("flavor") return dict_printer(elements, order=order, header=header, output=format) except Exception as ex: Console.error(ex.message, ex) @classmethod def details(cls, cloud, id, live=False, format="table"): if live: cls.refresh(cloud) return CloudProvider(cloud).details('flavor', cloud, id, format)
class Image(ListResource): cm = CloudmeshDatabase() @classmethod def refresh(cls, cloud): """ This method would refresh the image list by first clearing the database, then inserting new data :param cloud: the cloud name """ # Newly implemented refresh result = cls.cm.refresh("image", cloud) return result @classmethod def list(cls, cloud, format="table"): """ This method lists all images of the cloud :param cloud: the cloud name """ # TODO: make a CloudmeshDatabase without requiring the user= # cm = CloudmeshDatabase() try: elements = cls.cm.find("image", category=cloud) (order, header) = CloudProvider(cloud).get_attributes("image") return dict_printer(elements, order=order, header=header, output=format) except Exception as ex: Console.error(ex.message, ex) @classmethod def details(cls, cloud, id, live=False, format="table"): if live: cls.refresh(cloud) return CloudProvider(cloud).details('image', cloud, id, format)
class Counter(object): """ A counter is used to keep track of some value that can be increased and is associated with a user. Typically it is used to increment the vm id or the job id. """ cm = CloudmeshDatabase() @classmethod def incr(cls, name='counter', user=None): """ increments the counter by one :param name: name of the counter :param user: username associated with the counter :return: """ cls.cm.counter_incr(name=name, user=user) @classmethod def get(cls, name='counter', user=None): """ returns the value of the counter :param name: name of the counter :param user: username associated with the counter :return: the value of the counter """ return cls.cm.counter_get(name=name, user=user) @classmethod def set(cls, name='counter', value=None, user=None): """ sets a counter associated with a particular user :param name: name of the counter :param user: username associated with the counter :param value: the value :return: """ cls.cm.counter_set(name=name, value=value, user=user)
class Mesh(object): """ Design decision. Bellow we like to implement actually a type detection that based on one specifying a single string or a list does the right things I am not yet sure how to reflect this in the :type: but we can probably sat str or list of str example: delete("myvm1") delete("myvm2") delete("myvm1", "myvm2") delete(["myvm1", "myvm2"]) are doing all the same thing the way we do this is we implement for lists and than mak the other call recursively to a list We start simply with a print msg """ t_flavour = "flavour" t_image = "image" t_vm = "vm" def __init__(self): self.db = CloudmeshDatabase() @classmethod def clouds(self, format='json', order=None): filename = "cloudmesh.yaml" config = ConfigDict(filename) yaml_clouds = dict(config["cloudmesh"]["clouds"]) return dict_printer(yaml_clouds, output=format, order=order) @classmethod def verify(self): print("verify") @classmethod def refresh(self): print("refresh") @classmethod def register(self): print("register") self.db.update(['vm', 'image', 'flavor'], ['india', 'aws', 'azure']) """ @classmethod def key_add(self, *args): print ("key add") sshm = SSHKeyManager() sshdb = SSHKeyDBManager() print len(args) if len(args) == 1: if os.path.isdir(path_expand(args[0])): sshm.get_from_dir(args[0]) print sshm for key in sshm.__keys__: print key print sshm.__keys__[key]['comment'] sshdb.add_from_sshkey(sshm.__keys__[key], key) else: # args is the github username sshm.get_from_git(args[0]) print args[0] for key in sshm.__keys__: sshdb.add_from_sshkey(sshm.__keys__[key], key) elif len(args) == 2: keyname = args[0] path = path_expand(args[1]) sshdb.add(path, keyname) def key_delete(self, keynames): sshdb = SSHKeyDBManager() names = keynames.split(',') for name in names: sshdb.delete(name) """ @classmethod def clear(self): print("clear") self.db.delete_all(["VM", "FLAVOR", "IMAGE", "DFAULT"]) @classmethod def dump(self, filename): """ :param filename: name of the db file that will receive the content of cloudmesh.db :return: """ from_file = path_expand("~/.cloudmesh/cloudmesh.db") to_file = path_expand("~/.cloudmesh/{}".format(filename)) shutil.copyfile(from_file, to_file) print("dump") @classmethod def load(self, filename): """ :param filename: name of the db file located on ./cloudmesh that will be copied do cloudmesh.db :return: """ print("load") from_file = path_expand("~/.cloudmesh/{}".format(filename)) to_file = path_expand("~/.cloudmesh/cloudmesh.db") shutil.copyfile(from_file, to_file) @classmethod def vms(self, clouds): if isinstance(clouds, str): return self.vms(kind, [clouds], output) for cloud in clouds: print("get vm ids from ", cloud) @classmethod def list(self, kind, clouds, output="table"): """ Lists the IaaS objects such as flavors, images, vms in the specified output format. :param kind: flavor, image, vm :type kind: str :param clouds: list of clouds we list :type clouds: list of str :param output: json, yaml, table :type output: str :return: """ if isinstance(clouds, str): return self.list(kind, [clouds], output) for cloud in clouds: if kind in ["f", "flavour"]: print("flavor", cloud) elif kind in ["v", "vm"]: print("vm", cloud) elif kind in ["i", "image"]: print("image", cloud) else: print("kind", kind, "not supported") return "done" @classmethod def boot(self, cloud=None, image=None, flavor=None, key=None, arguments=None): """ Boots the image on a specified cloud :param image: The name of the image :type image: str :param flavor: The name of the flavor :type flavor: str :param key: The name of the key :type key: str :param cloud: The name of the cloud :type cloud: str :param arguments: An array of arguments :type arguments: list of str :return: the id of the vm :rtype: str """ if cloud is None: cloud = Default.get("cloud", "general") print("get default cloud: " + str(cloud)) if image is None: image = Default.get("image", cloud) print("get default image ", str(image)) if flavor is None: flavor = Default.get("flavor", cloud) print("get default flavor ", str(flavor)) if key is None: key = Default.get("key", str(cloud)) print("get default key ", str(key)) # command_key print("boot an image", image, flavor, key, cloud, arguments) pass @classmethod def delete(self, ids): """ delete the vms Example: delete("gregor-[001-010]") :param ids: host ids specified in hostlist format :return: """ names = Parameter.expand(ids) for name in names: print("delete", name) @classmethod def _generate_name(self, prefix, number, padding): """ method to create a vm name from a prefix, with given padding of 0 :param prefix: the prefix of the name :type prefix: str :param number: the number :type number: str or int :param padding: the number of 0 that are used for padding :type padding: str or int :return: """ format_string = prefix + "-{0:0" + str(padding) + "}" return format_string.format(number) @classmethod def _parse_name(self, name): """ returns the prefix, the number and the padding length :param name: the name to parse :type name: str :return: prefix, number, padding length :rtype: str, int, int """ prefix, number = name.rsplit('-', 1) n = int(number) padding = len(number) return prefix, n, padding @classmethod def next_name(self, name): """ generates the next name. Example: next_name("gregor-0001") gregor-0002 :param name: the name :type name: str :return: the new name :rtype: str """ prefix, n, padding = self._parse_name(name) n += 1 return self._generate_name(prefix, n, padding) @classmethod def get_name(self): """gets the next name of a vm while increasing its index.""" # the name format is written to the database based on a set_name # the database will contain an index that stores the current index. # if no name hase bee defined and this is accessed first the name # username-0001 will be used where username is your username return "notimplemented-0001" @classmethod def set_name(self, name): """ :param name: :return: """ """sets the name of a vm. The name is ended by a simple number. The number of 0 in it wil be used for padding. set_name("gregor-001") This information will be used when getting the next index. If the next index exceeds the padding we simply just increase the number. """ prefix, n, padding = self.parse_name(name)
def __init__(self): self.db = CloudmeshDatabase()
def start(cls, name, count, cloud, image, flavor, group): """ TODO: group has not been used yet. fix that starts a virtual Machine (VM) or a set of VMs :param name: name of the virtual machine :type name: string, None :param count: give the number of servers to start :type count: integer, None :param cloud: give a cloud to work on, if not given, selected or default cloud will be used :type cloud: integer, None :param image: image name :type image: string, None :param flavor:flavor name. m1.medium, for example :type flavor: string, None :param group: the group name of server :type group: string, None :return: """ # TODO: vm start (without arguments) use default cloud, image, flavor, group. if cloud is None: # use default values for cloud, image and flavor pass config = CloudRegister.get(cloud) if cm_type in ["openstack"]: provider = Provider.OPENSTACK OpenStack = get_driver(provider) try: cloud_credentials = config['credentials'] except Exception, e: Console.error(e.message) return # TODO: THIS MAY BE JUST TRUE IF THE CERT PATH EXISTS IN CREDENTIAL # set path to cacert and enable ssl connection libcloud.security.CA_CERTS_PATH = [Config.path_expand(cloud_credentials['OS_CACERT'])] libcloud.security.VERIFY_SSL_CERT = True auth_url = "%s/tokens/" % cloud_credentials['OS_AUTH_URL'] driver = OpenStack(cloud_credentials['OS_USERNAME'], cloud_credentials['OS_PASSWORD'], ex_force_auth_url=auth_url, ex_tenant_name=cloud_credentials['OS_TENANT_NAME'], ex_force_auth_version='2.0_password', ex_force_service_region='regionOne') # obtain available images # type of images: <class 'libcloud.compute.base.NodeImage'> images = driver.list() if not [i for i in images if i.name == image]: Console.error("Image {:} not found".format(image)) return image = [i for i in images if i.name == image][0] # sizes/flavors sizes = driver.list_sizes() if not [i for i in sizes if i.name == flavor]: Console.error("Flavor {:} not found".format(flavor)) return size = [i for i in sizes if i.name == flavor][0] if count is None: count = 1 count = int(count) def __findsufix(): # TODO: THIS IS A BIG BUG AS THE NEXT VM NAME IS NOT MANAGED BY SUFFIX """ Virtual machine name (VM) format: string-001, string-002, ..., string-n returns the max sufix from the VM list. It will be used in the new vm name in order to avoid VMs with the same name. :return: max sufix :return type: string """ nodes = driver.list_nodes() sufix = 1 for i in nodes: n = 0 try: n = int(i.name.split('-', 1)[1]) # not always is int(i.name.split('-', 1)[1] a digit except: pass if sufix <= n: sufix = n + 1 sufix = str(sufix).zfill(3) return sufix # set vm name sufix = __findsufix() c = CloudmeshDatabase() if name is None: c.name(cloud_credentials['OS_USERNAME'] + "-" + sufix) else: c.name(name + "-" + sufix) # launch a new VM Console.ok("Booting Virtual Machine...") for i in range(0, count): name = c.get_name() try: node = driver.create_node(name=name, image=image, size=size) except Exception, e: Console.error("{:} virtual machines have not been created. {:}".format(count - i, e.message)) return c.name(c.next_name())
def do_vm(self, args, arguments): """ :: Usage: vm default [--cloud=CLOUD][--format=FORMAT] vm refresh [all][--cloud=CLOUD] vm boot [--name=NAME] [--cloud=CLOUD] [--username=USERNAME] [--image=IMAGE] [--flavor=FLAVOR] [--group=GROUP] [--public] [--secgroup=SECGROUP] [--key=KEY] [--dryrun] vm boot [--n=COUNT] [--cloud=CLOUD] [--username=USERNAME] [--image=IMAGE] [--flavor=FLAVOR] [--group=GROUP] [--public] [--secgroup=SECGROUP] [--key=KEY] [--dryrun] vm ping [NAME] [N] vm console [NAME] [--group=GROUP] [--cloud=CLOUD] [--force] vm start [NAMES] [--group=GROUP] [--cloud=CLOUD] [--force] vm stop [NAMES] [--group=GROUP] [--cloud=CLOUD] [--force] vm terminate [NAMES] [--group=GROUP] [--cloud=CLOUD] [--force] vm delete [NAMES] [--group=GROUP] [--cloud=CLOUD] [--keep] [--dryrun] vm ip assign [NAMES] [--cloud=CLOUD] vm ip show [NAMES] [--group=GROUP] [--cloud=CLOUD] [--format=FORMAT] [--refresh] vm ip inventory [NAMES] [--header=HEADER] [--file=FILE] vm ssh [NAME] [--username=USER] [--quiet] [--ip=IP] [--cloud=CLOUD] [--key=KEY] [--command=COMMAND] vm rename [OLDNAMES] [NEWNAMES] [--force] [--dryrun] vm list [NAMES] [--cloud=CLOUDS|--active] [--group=GROUP] [--format=FORMAT] [--refresh] vm status [NAMES] vm wait [--cloud=CLOUD] [--interval=SECONDS] vm info [--cloud=CLOUD] [--format=FORMAT] vm check NAME vm username USERNAME [NAMES] [--cloud=CLOUD] Arguments: COMMAND positional arguments, the commands you want to execute on the server(e.g. ls -a) separated by ';', you will get a return of executing result instead of login to the server, note that type in -- is suggested before you input the commands NAME server name. By default it is set to the name of last vm from database. NAMES server name. By default it is set to the name of last vm from database. KEYPAIR_NAME Name of the openstack keypair to be used to create VM. Note this is not a path to key. NEWNAMES New names of the VM while renaming. OLDNAMES Old names of the VM while renaming. Options: --username=USERNAME the username to login into the vm. If not specified it will be guessed from the image name and the cloud --ip=IP give the public ip of the server --cloud=CLOUD give a cloud to work on, if not given, selected or default cloud will be used --count=COUNT give the number of servers to start --detail for table print format, a brief version is used as default, use this flag to print detailed table --flavor=FLAVOR give the name or id of the flavor --group=GROUP give the group name of server --secgroup=SECGROUP security group name for the server --image=IMAGE give the name or id of the image --key=KEY specify a key to use, input a string which is the full path to the private key file --keypair_name=KEYPAIR_NAME Name of the openstack keypair to be used to create VM. Note this is not a path to key. --user=USER give the user name of the server that you want to use to login --name=NAME give the name of the virtual machine --force rename/ delete vms without user's confirmation --command=COMMAND specify the commands to be executed Description: commands used to boot, start or delete servers of a cloud vm default [options...] Displays default parameters that are set for vm boot either on the default cloud or the specified cloud. vm boot [options...] Boots servers on a cloud, user may specify flavor, image .etc, otherwise default values will be used, see how to set default values of a cloud: cloud help vm start [options...] Starts a suspended or stopped vm instance. vm stop [options...] Stops a vm instance . vm delete [options...] Delete servers of a cloud, user may delete a server by its name or id, delete servers of a group or servers of a cloud, give prefix and/or range to find servers by their names. Or user may specify more options to narrow the search vm floating_ip_assign [options...] assign a public ip to a VM of a cloud vm ip show [options...] show the ips of VMs vm ssh [options...] login to a server or execute commands on it vm list [options...] same as command "list vm", please refer to it vm status [options...] Retrieves status of last VM booted on cloud and displays it. Tip: give the VM name, but in a hostlist style, which is very convenient when you need a range of VMs e.g. sample[1-3] => ['sample1', 'sample2', 'sample3'] sample[1-3,18] => ['sample1', 'sample2', 'sample3', 'sample18'] Quoting commands: cm vm login gvonlasz-004 --command=\"uname -a\" """ """ # terminate # issues a termination to the cloud, keeps vm in database # delete # issues a terminate if not already done # (remember you do not have to go to cloud if state is already terminated) # deletes the vm from database # # bulk rename rename abc[0-1] def[3-4] renames the abc0,abc1 -> def3,def4 if arguments["rename"]: oldnames = Parameter.expand(arguments["OLDNAME"]) newnames = Parameter.expand(arguments["NEWNAME"]) # check if new names ar not already taken # to be implemented if len(oldnames) == len(newnames): for i in range(0, len(oldnames)): oldname = oldnames[i] newname = newnames[i] if newname is None or newname == '': print("New node name cannot be empty") else: print(Cluster.rename_node(clusterid, oldname, newname)) """ cm = CloudmeshDatabase() def _print_dict(d, header=None, output='table'): return Printer.write(d, order=["id", "name", "status"], output=output, sort_keys=True) def _print_dict_ip(d, header=None, output='table'): return Printer.write(d, order=["network", "version", "addr"], output=output, sort_keys=True) def get_vm_name(name=None, offset=0, fill=3): if name is None: count = Default.get_counter(name='name') + offset prefix = Default.user if prefix is None or count is None: Console.error("Prefix and Count could not be retrieved correctly.", traceflag=False) return name = prefix + "-" + str(count).zfill(fill) return name def _refresh_cloud(cloud): try: msg = "Refresh VMs for cloud {:}.".format(cloud) if Vm.refresh(cloud=cloud): Console.ok("{:} OK.".format(msg)) else: Console.error("{:} failed".format(msg), traceflag=False) except Exception as e: Console.error("Problem running VM refresh", traceflag=False) def _get_vm_names(): vm_list = cm.find(kind="vm") vms = [vm["name"] for vm in vm_list] names = pattern = arguments["NAMES"] if pattern is not None: if "*" in pattern: names = search(vms, pattern) else: names = Parameter.expand(names) if names == ['last'] or names is None: names == [Default.vm] return vm_list, names cloud = arguments["--cloud"] or Default.cloud config = ConfigDict("cloudmesh.yaml") active_clouds = config["cloudmesh"]["active"] def _refresh(cloud): all = arguments["all"] or None if all is None: _refresh_cloud(cloud) else: for cloud in active_clouds: _refresh_cloud(cloud) arg = dotdict(arguments) arg.cloud = arguments["--cloud"] or Default.cloud arg.image = arguments["--image"] or Default.get(name="image", category=arg.cloud) arg.flavor = arguments["--flavor"] or Default.get(name="flavor", category=arg.cloud) arg.group = arguments["--group"] or Default.group arg.secgroup = arguments["--secgroup"] or Default.secgroup arg.key = arguments["--key"] or Default.key arg.dryrun = arguments["--dryrun"] arg.name = arguments["--name"] arg.format = arguments["--format"] or 'table' arg.refresh = Default.refresh or arguments["--refresh"] arg.count = int(arguments["--n"] or 1) arg.dryrun = arguments["--dryrun"] arg.verbose = not arguments["--quiet"] # # in many cases use NAMES # if arg.NAMES is not None: # arg.names = Parameter.expand(arg.NAMES) # gvonlasz[001-002] gives ["gvonlasz-001", "gvonlasz-002"] # else: # arg.names = None # if arguments["boot"]: arg.username = arguments["--username"] or Image.guess_username(arg.image) is_name_provided = arg.name is not None arg.user = Default.user for index in range(0, arg.count): vm_details = dotdict({ "cloud": arg.cloud, "name": get_vm_name(arg.name, index), "image": arg.image, "flavor": arg.flavor, "key": arg.key, "secgroup": arg.secgroup, "group": arg.group, "username": arg.username, "user": arg.user }) # correct the username vm_details.username = Image.guess_username_from_category( vm_details.cloud, vm_details.image, username=arg.username) try: if arg.dryrun: print(Printer.attribute(vm_details, output=arg.format)) msg = "dryrun info. OK." Console.ok(msg) else: vm_id = Vm.boot(**vm_details) if vm_id is None: msg = "info. failed." Console.error(msg, traceflag=False) return "" # set name and counter in defaults Default.set_vm(value=vm_details.name) if is_name_provided is False: Default.incr_counter("name") # Add to group if vm_id is not None: Group.add(name=vm_details.group, species="vm", member=vm_details.name, category=vm_details.cloud) msg = "info. OK." Console.ok(msg) except Exception as e: Console.error("Problem booting instance {name}".format(**vm_details), traceflag=False) elif arguments["username"]: arg.username = arguments["--username"] or Image.guess_username(arg.image) cloud = arg.cloud username = arg.USERNAME if arg.NAMES is None: names = [Default.vm] else: names = Parameter.expand(arg.NAMES) if len(names) == 0: return for name in names: arg.name = name Console.ok("Set username for {cloud}:{name} to {USERNAME}".format(**arg)) Vm.set_login_user(name=name, cloud=cloud, username=username) elif arguments["default"]: try: count = Default.get_counter() prefix = Username() if prefix is None or count is None: Console.error("Prefix and Count could not be retrieved correctly.", traceflag=False) return vm_name = prefix + "-" + str(count).zfill(3) arg = { "name": vm_name, "cloud": arguments["--cloud"] or Default.cloud } for attribute in ["image", "flavor"]: arg[attribute] = Default.get(name=attribute, category=cloud) for attribute in ["key", "group", "secgroup"]: arg[attribute] = Default.get(name=attribute, category='general') output = arguments["--format"] or "table" print(Printer.attribute(arg, output=output)) msg = "info. OK." Console.ok(msg) ValueError("default command not implemented properly. Upon " "first install the defaults should be read from yaml.") except Exception as e: # Error.traceback(e) Console.error("Problem listing defaults", traceflag=False) elif arguments["ping"]: try: if arguments["NAME"] is None and arguments["N"] is None: name = arguments["NAME"] or Default.vm n = arguments["N"] or 1 elif arguments["NAME"].isdigit(): n = arguments["NAME"] name = Default.vm else: name = arguments["NAME"] or Default.vm n = arguments["N"] or 1 print("Ping:", name, str(n)) vm = dotdict(Vm.list(name=name, category=cloud, output="dict")["dict"]) ip = vm.floating_ip result = Shell.ping(host=ip, count=n) print(result) except Exception as e: Console.error(e.message, traceflag=False) elif arguments["console"]: try: name = arguments["NAME"] or Default.vm vm = dotdict(Vm.list(name=name, category=cloud, output="dict")["dict"]) cloud_provider = CloudProvider(cloud).provider vm_list = cloud_provider.list_console(vm.uuid) print(vm_list) msg = "info. OK." Console.ok(msg) except Exception as e: # Error.traceback(e) Console.error("Problem retrieving status of the VM", traceflag=False) elif arguments["status"]: try: cloud_provider = CloudProvider(cloud).provider vm_list = cloud_provider.list_vm(cloud) vms = [vm_list[i]["name"] for i in vm_list ] print ("V", vms) pattern = arguments["NAMES"] if pattern is not None: if "*" in pattern: print ("serach") names = search(vms, pattern) else: names = Parameter.expand() for i in vm_list: if vm_list[i]["name"] in names: print("{} {}".format(vm_list[i]["status"], vm_list[i]["name"])) else: print("{} {}".format(vm_list[0]["status"], vm_list[0]["name"])) except Exception as e: # Error.traceback(e) Console.error("Problem retrieving status of the VM", traceflag=True) elif arguments["wait"]: interval = arguments["--interval"] or 5 try: cloud_provider = CloudProvider(cloud).provider for i in range(1,10): vm_list = cloud_provider.list_vm(cloud) time.sleep(float(1)) d = {} for id in vm_list: vm = vm_list[id] d[vm["name"]] = vm["status"] print (d) print("{} {}".format(vm_list[0]["status"], vm_list[0]["name"])) if vm_list[0]["status"] in ['ACTIVE']: return except Exception as e: # Error.traceback(e) Console.error("Problem retrieving status of the VM", traceflag=True) elif arguments["info"]: try: cloud_provider = CloudProvider(cloud).provider vms = cloud_provider.list_vm(cloud) vm = vms[0] output_format = arguments["--format"] or "table" print(Printer.attribute(vm, output=output_format)) msg = "info. OK." Console.ok(msg) except Exception as e: # Error.traceback(e) Console.error("Problem retrieving status of the VM", traceflag=False) elif arguments["check"]: test = {} try: names = Parameter.expand(arguments["NAME"]) id = 0 for name in names: print("Not implemented: {}".format(name)) # TODO: check the status of the vms status = "active" # TODO: check if they have a floating ip # TODO: get ip floating_ip = "127.0.0.1" ip = True # ping # TODO: ping the machine with the shell command ping = True # check if one can login and run a command check = False try: r = Shell.execute("uname", "-a") # do a real check check = True except: check = False test[name] = { "id": id, "name": name, "status": status, "ip": ip, "ping": ping, "login": check } id += 1 pprint(test) print(Printer.write(test, order=["id", "name", "status", "ip", "ping", "login"], output="table", sort_keys=True)) msg = "not yet implemented. failed." Console.error(msg, traceflag=False) except Exception as e: # Error.traceback(e) Console.error("Problem retrieving status of the VM", traceflag=False) elif arguments["start"]: try: servers = Parameter.expand(arguments["NAMES"]) # If names not provided, take the last vm from DB. if len(servers) == 0: last_vm = Default.vm if last_vm is None: Console.error("No VM records in database. Please run vm refresh.", traceflag=False) return "" name = last_vm["name"] # print(name) servers = list() servers.append(name) group = arguments["--group"] force = arguments["--force"] # if default cloud not set, return error if not cloud: Console.error("Default cloud not set.", traceflag=False) return "" Vm.start(cloud=cloud, servers=servers) msg = "info. OK." Console.ok(msg) except Exception as e: # Error.traceback(e) Console.error("Problem starting instances", traceflag=False) elif arguments["stop"]: try: servers = Parameter.expand(arguments["NAMES"]) # If names not provided, take the last vm from DB. if servers is None or len(servers) == 0: last_vm = Default.vm if last_vm is None: Console.error("No VM records in database. Please run vm refresh.", traceflag=False) return "" name = last_vm["name"] # print(name) servers = list() servers.append(name) group = arguments["--group"] force = arguments["--force"] # if default cloud not set, return error if not cloud: Console.error("Default cloud not set.", traceflag=False) return "" Vm.stop(cloud=cloud, servers=servers) msg = "info. OK." Console.ok(msg) except Exception as e: # Error.traceback(e) Console.error("Problem stopping instances", traceflag=False) elif arguments["refresh"]: _refresh(cloud) elif arguments["delete"]: dryrun = arguments["--dryrun"] group = arguments["--group"] force = not arguments["--keep"] cloud = arguments["--cloud"] vms, servers = _get_vm_names() if servers in [None, []]: Console.error("No vms found.", traceflag=False) return "" for server in servers: if dryrun: Console.ok("Dryrun: delete {}".format(server)) else: Vm.delete(servers=[server], force=force) return "" elif arguments["ip"] and arguments["assign"]: if arguments["NAMES"] is None: names = [Default.vm] else: names = Parameter.expand(arguments["NAMES"]) for name in names: # ip = Network.get_floatingip(....) vm = dotdict(Vm.list(name=name, category=cloud, output="dict")["dict"]) if vm.floating_ip is None: Console.ok("Assign IP to {}".format(name)) try: floating_ip = Network.find_assign_floating_ip(cloudname=cloud, instance_id=name) Vm.refresh(cloud=cloud) if floating_ip is not None: print( "Floating IP assigned to {:} is {:}".format( name, floating_ip)) msg = "info. OK." Console.ok(msg) except Exception as e: Console.error("Problem assigning floating ips.", traceflag=False) else: Console.error("VM {} already has a floating ip: {}".format(name, vm.floating_ip), traceflag=False) elif arguments["ip"] and arguments["inventory"]: vms, names = _get_vm_names() if names in [None, []]: if str(Default.vm) in ['None', None]: Console.error("The default vm is not set.", traceflag=False) return "" else: names = [Default.vm] header = arguments["--header"] or "[servers]" filename = arguments["--file"] or "inventory.txt" try: vm_ips = [] for vm in vms: if vm["name"] in names: print (vm["name"]) vm_ips.append(vm["floating_ip"]) result = header + "\n" result += '\n'.join(vm_ips) Console.ok("Creating inventory file: {}".format(filename)) Console.ok(result) with open(filename, 'w') as f: f.write(result) except Exception as e: Console.error("Problem getting ip addresses for instance", traceflag=True) elif arguments["ip"] and arguments["show"]: if arguments["NAMES"] is None: if str(Default.vm) in ['None', None]: Console.error("The default vm is not set.", traceflag=False) return "" else: names = [Default.vm] else: names = Parameter.expand(arguments["NAMES"]) group = arguments["--group"] output_format = arguments["--format"] or "table" refresh = arguments["--refresh"] try: ips = Ip.list(cloud=arg.cloud, output=output_format, names=names) print(ips) except Exception as e: Console.error("Problem getting ip addresses for instance", traceflag=False) elif arguments["ssh"]: def _print(msg): if arg.verbose: Console.msg(msg) chameleon = "chameleon" in ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["clouds"][arg.cloud][ "cm_host"] if chameleon: arg.username = "******" elif arg.cloud == "azure": arg.username = ConfigDict(filename="cloudmesh.yaml")["cloudmesh"]["clouds"]["azure"]["default"]["username"] else: if arg.username is None: Console.error("Could not guess the username of the vm", traceflag=False) return arg.username = arguments["--username"] or Image.guess_username(arg.image) arg.command = arguments["--command"] data = dotdict({ 'name': arguments["NAME"] or Default.vm, 'username': arg.username, 'cloud': arg.cloud, 'command': arg.command }) _print("login {cloud}:{username}@{name}".format(**data)) vm = Vm.get(data.name, category=data.cloud) Vm.set_login_user(name=data.name, cloud=data.cloud, username=data.username) data.floating_ip = vm.floating_ip data.key = arguments["--key"] or Default.key _print(Printer.attribute(data)) ''' if vm.username is None: user_from_db = Vm.get_login_user(vm.name, vm.cloud) user_suggest = user_from_db or Default.user username = input("Username (Default: {}):".format(user_suggest)) or user_suggest Vm.set_login_user(name=data.name, cloud=cloud, username=data.username) ''' ip = arguments["--ip"] commands = arguments["--command"] ip_addresses = [] cloud_provider = CloudProvider(cloud).provider ip_addr = cloud_provider.get_ips(vm.name) ipaddr_dict = Vm.construct_ip_dict(ip_addr, cloud) for entry in ipaddr_dict: ip_addresses.append(ipaddr_dict[entry]["addr"]) if len(ip_addresses) > 0: if ip is not None: if ip not in ip_addresses: Console.error("IP Address specified does not match with the host.", traceflag=False) return "" else: _print("Determining IP Address to use with a ping test.") # This part assumes that the ping is allowed to the machine. for ipadd in ip_addresses: _print("Checking {:}...".format(ipadd)) try: # Evading ping test, as ping is not enabled for VMs on Azure cloud # socket.gethostbyaddr(ipadd) # ip will be set if above command is successful. ip = ipadd except socket.herror: _print("Cannot reach {:}.".format(ipadd)) if ip is None: _print("Unable to connect to the machine") return "" else: _print("IP to be used is: {:}".format(ip)) # # TODO: is this correctly implemented # if not cloud == 'azure': SecGroup.enable_ssh(cloud=cloud) if arg.verbose: Console.info("Connecting to Instance at IP:" + format(ip)) # Constructing the ssh command to connect to the machine. sshcommand = "ssh" if arg.key is not None: sshcommand += " -i {:}".format(arg.key) sshcommand += " -o StrictHostKeyChecking=no" sshcommand += " {:}@{:}".format(data.username, ip) if commands is not None: sshcommand += " \"{:}\"".format(commands) # print(sshcommand) os.system(sshcommand) else: Console.error("No Public IPs found for the instance", traceflag=False) elif arguments["list"]: # groups = Group.list(output="dict") arg = dotdict(arguments) arg.names = arguments["NAMES"] arg.group = arguments["--group"] if arg.group is None: arg.group = [] else: arg.group = Parameter.expand(arguments["--group"]) arg.refresh = arguments["--refresh"] or Default.refresh if arg.NAMES is not None: arg.names = Parameter.expand(arguments["NAMES"]) else: arg.names = ["all"] _format = arguments["--format"] or "table" if arguments["--active"]: clouds = active_clouds else: if arguments["--cloud"]: clouds = Parameter.expand(arguments["--cloud"]) else: clouds = [Default.cloud] try: d = ConfigDict("cloudmesh.yaml") for cloud in clouds: if arg.refresh: _refresh(cloud) Console.ok("Listing VMs on Cloud: {:}".format(cloud)) vms = Vm.list(category=cloud, output="raw") # print ("XXX", type(vms), vms) if vms is None: break result = [] if "all" in arg.names: if result is None: result = [] else: result = vms elif arg.group is not None and len(arg.group) > 0: for vm in vms: if vm["group"] in arg.group: result.append(vm) elif arg.names is not None and len(arg.names) > 0: for vm in vms: if vm["name"] in arg.names: result.append(vm) if len(result) > 0: # print(result) (order, header) = CloudProvider(cloud).get_attributes("vm") print(Printer.write(result, order=order, output=_format) ) else: Console.error("No data found with requested parameters.", traceflag=False) except Exception as e: # Error.traceback(e) Console.error("Problem listing all instances", traceflag=False) elif arguments["rename"]: try: oldnames = Parameter.expand(arguments["OLDNAMES"]) newnames = Parameter.expand(arguments["NEWNAMES"]) force = arguments["--force"] if oldnames is None or newnames is None: Console.error("Wrong VMs specified for rename", traceflag=False) elif len(oldnames) != len(newnames): Console.error("The number of VMs to be renamed is wrong", traceflat=False) else: for i in range(0, len(oldnames)): oldname = oldnames[i] newname = newnames[i] if arguments["--dryrun"]: Console.ok("Rename {} to {}".format(oldname, newname)) else: Vm.rename(cloud=cloud, oldname=oldname, newname=newname, force=force ) msg = "info. OK." Console.ok(msg) except Exception as e: # Error.traceback(e) Console.error("Problem deleting instances", traceflag=False) return ""
class Test_cloud_model(object): cm = CloudmeshDatabase() data = dotdict({ "cloud": Default.cloud, }) def run(self, command): command = command.format(**self.data) banner(command, c="-") print(command) parameter = command.split(" ") shell_command = parameter[0] args = parameter[1:] result = Shell.execute(shell_command, args) print(result) return str(result) def setup(self): self.d = { 'name': 'vm1', 'cloud': 'india', 'update': '2015-06-18 22:11:48 UTC', 'user': '******', 'extra': {'created': '2015-05-21T20:37:10Z', 'metadata': {'base_image_ref': '398746398798372493287', 'description': None, 'image_location': 'snapshot', 'image_state': 'available', 'image_type': 'snapshot', 'instance_type_ephemeral_gb': '0', 'instance_type_flavorid': '3', 'instance_type_id': '1', 'instance_type_memory_mb': '4096', 'instance_type_name': 'm1.medium', 'instance_type_root_gb': '40', 'instance_type_rxtx_factor': '1.0', 'instance_type_swap': '0', 'instance_type_vcpus': '2', 'instance_uuid': '386473678463876387', 'kernel_id': None, 'network_allocated': 'True', 'owner_id': '36487264932876984723649', 'ramdisk_id': None, 'user_id': '762387463827463278649837'}, 'minDisk': 40, 'minRam': 0, 'progress': 100, 'serverId': 'yiuksajhlkjahl', 'status': 'ACTIVE', 'updated': '2015-05-27T02:11:48Z'}, 'id': '39276498376478936247832687', 'name': 'VM with Cloudmesh Configured Completely' } self.vm = { 'extra': {'access_ip': '', 'availability_zone': 'nova', 'config_drive': '', 'created': '2015-06-19T00:06:58Z', 'disk_config': 'MANUAL', 'flavorId': '1', 'hostId': '', 'imageId': 'abcd', 'key_name': None, 'metadata': {}, 'password': '******', 'tenantId': '1234', 'updated': '2015-06-19T00:06:58Z', 'uri': 'http://i5r.idp.iu.futuregrid.org/v2/1234/servers/abcd'}, 'id': '67f6bsf67a6b', 'image': None, 'name': 'vm2', 'private_ips': [], 'public_ips': [], 'size': None, 'state': 3 } self.d = FlatDict(self.vm) # noinspection PyPep8Naming def tearDown(self): pass def test_000(self): result = self.run("cm refresh off") print def test_001(self): HEADING("check the model") self.cm.clean() d = self.d banner("VM Data", c='-') pprint(d.__dict__) assert d['extra__imageId'] == 'abcd' banner("Add VM", c='-') name = "vm1" uuid = d.id vm = VM_OPENSTACK(name=name, user="******", category=self.data.cloud, **d) print(vm) banner("VM added", c='-') self.cm.add(vm) banner("Add VM", c='-') name = "vm2" uuid = d.id vm = VM_OPENSTACK(name=name, user="******", category=self.data.cloud, **d) print(vm) banner("VM added", c='-') # pprint(vm.__dict__) # vm.bla = "bla" self.cm.add(vm) # self.cm.save() banner("Get VM from Database", c='-') vms = self.cm.find(kind="vm", name=name, output='dict') pprint(vms) assert len(vms) == 1 assert vms[0]["name"] == name def test_002(self): HEADING("VM DB test") self.cm.clean() print("ADD TO OS ") d = { 'user': "******", 'category': self.data.cloud } for index in range(1, 6): name = "vm_" + str(index).zfill(3) banner(name) print("ADD", name) try: d['name'] = name d['uuid'] = "uuid_" + str(index) vm = VM_OPENSTACK(**d) pprint(vm.__dict__) except Exception as e: Console.error("issue adding vm", traceflag=True) self.cm.add(vm) print("VM added. ok") print("ADD TO LIBCLOUD ") for index in range(6, 11): name = "vm_" + str(index).zfill(3) banner(name) print("ADD", name) d['name'] = name d['uuid'] = "uuid_" + str(index) vm = VM_LIBCLOUD(**d) print("VM", vm.__dict__) self.cm.add(vm) self.cm.save() result = self.run("cm refresh off") print(result) vms = self.cm.find(kind="vm", scope="all", output='dict') pprint(vms) print (len(vms)) assert len(vms) == 10 def test_003(self): HEADING("find vm tables") print("---------") all_tables = self.cm.tables for t in all_tables: print(t.__tablename__, t.__kind__) print(all_tables) assert 'DEFAULT' in str(all_tables) # print ("---------") # vm_tables = self.cm.tables(kind="vm") # for t in vm_tables: # print (t.__tablename__, t.__kind__) # assert len(vm_tables) == 2 def test_004(self): HEADING("find vm tables") print("-------------") vm = self.cm.find(kind="vm", name="vm_001", scope='first') pprint(vm) assert vm.name == 'vm_001' print("-------------") vm = self.cm.find(kind="vm", name="vm_006", scope='first') pprint(vm) assert vm.name == 'vm_006' print("-------------") vms = self.cm.find(kind="vm", scope="all") pprint(vms) print(len(vms)) assert len(vms) == 10
from cmd3.console import Console from cloudmesh_base.Shell import Shell from os.path import expanduser from libcloud.compute.types import Provider from libcloud.compute.providers import get_driver from cloudmesh_client.common.ConfigDict import ConfigDict import libcloud.security from cloudmesh_client.common.ConfigDict import Config from time import sleep from cloudmesh_client.db.CloudmeshDatabase import CloudmeshDatabase from cloudmesh_client.common.hostlist import Parameter cm = CloudmeshDatabase() class Command_vm(object): # TODO DOES NOT INTERFACE WITH DATABASE @classmethod def start(cls, name, count, cloud, image, flavor, group): """ TODO: group has not been used yet. fix that starts a virtual Machine (VM) or a set of VMs :param name: name of the virtual machine :type name: string, None :param count: give the number of servers to start :type count: integer, None :param cloud: give a cloud to work on, if not given, selected or default cloud will be used :type cloud: integer, None
class Var(ListResource): """ Cloudmesh contains the concept of defaults. Defaults can have categories (we will rename cloud to categories). A category can be a cloud name or the name 'general'. The category general is a 'global' name space and contains defaults of global value (in future we will rename the value to global). """ cm = CloudmeshDatabase() """cm is a static variable so that db is used uniformly.""" @classmethod def list(cls, format="table", order=None, output=format): """ lists the default values in the specified format. TODO: This method has a bug as it uses format and output, only one should be used. :param category: the category of the default value. If general is used it is a special category that is used for global values. :param format: json, table, yaml, dict, csv :param order: The order in which the attributes are returned :param output: The output format. :return: """ if order is None: order = ['name', 'value', 'user'] try: d = cls.cm.all("var") return (Printer.dict_printer(d, order=order, output=format)) except: return None # # GENERAL SETTER AND GETTER METHOD # @classmethod def set(cls, key, value, user=None): """ sets the default value for a given category :param key: the dictionary key of the value to store it at. :param value: the value :param user: the username to store this default value at. :return: """ try: o = cls.get_object(key) me = cls.cm.user or user if o is None: o = cls.cm.db_obj_dict('var', name=key, value=value, category="var", user=me) cls.cm.add_obj(o) else: o.value = value cls.cm.add(o) # cls.cm.update(o) cls.cm.save() except: return None @classmethod def get_object(cls, key): """ returns the first object that matches the key in teh Default database. :param key: The dictionary key :param category: The category :return: """ try: arguments = {'name': key} o = cls.cm.find('var', output='object', **arguments).first() return o except Exception: return None @classmethod def get(cls, key): """ returns the value of the first objects matching the key with the given category. :param key: The dictionary key :param category: The category :return: """ arguments = {'name': key} o = cls.cm.find('var', output='dict', scope='first', **arguments) if o is not None: return o['value'] else: return None @classmethod def delete(cls, key): try: o = Var.get_object(key) if o is not None: cls.cm.delete(o) return "Deletion. ok." else: return None except: return None @classmethod def clear(cls): """ deletes all default values in the database. :return: """ try: d = cls.cm.all('var') for item in d: name = d[item]["name"] cls.cm.delete_by_name('var', name) cls.cm.save() except: return None
class Vm(ListResource): cm = CloudmeshDatabase() @classmethod def construct_ip_dict(cls, ip_addr, name="kilo"): 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": print("ec2 ip dict yet to be implemented") TODO.implement() # Handle Azure Specific Output if cloud_details["cm_type"] == "azure": print("azure ip dict yet to be implemented") TODO.implement() except Exception as e: Error.error("error in vm construct dict", traceback=True) @classmethod def isUuid(cls, name): try: UUID(name, version=4) return True except ValueError: return False @classmethod def boot(cls, **kwargs): key_name = kwargs["key_name"] cloud_name = kwargs["cloud"] conf = ConfigDict("cloudmesh.yaml") username = conf["cloudmesh"]["profile"]["username"] keycloudmap = cls.cm.get_key_cloud_mapping(username, key_name, cloud_name) if keycloudmap is None or len(keycloudmap) == 0: raise RuntimeError("No key cloud mapping found for user {:}, key name {:} and cloud {:} in database." .format(username, key_name, cloud_name)) # print("Keycloudmap = {:}".format(keycloudmap)) key_name_on_cloud = keycloudmap["key_name_on_cloud"] # print("Booting with key_name_on_cloud as " + key_name_on_cloud) cloud_provider = CloudProvider(cloud_name).provider if "nics" in kwargs: vm = cloud_provider.boot_vm(kwargs["name"], kwargs["image"], kwargs["flavor"], key=key_name_on_cloud, secgroup=kwargs["secgroup_list"], nics=kwargs["nics"]) else: vm = cloud_provider.boot_vm(kwargs["name"], kwargs["image"], kwargs["flavor"], key=key_name_on_cloud, secgroup=kwargs["secgroup_list"], nics=None) print("Machine {:} is being booted on {:} Cloud...".format(kwargs["name"], cloud_provider.cloud)) return vm @classmethod def start(cls, **kwargs): cloud_provider = CloudProvider(kwargs["cloud"]).provider for server in kwargs["servers"]: cloud_provider.start_vm(server) print("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"]) @classmethod def stop(cls, **kwargs): cloud_provider = CloudProvider(kwargs["cloud"]).provider for server in kwargs["servers"]: cloud_provider.stop_vm(server) print("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"]) @classmethod def delete(cls, **kwargs): cloud_provider = CloudProvider(kwargs["cloud"]).provider for server in kwargs["servers"]: cloud_provider.delete_vm(server) print("Machine {:} is being deleted on {:} Cloud...".format(server, cloud_provider.cloud)) # Explicit refresh called after VM delete, to update db. cls.refresh(cloud=kwargs["cloud"]) @classmethod def rename(cls, **kwargs): cloud_provider = CloudProvider(kwargs["cloud"]).provider new_name = kwargs["new_name"] for server in kwargs["servers"]: cloud_provider.rename_vm(server, new_name) print("Machine {:} renamed to {:} on {:} Cloud...".format(server, new_name, cloud_provider.cloud)) # Explicit refresh called after VM delete, to update db. cls.refresh(cloud=kwargs["cloud"]) @classmethod def info(cls, **kwargs): raise NotImplementedError() @classmethod def list(cls, **kwargs): """ This method lists all VMs of the cloud """ try: if "name_or_id" in kwargs and kwargs["name_or_id"] is not None: if cls.isUuid(kwargs["name_or_id"]): elements = cls.cm.find("vm", category=kwargs["cloud"], uuid=kwargs["name_or_id"]) else: elements = cls.cm.find("vm", category=kwargs["cloud"], label=kwargs["name_or_id"]) else: elements = cls.cm.find("vm", category=kwargs["cloud"]) # print(elements) # order = ['id', 'uuid', 'name', 'cloud'] (order, header) = CloudProvider(kwargs["cloud"]).get_attributes("vm") # order = None if "name_or_id" in kwargs and kwargs["name_or_id"] is not None: return attribute_printer(list(elements.values())[0], output=kwargs["output_format"]) else: return dict_printer(elements, order=order, output=kwargs["output_format"]) except Exception as ex: Console.error(ex.message, ex) @classmethod def clear(cls, **kwargs): raise NotImplementedError() @classmethod def refresh(cls, **kwargs): # print("Inside refresh") return cls.cm.refresh("vm", kwargs["cloud"]) @classmethod def status_from_cloud(cls, **kwargs): cloud_provider = CloudProvider(kwargs["cloud"]).provider vm = cloud_provider.get_vm(name=kwargs["name_or_id"]) return vm["status"] @classmethod def set_vm_login_user(cls, name_or_id, cloud, username): if cls.isUuid(name_or_id): uuid = name_or_id else: vm_data = cls.cm.find("vm", category=cloud, label=name_or_id) if vm_data is None or len(vm_data) == 0: raise RuntimeError("VM with label {} not found in database.".format(name_or_id)) uuid = list(vm_data.values())[0]["uuid"] user_map_entry = cls.cm.find("VMUSERMAP", vm_uuid=uuid) if user_map_entry is None or len(user_map_entry) == 0: user_map_dict = cls.cm.db_obj_dict("VMUSERMAP", vm_uuid=uuid, username=username) cls.cm.add_obj(user_map_dict) cls.cm.save() else: cls.cm.update_vm_username(vm_uuid=uuid, username=username) @classmethod def get_vm_login_user(cls, name_or_id, cloud): if cls.isUuid(name_or_id): uuid = name_or_id else: vm_data = cls.cm.find("vm", category=cloud, label=name_or_id) if vm_data is None or len(vm_data) == 0: raise RuntimeError("VM with label {} not found in database.".format(name_or_id)) 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"] @classmethod def get_last_vm(cls, cloud): vm_data = cls.cm.find("vm", scope="first", category=cloud) if vm_data is None or len(vm_data) == 0: raise RuntimeError("VM data not found in database.") return vm_data
def start(cls, name, count, cloud, image, flavor, group): """ TODO: group has not been used yet. fix that starts a virtual Machine (VM) or a set of VMs :param name: name of the virtual machine :type name: string, None :param count: give the number of servers to start :type count: integer, None :param cloud: give a cloud to work on, if not given, selected or default cloud will be used :type cloud: integer, None :param image: image name :type image: string, None :param flavor:flavor name. m1.medium, for example :type flavor: string, None :param group: the group name of server :type group: string, None :return: """ # TODO: vm start (without arguments) use default cloud, image, flavor, group. if cloud is None: # use default values for cloud, image and flavor pass config = CloudRegister.get(cloud) if cm_type in ["openstack"]: provider = Provider.OPENSTACK OpenStack = get_driver(provider) try: cloud_credentials = config['credentials'] except Exception as e: Console.error(e.message) return # TODO: THIS MAY BE JUST TRUE IF THE CERT PATH EXISTS IN CREDENTIAL # set path to cacert and enable ssl connection libcloud.security.CA_CERTS_PATH = [Config.path_expand(cloud_credentials['OS_CACERT'])] libcloud.security.VERIFY_SSL_CERT = True auth_url = "%s/tokens/" % cloud_credentials['OS_AUTH_URL'] driver = OpenStack(cloud_credentials['OS_USERNAME'], cloud_credentials['OS_PASSWORD'], ex_force_auth_url=auth_url, ex_tenant_name=cloud_credentials['OS_TENANT_NAME'], ex_force_auth_version='2.0_password', ex_force_service_region='regionOne') # obtain available images # type of images: <class 'libcloud.compute.base.NodeImage'> images = driver.list() if not [i for i in images if i.name == image]: Console.error("Image {:} not found".format(image)) return image = [i for i in images if i.name == image][0] # sizes/flavors sizes = driver.list_sizes() if not [i for i in sizes if i.name == flavor]: Console.error("Flavor {:} not found".format(flavor)) return size = [i for i in sizes if i.name == flavor][0] if count is None: count = 1 count = int(count) def __findsufix(): # TODO: THIS IS A BIG BUG AS THE NEXT VM NAME IS NOT MANAGED BY SUFFIX """ Virtual machine name (VM) format: string-001, string-002, ..., string-n returns the max sufix from the VM list. It will be used in the new vm name in order to avoid VMs with the same name. :return: max sufix :return type: string """ nodes = driver.list_nodes() sufix = 1 for i in nodes: n = 0 try: n = int(i.name.split('-', 1)[1]) # not always is int(i.name.split('-', 1)[1] a digit except: pass if sufix <= n: sufix = n + 1 sufix = str(sufix).zfill(3) return sufix # set vm name sufix = __findsufix() c = CloudmeshDatabase() if name is None: c.name(cloud_credentials['OS_USERNAME'] + "-" + sufix) else: c.name(name + "-" + sufix) # launch a new VM Console.ok("Booting Virtual Machine...") for i in range(0, count): name = c.get_name() try: node = driver.create_node(name=name, image=image, size=size) except Exception as e: Console.error("{:} virtual machines have not been created. {:}".format(count - i, e.message)) return c.name(c.next_name()) # wait the node to be ready before assigning public IP sleep(10) Console.ok("Virtual Machine created") else: Console.error('cloud {:} not found'.format(cloud))
class SecGroup(ListResource): cm_db = CloudmeshDatabase( ) # Instance to communicate with the cloudmesh database @classmethod def convert_list_to_dict(cls, os_result): d = {} for i, obj in enumerate(os_result): d[i] = {} d[i]["Id"] = obj.id d[i]["Name"] = obj.name d[i]["Description"] = obj.description return d # noinspection PyPep8 @classmethod def convert_rules_to_dict(cls, os_result): d = {} for i, obj in enumerate(os_result): d[i] = {} d[i]["IP Protocol"] = obj["ip_protocol"] d[i]["From Port"] = obj["from_port"] d[i]["To Port"] = obj["to_port"] if obj["ip_range"]["cidr"]: ip_range = obj["ip_range"]["cidr"] else: ip_range = "0.0.0.0/0" d[i]["IP Range"] = ip_range return d @classmethod def refresh(cls, cloud): """ This method would refresh the secgroup list by first clearing the database, then inserting new data :param cloud: the cloud name """ return cls.cm_db.refresh('secgroup', cloud) # noinspection PyPep8Naming @classmethod def remove_subjectAltName_warning(cls, content): result = [] for line in content.split("\n"): if "Certificate has no `subjectAltName`" in line: pass elif "SecurityWarning" in line: pass else: result.append(line) return "\n".join(result) @classmethod def create(cls, label, cloud=None): """ Method creates a new security group in database & returns the uuid of the created group :param label: :param cloud: :param tenant: :return: """ # Create the security group in given cloud try: cloud_provider = CloudProvider(cloud).provider secgroup = cloud_provider.create_secgroup(label) if secgroup: uuid = secgroup.id return uuid else: print("Failed to create security group, {}".format(secgroup)) except Exception as e: print("Exception creating security group in cloud, {}".format(e)) return None @classmethod def list(cls, cloud="general", format="table"): """ This method queries the database to fetch list of secgroups filtered by cloud. :param cloud: :return: """ try: elements = cls.cm_db.find("secgroup", category=cloud) #pprint(elements) (order, header) = CloudProvider(cloud).get_attributes("secgroup") return dict_printer(elements, order=order, header=header, output=format) except Exception as ex: Console.error(ex.message, ex) @classmethod def enable_ssh(cls, secgroup_name='default', cloud="general"): ret = False cloud_provider = CloudProvider(cloud).provider.provider secgroups = cloud_provider.security_groups.list() for asecgroup in secgroups: if asecgroup.name == secgroup_name: rules = asecgroup.rules rule_exists = False # structure of a secgroup rule: # {u'from_port': 22, u'group': {}, u'ip_protocol': u'tcp', u'to_port': 22, u'parent_group_id': u'UUIDHERE', u'ip_range': {u'cidr': u'0.0.0.0/0'}, u'id': u'UUIDHERE'} for arule in rules: if arule["from_port"] == 22 and \ arule["to_port"] == 22 and \ arule["ip_protocol"] == 'tcp' and \ arule["ip_range"] == {'cidr': '0.0.0.0/0'}: # print (arule["id"]) rule_exists = True break if not rule_exists: cloud_provider.security_group_rules.create( asecgroup.id, ip_protocol='tcp', from_port=22, to_port=22, cidr='0.0.0.0/0') # else: # print ("The rule allowing ssh login did exist!") ret = True break # print ("*" * 80) # d = SecGroup.convert_list_to_dict(secgroups) # print (d) return ret @classmethod def get(cls, name, cloud="general"): """ This method queries the database to fetch secgroup with given name filtered by cloud. :param name: :param cloud: :return: """ try: args = { "name": name, "category": cloud, } secgroup = cls.cm_db.find("secgroup", output="object", **args).first() return secgroup except Exception as ex: Console.error(ex.message, ex) @classmethod def add_rule(cls, cloud, secgroup, from_port, to_port, protocol, cidr): try: # Get the nova client object cloud_provider = CloudProvider(cloud).provider # Create add secgroup rules to the cloud args = { 'uuid': secgroup.uuid, 'protocol': protocol, 'from_port': from_port, 'to_port': to_port, 'cidr': cidr } rule_id = cloud_provider.add_secgroup_rule(**args) # create local db record ruleObj = cls.cm_db.db_obj_dict("secgrouprule", uuid=str(rule_id), name=secgroup.name, groupid=secgroup.uuid, category=secgroup.category, user=secgroup.user, project=secgroup.project, fromPort=from_port, toPort=to_port, protocol=protocol, cidr=cidr) cls.cm_db.add_obj(ruleObj) cls.cm_db.save() Console.ok( "Added rule [{} | {} | {} | {}] to secgroup [{}]".format( from_port, to_port, protocol, cidr, secgroup.name)) except Exception as ex: if "This rule already exists" in ex.message: Console.ok("Rule already exists. Added rule.") return else: Console.error(ex.message, ex) return @classmethod def get_rules(cls, uuid): """ This method gets the security group rule from the cloudmesh database :param uuid: :return: """ try: args = {"groupid": uuid} rule = cls.cm_db.find("secgrouprule", **args) # check if rules exist if rule is None: return "No rules for security group [{}] in the database. Try cm secgroup refresh." # return table return (dict_printer(rule, order=[ "user", "category", "name", "fromPort", "toPort", "protocol", "cidr" ], output="table")) except Exception as ex: Console.error(ex.message, ex) return None @classmethod def delete_secgroup(cls, label, cloud): try: # Find the secgroup from the cloud cloud_provider = CloudProvider(cloud).provider result = cloud_provider.delete_secgroup(label) return result except Exception as ex: Console.error(ex.message, ex) @classmethod def delete_rule(cls, cloud, secgroup, from_port, to_port, protocol, cidr): try: args = { "groupid": secgroup.uuid, "fromPort": from_port, "toPort": to_port, "protocol": protocol, "cidr": cidr } rule = cls.cm_db.find("secgrouprule", output="object", **args).first() if rule is not None: # get the nova client for cloud cloud_provider = CloudProvider(cloud).provider # delete the rule from the cloud cloud_provider.delete_secgroup_rule(rule.uuid) # delete the local db record cls.cm_db.delete(rule) return "Rule [{} | {} | {} | {}] deleted" \ .format(from_port, to_port, protocol, cidr) else: return None except Exception as ex: Console.error(ex.message, ex) return @classmethod def delete_all_rules(cls, secgroup): try: args = {"groupid": secgroup.uuid} rules = cls.cm_db.find("secgrouprule", output="object", **args) if rules is not None: for rule in rules: cls.cm_db.delete(rule) Console.ok("Rule [{} | {} | {} | {}] deleted".format( rule.fromPort, rule.toPort, rule.protocol, rule.cidr)) else: pass except Exception as ex: Console.error(ex.message, ex) return @classmethod def toDict(cls, item): """ Method converts the item to a dict :param item: :return: """ # Convert to dict & print table d = {} # If list, iterate to form dict if isinstance(item, list): for element in item: d[element.id] = {} for key in element.__dict__.keys(): if not key.startswith("_sa"): d[element.id][key] = str(element.__dict__[key]) # Form dict without iterating else: d[item.id] = {} for key in item.__dict__.keys(): if not key.startswith("_sa"): d[item.id][key] = str(item.__dict__[key]) # return the dict return d
class List(object): cm = CloudmeshDatabase() # Instance to communicate with the cloudmesh database @classmethod def list(cls, kind, cloud, user=None, tenant=None, order=None, header=None, output="table"): """ Method lists the data in the db for given cloud and of given kind :param kind: :param cloud: :param tenant: :param user: :param order: :param header: :param output: :return: """ try: # get the model object table = cls.cm.get_table(kind) filter = {} if cloud is not None: filter["category"] = cloud if user is not None: filter["user"] = user if tenant is not None: filter["tenant"] = tenant elements = cls.cm.find(table, **filter) if elements is not None or elements is not {}: # convert the output to a dict return (dict_printer(elements, order=order, header=header, output=output)) else: return None except Exception as ex: Console.error(ex.message, ex) # # TODO: don't we have not already a conversion method # @classmethod def toDict(cls, item): """ Method converts the item to a dict :param item: :return: """ # Convert to dict & print table d = {} # If list, iterate to form dict if isinstance(item, list): for element in item: d[element.id] = {} for key in list(element.__dict__.keys()): if not key.startswith("_sa"): d[element.id][key] = str(element.__dict__[key]) # Form dict without iterating else: d[item.id] = {} for key in list(item.__dict__.keys()): if not key.startswith("_sa"): d[item.id][key] = str(item.__dict__[key]) # return the dict return d
class Mesh(object): """ Design decision. Bellow we like to implement actually a type detection that based on one specifying a single string or a list does the right things I am not yet sure how to reflect this in the :type: but we can probably sat str or list of str example: delete("myvm1") delete("myvm2") delete("myvm1", "myvm2") delete(["myvm1", "myvm2"]) are doing all the same thing the way we do this is we implement for lists and than mak the other call recursively to a list We start simply with a print msg """ t_flavour = "flavour" t_image = "image" t_vm = "vm" def __init__(self): self.db = CloudmeshDatabase() @classmethod def clouds(self, format="json", order=None): filename = "cloudmesh.yaml" config = ConfigDict(filename) yaml_clouds = dict(config["cloudmesh"]["clouds"]) return dict_printer(yaml_clouds, output=format, order=order) @classmethod def verify(self): print("verify") @classmethod def refresh(self): print("refresh") @classmethod def register(self): print("register") self.db.update(["vm", "image", "flavor"], ["india", "aws", "azure"]) """ @classmethod def key_add(self, *args): print ("key add") sshm = SSHKeyManager() sshdb = SSHKeyDBManager() print len(args) if len(args) == 1: if os.path.isdir(path_expand(args[0])): sshm.get_from_dir(args[0]) print sshm for key in sshm.__keys__: print key print sshm.__keys__[key]['comment'] sshdb.add_from_sshkey(sshm.__keys__[key], key) else: # args is the github username sshm.get_from_git(args[0]) print args[0] for key in sshm.__keys__: sshdb.add_from_sshkey(sshm.__keys__[key], key) elif len(args) == 2: keyname = args[0] path = path_expand(args[1]) sshdb.add(path, keyname) def key_delete(self, keynames): sshdb = SSHKeyDBManager() names = keynames.split(',') for name in names: sshdb.delete(name) """ @classmethod def clear(self): print("clear") self.db.delete_all(["VM", "FLAVOR", "IMAGE", "DFAULT"]) @classmethod def dump(self, filename): """ :param filename: name of the db file that will receive the content of cloudmesh.db :return: """ from_file = path_expand("~/.cloudmesh/cloudmesh.db") to_file = path_expand("~/.cloudmesh/{}".format(filename)) shutil.copyfile(from_file, to_file) print("dump") @classmethod def load(self, filename): """ :param filename: name of the db file located on ./cloudmesh that will be copied do cloudmesh.db :return: """ print("load") from_file = path_expand("~/.cloudmesh/{}".format(filename)) to_file = path_expand("~/.cloudmesh/cloudmesh.db") shutil.copyfile(from_file, to_file) @classmethod def vms(self, clouds): if isinstance(clouds, str): return self.vms(kind, [clouds], output) for cloud in clouds: print("get vm ids from ", cloud) @classmethod def list(self, kind, clouds, output="table"): """ Lists the IaaS objects such as flavors, images, vms in the specified output format. :param kind: flavor, image, vm :type kind: str :param clouds: list of clouds we list :type clouds: list of str :param output: json, yaml, table :type output: str :return: """ if isinstance(clouds, str): return self.list(kind, [clouds], output) for cloud in clouds: if kind in ["f", "flavour"]: print("flavor", cloud) elif kind in ["v", "vm"]: print("vm", cloud) elif kind in ["i", "image"]: print("image", cloud) else: print("kind", kind, "not supported") return "done" @classmethod def boot(self, cloud=None, image=None, flavor=None, key=None, arguments=None): """ Boots the image on a specified cloud :param image: The name of the image :type image: str :param flavor: The name of the flavor :type flavor: str :param key: The name of the key :type key: str :param cloud: The name of the cloud :type cloud: str :param arguments: An array of arguments :type arguments: list of str :return: the id of the vm :rtype: str """ if cloud is None: cloud = Default.get("cloud", "general") print("get default cloud: " + str(cloud)) if image is None: image = Default.get("image", cloud) print("get default image ", str(image)) if flavor is None: flavor = Default.get("flavor", cloud) print("get default flavor ", str(flavor)) if key is None: key = Default.get("key", str(cloud)) print("get default key ", str(key)) # command_key print("boot an image", image, flavor, key, cloud, arguments) pass @classmethod def delete(self, ids): """ delete the vms Example: delete("gregor-[001-010]") :param ids: host ids specified in hostlist format :return: """ names = Parameter.expand(ids) for name in names: print("delete", name) @classmethod def _generate_name(self, prefix, number, padding): """ method to create a vm name from a prefix, with given padding of 0 :param prefix: the prefix of the name :type prefix: str :param number: the number :type number: str or int :param padding: the number of 0 that are used for padding :type padding: str or int :return: """ format_string = prefix + "-{0:0" + str(padding) + "}" return format_string.format(number) @classmethod def _parse_name(self, name): """ returns the prefix, the number and the padding length :param name: the name to parse :type name: str :return: prefix, number, padding length :rtype: str, int, int """ prefix, number = name.rsplit("-", 1) n = int(number) padding = len(number) return prefix, n, padding @classmethod def next_name(self, name): """ generates the next name. Example: next_name("gregor-0001") gregor-0002 :param name: the name :type name: str :return: the new name :rtype: str """ prefix, n, padding = self._parse_name(name) n += 1 return self._generate_name(prefix, n, padding) @classmethod def get_name(self): """gets the next name of a vm while increasing its index.""" # the name format is written to the database based on a set_name # the database will contain an index that stores the current index. # if no name hase bee defined and this is accessed first the name # username-0001 will be used where username is your username return "notimplemented-0001" @classmethod def set_name(self, name): """ :param name: :return: """ """sets the name of a vm. The name is ended by a simple number. The number of 0 in it wil be used for padding. set_name("gregor-001") This information will be used when getting the next index. If the next index exceeds the padding we simply just increase the number. """ prefix, n, padding = self.parse_name(name)
class Network(ListResource): cm = CloudmeshDatabase() @classmethod def get_fixed_ip(cls, cloudname, fixed_ip_addr, output='table'): """ Method retrieves fixed ip info :param cloudname: :param fixed_ip_addr: :return: fixed_ip_info """ try: cloud_provider = CloudProvider(cloudname).provider result = cloud_provider.get_fixed_ip(fixed_ip_addr=fixed_ip_addr) return Printer.attribute(result, header=["name", "value"], output=output) except Exception as ex: Console.error(ex.message) return @classmethod def get_floating_ip(cls, cloudname, floating_ip_or_id, output='table'): """ Method to get floating ip info :param cloudname: :param floating_ip_or_id: :return: floating ip info """ try: cloud_provider = CloudProvider(cloudname).provider result = None # check if argument is ip or uuid if cls.isIPAddr(ip_or_id=floating_ip_or_id): # get floating ip list floating_ips = cls.get_floating_ip_list(cloudname) for floating_ip in list(floating_ips): ip_addr = floating_ip["ip"] # if argument ip matches floating ip addr if ip_addr == floating_ip_or_id: result = floating_ip break else: # find by floating ip uuid result = cloud_provider.get_floating_ip( floating_ip_id=floating_ip_or_id) # Could not find floating IP from given args if result is None: return None instance_id = result["instance_id"] instance_name = None if instance_id is not None: # lookup instance_name from id instance_name = cls.find_instance_name(cloudname=cloudname, instance_id=instance_id) # add instance_name to dict result["instance_name"] = instance_name # add misc details to response result["cloud"] = cloudname result["user"] = cloud_provider.cloud_details["credentials"][ "OS_USERNAME"] result["project"] = cloud_provider.cloud_details["credentials"][ "OS_TENANT_NAME"] return Printer.attribute(result, header=["name", "value"], output=output) except Exception: # auto detect floating-ip-id floating_ips = cls.get_floating_ip_list(cloudname) # for each floating-ip from list for floating_ip in list(floating_ips): if floating_ip["id"].startswith(floating_ip_or_id) or \ floating_ip["ip"].startswith(floating_ip_or_id): # confirm choice with user print("Did you mean floating-ip [{}] ? (y/n)".format( floating_ip["ip"])) choice = input().lower() # if yes, return dict if choice == 'y': return Printer.attribute(floating_ip, header=["name", "value"], output=output) # Console.error(ex.message) return @classmethod def reserve_fixed_ip(cls, cloudname, fixed_ip_addr): """ Reserve a fixed ip address :param cloudname: :param fixed_ip_addr: :return: """ try: cloud_provider = CloudProvider(cloudname).provider cloud_provider.reserve_fixed_ip(fixed_ip_addr=fixed_ip_addr) return "Success." except Exception as ex: Console.error(ex.message) return @classmethod def unreserve_fixed_ip(cls, cloudname, fixed_ip_addr): """ Unreserve a fixed ip address :param cloudname: :param fixed_ip_addr: :return: """ try: cloud_provider = CloudProvider(cloudname).provider cloud_provider.unreserve_fixed_ip(fixed_ip_addr=fixed_ip_addr) return "Success." except Exception as ex: Console.error(ex.message) return ex @classmethod def associate_floating_ip(cls, cloudname, instance_name, floating_ip): """ Method to associate floating ip to an instance :param cloudname: :param instance_name: :param floating_ip: :return: """ try: cloud_provider = CloudProvider(cloudname).provider # Find the server instance server = cloud_provider.provider.servers.find(name=instance_name) # Add the floating ip to the instance server.add_floating_ip(floating_ip) return "Success." except Exception as ex: if "already has a floating" in ex.message: Console.error("VM has already floating ip", traceflag=False) else: Console.error(ex.message) return ex pass @classmethod def disassociate_floating_ip(cls, cloudname, instance_name, floating_ip): """ Disassociates a floating ip from an instance :param cloudname: :param instance_name: :param floating_ip: :return: """ try: cloud_provider = CloudProvider(cloudname).provider # Find the server instance server = cloud_provider.provider.servers.find(name=instance_name) # Remove the floating ip from the instance server.remove_floating_ip(floating_ip) # Release the floating ip to the pool cls.delete_floating_ip(cloudname=cloudname, floating_ip_or_id=floating_ip) return "Success." except Exception as ex: Console.error(ex.message) return ex pass @classmethod def create_assign_floating_ip(cls, cloudname, instance_name): """ Method to create a new floating-ip and associate it with the instance :param cloudname: cloud :param instance_name: name of instance :return: floating_ip """ try: cloud_provider = CloudProvider(cloudname).provider floating_ip = cloud_provider.create_assign_floating_ip( instance_name) return floating_ip except Exception as ex: Console.error(ex.message) return @classmethod def create_floating_ip(cls, cloudname, floating_pool=None): """ Method to create a floating ip address under a pool :param cloudname: :param floating_pool: :return: floating ip addr """ try: cloud_provider = CloudProvider(cloudname).provider # If floating pool is not given, # get first from list if floating_pool is None: floating_pool = cloud_provider.provider.floating_ip_pools.list( )[0].name Console.ok( "Floating pool not provided, selected [{}] as the pool.". format(floating_pool)) floating_ip = cloud_provider.create_floating_ip( float_pool=floating_pool) return floating_ip except Exception as ex: Console.error(ex.message) return @classmethod def delete_floating_ip(cls, cloudname, floating_ip_or_id): """ Method to delete a floating ip address :param cloudname: :param floating_ip_or_id: :return: """ try: cloud_provider = CloudProvider(cloudname).provider floating_ip_dict = None # check if argument is ip or uuid if cls.isIPAddr(ip_or_id=floating_ip_or_id): # get floating ip list floating_ips = cls.get_floating_ip_list(cloudname) for floating_ip in list(floating_ips): ip_addr = floating_ip["ip"] # if argument ip matches floating ip addr if ip_addr == floating_ip_or_id: floating_ip_dict = floating_ip break else: # find by floating ip uuid floating_ip_dict = cloud_provider.get_floating_ip( floating_ip_id=floating_ip_or_id) # Could not find floating IP from given args if floating_ip_dict is None: return None # Delete the floating ip; returns None if success result = cloud_provider.delete_floating_ip(floating_ip_dict["id"]) if not result: return "Floating IP [{}] deleted successfully!" \ .format(floating_ip_dict["ip"]) except Exception as ex: Console.error(ex.message) return @classmethod def list_floating_ip(cls, cloudname, output='table'): """ Method to list floating ips :param cloudname: :return: floating ip list """ try: floating_ips = cls.get_floating_ip_list(cloudname) for floating_ip in list(floating_ips.values()): # Get instance_id associated with instance instance_id = floating_ip["instance_id"] if instance_id is not None: try: instance_name = cls.find_instance_name( cloudname=cloudname, instance_id=instance_id) # Assign it to the dict floating_ip["instance_name"] = instance_name floating_ip["cloud"] = cloudname except Exception as ex: Console.error(ex.message) continue else: # If no instance associated, keep None floating_ip["instance_name"] = None (order, header) = CloudProvider(cloudname).get_attributes("floating_ip") return Printer.write(floating_ips, order=order, header=header, output=output) except Exception as ex: Console.error(ex.message) return @classmethod def list_unused_floating_ip(cls, cloudname, output='table'): """ Method to list unused floating ips These floating ips are not associated with any instance :param cloudname: :return: floating ip list """ try: # fetch unused floating ips floating_ips = cls.get_unused_floating_ip_list(cloudname) # print the output return Printer.write(floating_ips, order=["ip", "pool", "id", "cloud"], header=[ "floating_ip", "floating_ip_pool", "floating_ip_id", "cloud" ], output=output) except Exception as ex: Console.error(ex.message) return @classmethod def list_floating_ip_pool(cls, cloudname): """ Method to list floating ip pool :param cloudname: :return: """ try: cloud_provider = CloudProvider(cloudname).provider floating_ip_pools = cloud_provider.list_floating_ip_pools() (order, header ) = CloudProvider(cloudname).get_attributes("floating_ip_pool") return Printer.write(floating_ip_pools, order=order, header=header) except Exception as ex: Console.error(ex.message) pass @classmethod def isIPAddr(cls, ip_or_id): """ Method to check if argument is IP address or notS :param ip_or_id: :return: """ try: socket.inet_aton(ip_or_id) return True except: return False @classmethod def get_unused_floating_ip_list(cls, cloudname): """ Method to get the unused floating IP list :param cloudname: :return: floating_ips """ try: cloud_provider = CloudProvider(cloudname).provider floating_ips = cloud_provider.list_floating_ips() unused_floating_ips = list() for floating_ip in list(floating_ips.values()): if floating_ip["fixed_ip"] is None and \ floating_ip["instance_id"] is None: # add to unused list floating_ip["cloud"] = cloudname unused_floating_ips.append(floating_ip) return unused_floating_ips except Exception as ex: Console.error(ex.message) @classmethod def get_floating_ip_list(cls, cloudname): """ Method to get the floating IP list :param cloudname: :return: floating_ips """ try: cloud_provider = CloudProvider(cloudname).provider floating_ips = cloud_provider.list_floating_ips() return floating_ips except Exception as ex: Console.error(ex.message) @classmethod def find_instance_name(cls, **kwargs): """ Method to find instance name :param kwargs: :return: instance_name """ cloudname = kwargs["cloudname"] instance_id = kwargs["instance_id"] # Cloudmesh database instance # Lookup instance details from db instance_dict = cls.cm.find(kind="vm", category=cloudname, uuid=instance_id) # Get instance_name for vm if len(instance_dict) > 0: instance_name = list(instance_dict)[0]["name"] return instance_name @classmethod def get_instance_dict(cls, **kwargs): """ Method to get instance dict :param kwargs: :return: instance dict """ cloudname = kwargs["cloudname"] instance_id = kwargs["instance_id"] # Cloudmesh database instance # Lookup instance details from db if cls.isUuid(instance_id): instance_dict = cls.cm.find(kind="vm", category=cloudname, uuid=instance_id) else: instance_dict = cls.cm.find(kind="vm", category=cloudname, name=instance_id) # Instance not found in DB if cls.isDictEmpty(instance_dict): # auto detect instance_id feature vms = cls.cm.find(kind="vm", category=cloudname) # check for each instance in db for vm in list(vms): # if match found in either name/id if vm["uuid"].startswith(instance_id) or \ vm["name"].startswith(instance_id): # confirm choice with user print("Did you mean instance [{}] ? (y/n)".format( vm["name"])) choice = input().lower() # if yes, return dict if choice == 'y': return vm return None else: return list(instance_dict)[0] @classmethod def find_assign_floating_ip(cls, cloudname, instance_id): instance_dict = cls.get_instance_dict(cloudname=cloudname, instance_id=instance_id) # Instance not found if instance_dict is None: Console.error( "Instance [{}] not found in the cloudmesh database!".format( instance_id)) return None instance_name = instance_dict["name"] # Get an unused ip from pool if exist and associate unused_floating_ips = cls.get_unused_floating_ip_list( cloudname=cloudname) if unused_floating_ips: floating_ip = unused_floating_ips[0]["ip"] result = cls.assign_floating_ip(cloudname=cloudname, instance_id=instance_id, floating_ip=floating_ip) if result is None: Console.error("IP {} could not be assigned to VM {} ".format( floating_ip, instance_name)) return None else: # create a new ip and associate floating_ip = cls.create_assign_floating_ip( cloudname=cloudname, instance_name=instance_name) return floating_ip @classmethod def assign_floating_ip(cls, cloudname, instance_id, floating_ip): # find instance in db instance_dict = cls.get_instance_dict(cloudname=cloudname, instance_id=instance_id) # Instance not found if instance_dict is None: Console.error( "Instance [{}] not found in the cloudmesh database!".format( instance_id)) return None instance_name = instance_dict["name"] result = cls.associate_floating_ip(cloudname=cloudname, instance_name=instance_name, floating_ip=floating_ip) return result @classmethod def isUuid(cls, argument): """ Method to check if arg is an UUID :param argument: :return: """ try: UUID(argument, version=4) return True except ValueError: return False @classmethod def isDictEmpty(cls, dictionary): """ Method to test empty Dict :param dictionary: :return: """ if bool(dictionary): return False else: return True
class Default(ListResource): """ Cloudmesh contains the concept of defaults. Defaults can have categories (we will rename cloud to categories). A category can be a cloud name or the name 'general'. The category general is a 'global' name space and contains defaults of global value (in future we will rename the value to global). """ cm = CloudmeshDatabase() """cm is a static variable so that db is used uniformly.""" @classmethod def list(cls, category=None, format="table", order=None, output=format): """ lists the default values in the specified format. TODO: This method has a bug as it uses format and output, only one should be used. :param category: the category of the default value. If general is used it is a special category that is used for global values. :param format: json, table, yaml, dict, csv :param order: The order in which the attributes are returned :param output: The output format. :return: """ if order is None: order = ['user', 'category', 'name', 'value'] try: if category is None: d = cls.cm.all("default") else: d = cls.cm.find('default', category=category) return (Printer.dict_printer(d, order=order, output=format)) except: return None # # GENERAL SETTER AND GETTER METHOD # @classmethod def set(cls, key, value, category=None, user=None): """ sets the default value for a given category :param key: the dictionary key of the value to store it at. :param value: the value :param category: the name of the category :param user: the username to store this default value at. :return: """ try: o = Default.get_object(key, category) me = cls.cm.user or user if o is None: o = cls.cm.db_obj_dict('default', name=key, value=value, category=category, user=me) cls.cm.add_obj(o) else: o.value = value cls.cm.add(o) # cls.cm.update(o) cls.cm.save() except: return None @classmethod def get_object(cls, key, category="general"): """ returns the first object that matches the key in teh Default database. :param key: The dictionary key :param category: The category :return: """ try: arguments = {'name': key, 'category': category} o = cls.cm.find('default', output='object', **arguments).first() return o except Exception: return None @classmethod def get(cls, key, category="general"): """ returns the value of the first objects matching the key with the given category. :param key: The dictionary key :param category: The category :return: """ arguments = {'name': key, 'category': category} o = cls.cm.find('default', output='dict', scope='first', **arguments) if o is not None: return o['value'] else: return None @classmethod def delete(cls, key, category): # # TODO: this is wrong implemented, # try: o = Default.get_object(key, category) if o is not None: cls.cm.delete(o) return "Deletion. ok." else: return None except: return None @classmethod def clear(cls): """ deletes all default values in the database. :return: """ try: d = cls.cm.all('default') for item in d: name = d[item]["name"] cls.cm.delete_by_name('default', name) cls.cm.save() except: return None # # Set the default category # @classmethod def get_cloud(cls): """ returns the cloud in teh category general :return: """ o = cls.get("cloud", category="general") return o @classmethod def set_cloud(cls, value): """ sets the cloud in the category general :param value: the cloud as defined in cloudmesh.yaml :return: """ cls.set("cloud", value, category="general") # # Set the default image # @classmethod def set_image(cls, value, category): """ sets the default image for a specific category. :param value: the image uuid or name :param category: the category :return: """ cls.set("image", value, category=category) @classmethod def get_image(cls, category): """ returns the image for a particular category :param category: the category :return: """ return cls.get("image", category) # # Set the default flavor # @classmethod def set_flavor(cls, value, category): """ sets the default flavor for a particular category :param value: teh flavor name or uuid :param category: the category :return: """ cls.set("flavor", value, category=category) @classmethod def get_flavor(cls, category): """ gets ths flavor default for a category :param category: the category :return: """ return cls.get("flavor", category) # # Set the default group # @classmethod def set_group(cls, value): """ sets the default group :param value: the group name :return: """ cls.set("group", value, category="general") @classmethod def get_group(cls): """ get the default group :return: """ return cls.get("group", "general") # # Set the default key # @classmethod def set_key(cls, name): """ :param name: the key name :return: """ cls.set("key", name, category="general") @classmethod def get_key(cls): """ get the default key name :return: """ return cls.get("key", "general") # # Set the default cluster # @classmethod def set_cluster(cls, value): """ sets the default cluster :param value: the cluster name as defined in the cloudmesh yaml file. :return: """ cls.set("cluster", value, category="general") @classmethod def get_cluster(cls): """ gets the default cluster name. :return: """ return cls.get("cluster", "general") # # Set the default key # @classmethod def set_debug(cls, value): """ enables debugging :param value: True/False :return: """ cls.set("debug", value, category="general") @classmethod def get_debug(cls): """ is debugging switched on? :return: """ return cls.get("debug", "general") @classmethod def debug(cls): """ :return: returns True if debugging is on """ return cls.get("debug", "general") # # Set the default for refresh # @classmethod def set_refresh(cls, value): """ sets the default for all clouds to refresh :param value: :return: """ cls.set("refresh", value, "general") @classmethod def get_refresh(cls): """ is refresh switched on? :return: """ return cls.get("refresh", "general") @classmethod def refresh(cls): """ :return: "on" if refresh is True, "off" otherwise """ try: value = cls.get_refresh() except: cls.set_refresh("on") value = "on" return value == "on" # set default for timer @classmethod def set_timer(cls, value): """ sets the default for all clouds to timer :param value: :return: """ cls.set("timer", value, "general") @classmethod def get_timer(cls): """ gets the timer :return: "on" if timer is True, "off" otherwise """ try: value = cls.get("timer", "general") except: cls.set_timer("off") value = "off" return value @classmethod def timer(cls): """ :return: "on" if timer is True, "off" otherwise """ value = cls.get_timer() return value == "on" @classmethod def load(cls, filename): config = ConfigDict(filename=filename)["cloudmesh"] clouds = config["clouds"] # FINDING DEFAULTS FOR CLOUDS for cloud in clouds: db = { "image": cls.get("image", cloud), "flavor": cls.get("flavor", cloud), } defaults = clouds[cloud]["default"] for attribute in ["image", "flavor"]: value = db[attribute] if attribute in defaults: value = db[attribute] or defaults[attribute] Default.set(attribute, value, category=cloud) # FINDING DEFAUlTS FOR KEYS # keys: # default: id_rsa # keylist: # id_rsa: ~/.ssh/id_rsa.pub # key_db = SSHKeyDBManager() name_key = cls.get("key") keys = config["keys"] name = keys["default"] if name in keys["keylist"]: value = name_key or keys["keylist"][name] # key_db.add(value, keyname=name) Default.set_key(name)