Exemple #1
0
    def list(self, name=None):
        """

        :param name:  if none all
        :return:
        """

        cm = CmDatabase()
        if name == None:
            entries = cm.find(cloud="local", kind="registry")
        else:
            entries = cm.find_name(name=name, kind="registry")

        return entries
    def list(self, name=None):
        """
        list entries in the registry

        :param name:  name of registered server.  If not passed will list all registered servers.
        :return:  list of registered server(s)
        """

        cm = CmDatabase()
        if name == None:
            entries = cm.find(cloud="local", kind="registry")
        else:
            entries = cm.find_name(name=name, kind="registry")

        return entries
Exemple #3
0
    def group_action(self, name=None, group=None, action="add"):
        names = Parameter.expand(name)
        groups = Parameter.expand(group)

        cloud = "local"
        db = CmDatabase()
        keys = db.find(collection=f"{cloud}-key")
        for key in keys:
            if key["name"] in names:
                for _group in groups:
                    if action == "add":
                        if _group not in key["group"]:
                            key["group"].append(_group)
                    elif action == "delete":
                        if _group in key["group"]:
                            key["group"].remove(_group)
        return keys
Exemple #4
0
    def _cancel(self, specification):
        cm = CmDatabase()
        entries = cm.find(cloud=self.name, kind='storage')
        id = specification['cm']['name']
        if id == 'None':
            for entry in entries:
                if entry['status'] == 'waiting':
                    entry['status'] = "cancelled"
        else:
            for entry in entries:
                if entry['cm']['id'] == id and entry['status'] == 'waiting':
                    entry['status'] = "cancelled"
                    break
        cm.update(entries)

        specification['status'] = 'completed'
        return specification
 def monitor(self, status, rate=5, output="table"):
     cm = CmDatabase()
     try:
         while True:
             entries = cm.find(cloud=self.name, kind='storage')
             if status != "all":
                 entries = list(
                     filter(lambda entry: entry['status'] == status,
                            entries))
             os.system("clear")
             self.pretty_print(data=entries,
                               data_type="monitor",
                               output=output)
             print("--------------Press Ctrl+C to quit.--------------")
             time.sleep(rate)
     except KeyboardInterrupt:
         pass
Exemple #6
0
    def list(self, name=None):
        cm = CmDatabase()
        result = []

        if name:
            col = cm.collection(name=f"{self.cloud}-{self.name}")
            entries = col.find_one({"cm.kind": 'group',
                                    "cm.cloud": 'local',
                                    "cm.name": name
                                    }, {"_id": 0})
            return [entries]
        else:
            entries = cm.find(collection=f"{self.cloud}-{self.name}")

        for entry in entries:
            result.append(entry)
        return result
Exemple #7
0
def load_content(fileName):
    """
    Load the content from the entry if it exists in the mongo db local-text
    collection

    :param fileName: name of the file contents being accessed
    :return:
    """

    db = CmDatabase()
    result = db.find(collection="local-text",
                     cloud="local",
                     kind="text",
                     query={"name": f"{fileName}"})
    result = flatten(result)
    result.pop()
    return result[0]["content"]
Exemple #8
0
    def export(self, group=None):

        if type(group) == list:
            groups = group
        else:
            groups = Parameter.expand(group)

        cm = CmDatabase()
        cloud = "local"

        keys = cm.find(collection=f"{cloud}-key")

        found = []
        for key in keys:

            for group in groups:
                if group in key["group"]:
                    found.append(key)
        return found
Exemple #9
0
    def get_actions(self):
        cm = CmDatabase()
        entries = cm.find(cloud=self.name, kind='storage')
        mkdir = []
        copy = []
        list = []
        delete = []
        cancel = []
        for entry in entries:
            pprint(entry)
            if entry['action'] == 'mkdir' and entry['status'] == 'waiting':
                mkdir.append(entry)
            elif entry['action'] == 'copy' and entry['status'] == 'waiting':
                copy.append(entry)
            elif entry['action'] == 'list' and entry['status'] == 'waiting':
                list.append(entry)
            elif entry['action'] == 'delete' and entry['status'] == 'waiting':
                delete.append(entry)
            elif entry['action'] == 'cancel' and entry['status'] == 'waiting':
                cancel.append(entry)

        return mkdir, copy, list, delete, cancel
    def get_actions(self):
        """
        get all the actions from database
        param:
        :return lists of actions from database
        """
        cm = CmDatabase()
        entries = cm.find(cloud=self.name, kind='storage')
        get_actions = []
        put_actions = []
        mkdir_actions = []
        copy_actions = []
        list_actions = []
        delete_actions = []
        cancel_actions = []
        search_actions = []

        for entry in entries:
            if entry['action'] == 'get' and entry['status'] == 'waiting':
                get_actions.append(entry)
            elif entry['action'] == 'put' and entry['status'] == 'waiting':
                put_actions.append(entry)
            elif entry['action'] == 'mkdir' and entry['status'] == 'waiting':
                mkdir_actions.append(entry)
            elif entry['action'] == 'copy' and entry['status'] == 'waiting':
                copy_actions.append(entry)
            elif entry['action'] == 'list' and entry['status'] == 'waiting':
                list_actions.append(entry)
            elif entry['action'] == 'delete' and entry['status'] == 'waiting':
                delete_actions.append(entry)
            elif entry['action'] == 'cancel' and entry['status'] == 'waiting':
                cancel_actions.append(entry)
            elif entry['action'] == 'search' and entry['status'] == 'waiting':
                search_actions.append(entry)

        return get_actions, put_actions, mkdir_actions, copy_actions, \
               list_actions, delete_actions, cancel_actions, search_actions
Exemple #11
0
 def list(self):
     cloud = "local"
     db = CmDatabase()
     keys = db.find(collection=f"{cloud}-key")
     return keys
Exemple #12
0
    def create(self, **kwargs):

        arguments = dotdict(kwargs)
        name = arguments.name
        cloud = arguments.cloud

        if name is None:
            name_generator = Name()
            vms = [str(name_generator)]
        else:
            vms = self.expand(name)

        #
        # Step 0, find the cloud
        #
        variables = Variables()
        if cloud is None:
            arguments.cloud = cloud = variables['cloud']

        # Step 1. iterate through the names to see if they already exist in
        # the DB and fail if one of them already exists

        database = CmDatabase()
        defaults = Config()[f"cloudmesh.cloud.{cloud}.default"]

        duplicates = []
        for vm in vms:
            query = {"name": vm}
            duplicates += database.find(collection=f'{cloud}-node', query=query)
        database.close_client()

        if len(duplicates) > 0:
            print(Printer.flatwrite(duplicates,
                                    order=['cm.name', 'cm.cloud'],
                                    header=['Name', 'Cloud'],
                                    output='table'))
            raise Exception("these vms already exists")

        # Step 2. identify the image and flavor from kwargs and if they do
        # not exist read them for that cloud from the yaml file

        if arguments.image is None:
            arguments.image = self.find_attribute('image', [variables, defaults])

        if arguments.image is None:
            raise ValueError("image not specified")

        if arguments.group is None:
            arguments.group = self.find_attribute('group', [variables, defaults])

        if arguments.group is None:
            arguments.group = "default"

        if arguments.size is None:
            arguments.size = self.find_attribute('size', [variables, defaults])

        if arguments.size is None:
            raise ValueError("size not specified")

        # Step 3: use the create command to create the vms

        # created = self.loop(vms, self.p.create, **arguments)
        arguments['name'] = vms

        created = self.loop(self._create, **arguments)

        VERBOSE(created)

        self.list()

        return created
    def do_flavor(self, args, arguments):
        """
        ::

            Usage:
                flavor list [NAMES] [--cloud=CLOUD] [--refresh] [--output=OUTPUT]


            Options:
               --output=OUTPUT  the output format [default: table]
               --cloud=CLOUD    the ycloud name
               --refresh        refreshes the data before displaying it

            Description:

                This lists out the flavors present for a cloud

            Examples:
                cm flavor list --refresh
                cm flavor list
                cm flavor list --output=csv
                cm flavor list 58c9552c-8d93-42c0-9dea-5f48d90a3188 --refresh

                please remember that a uuid or the flavor name can be used to
                identify a flavor.
        """

        map_parameters(arguments,
                       "refresh",
                       "cloud",
                       "output")

        variables = Variables()

        arguments.output = Parameter.find("output",
                                          arguments,
                                          variables,
                                          "table")

        arguments.refresh = Parameter.find_bool("refresh",
                                                arguments,
                                                variables)

        if arguments.list and arguments.refresh:

            names = []

            clouds, names = Arguments.get_cloud_and_names("list",
                                                          arguments,
                                                          variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                flavors = provider.flavors()

                provider.Print(flavors, output=arguments.output, kind="flavor")

            return ""

        elif arguments.list:

            names = []

            clouds, names = Arguments.get_cloud_and_names("list",
                                                          arguments,
                                                          variables)

            print(clouds, names)
            try:

                for cloud in clouds:
                    print(f"List {cloud}")
                    provider = Provider(cloud)

                    db = CmDatabase()
                    flavors = db.find(collection=f"{cloud}-flavor")

                    provider.Print(flavors, output=arguments.output, kind="flavor")

            except Exception as e:

                VERBOSE(e)

            return ""
    def do_key(self, args, arguments):
        """
        ::

           Usage:
             key  -h | --help
             key list --cloud=CLOUDS [--output=OUTPUT]
             key list --source=ssh [--dir=DIR] [--output=OUTPUT]
             key list --source=git [--output=OUTPUT] [--username=USERNAME]
             key list [--output=OUTPUT]
             key add NAME --filename=FILENAME [--output=OUTPUT]
             key add [NAME] [--source=FILENAME]
             key add [NAME] [--source=git]
             key add [NAME] [--source=ssh]
             key delete NAMES [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [VMS] [--dryrun]
             key group upload [NAMES] [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group add [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group add --file=FILENAME
             key group delete [--group=GROUPNAMES] [NAMES] [--dryrun]
             key group list [--group=GROUPNAMES] [--output=OUTPUT]
             key group export --group=GROUNAMES --filename=FILENAME


           Arguments:
             VMS            Parameterized list of virtual machines
             CLOUDS         The clouds
             NAME           The name of the key.
             SOURCE         db, ssh, all
             KEYNAME        The name of a key. For key upload it defaults to the default key name.
             OUTPUT         The format of the output (table, json, yaml)
             FILENAME       The filename with full path in which the key
                            is located

           Options:
              --dir=DIR                     the directory with keys [default: ~/.ssh]
              --output=OUTPUT               the format of the output [default: table]
              --source=SOURCE               the source for the keys
              --username=USERNAME           the source for the keys [default: none]
              --name=KEYNAME                The name of a key


           Description:

               Please note that some values are read from the cloudmesh.yaml
               file. One such value is cloudmesh.profile.user

               Manages public keys is an essential component of accessing
               virtual machine sin the cloud. There are a number of sources
               where you can find public keys. This includes teh ~/.ssh
               directory and for example github.

               Keys will be uploaded into cloudmesh database with the add
               command under the given NAME. If the name is not specified the name
               cloudmesh.profile.user is assumed.

                key add NAME  --source=ssh
                    adds the default key in ~/.ssh/id_rsa.pub
                key add NAME  --source=FILENAME
                    adds the key specified by the filename with the given name
                key add NAME --git --username=username
                    adds a named github key from a user with the given github
                    username.

               Once the keys are uploaded to github, they can be listed
               To list these keys the following list functions are provided.

                key list --source=git  [--username=USERNAME]
                    lists all keys in git for the specified user. If the
                    name is not specified it is read from cloudmesh.yaml
                key list --source=ssh  [--dir=DIR] [--output=OUTPUT]
                    lists all keys in the directory. If the directory is not
                    specified the default will be ~/.ssh

                key list NAMES
                    lists all keys in the named virtual machines.


                List command can use the [--output=OUTPUT] option

                    list the keys loaded to cloudmesh in the given format:
                    json, yaml, table. table is default. The NAME can be
                    specified and if omitted the name cloudmesh.profile.user
                    is assumed.

                To get keys from the cloudmesh database the following commands
                are available:

                key delete NAMES
                    deletes the Named keys. This may also have an impact on groups
                key rename NAME NEW
                    renames the key from NAME to NEW in the cloudmesh database.

               Group management of keys is an important concept in cloudmesh,
               allowing multiple users to be added to virtual machines while
               managing the keys associated with them. The keys must be uploaded
               to cloudmesh database with a name so they can be used in a
               group. The --dryrun option executes the command without
               uploading the information to the clouds. If no group name is
               specified the group name default is assumed. If no cloudnamesh
               are specified, all active clouds are assumed. active clouds
               can be set in the cloudmesh.yaml file.

                key group delete [GROUPNAMES] [NAMES] [--dryrun]
                    deletes the named keys from the named groups.

                key group list [GROUPNAMES] [--output=OUTPUT]
                    list the key names and details in the group.

                key group upload [GROUPNAMES] [CLOUDS] [--dryrun]
                    uploads the named groups to the specified clouds.

               In some cases you may want to store the public keys in files. For
               this reason we support the following commands.

                key group add --group=GROUPNAME --file=FILENAME
                    the command adds the keys to the given group. The keys are
                    written in the files in yaml format.


                key group export --group=GROUNAMES --filename=FILENAME
                    the command exports the keys to the given group. The keys are
                    written in the files in yaml format.


                The yaml format is as follows:

                cloudmesh:
                  keys:
                    NAMEOFKEY:
                      name: NAMEOFKEY
                      key: ssh-rsa AAAA..... comment
                      group:
                      - GROUPNAME
                    ...

                If a key is included in multiple groups they will be added
                to the grouplist of the key
        """
        def print_keys(keys):
            print(
                Printer.write(
                    keys,
                    sort_keys=["name"],
                    order=["name", "type", "fingerprint", "comment"],
                    header=["Name", "Type", "Fingerprint", "Comment"],
                    output=arguments.output))

        map_parameters(arguments, 'cloud', 'output', 'source', 'dir', 'output',
                       'source', 'dryrun')

        variables = Variables()

        if arguments.list and arguments.source == "git":

            config = Config()
            username = config["cloudmesh.profile.github"]
            keys = SSHkey().get_from_git(username)

            print_keys(keys)

            return ""

        elif arguments.list and arguments.source == "ssh":
            # this is much simpler

            sshkey = SSHkey()

            print_keys([sshkey])

            return ""

        elif arguments.list and arguments.cloud:

            clouds = Parameter.expand(arguments.cloud)

            if len(clouds) == 0:
                variables = Variables()
                cloudname = variables['cloud']
                clouds = [cloudname]
            keys = []

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                keys = provider.keys()

                provider.Print(keys, output=arguments.output, kind="key")

            return ""

        elif arguments.list:

            cloud = "local"
            db = CmDatabase()
            keys = db.find(collection=f"{cloud}-key")

            print_keys(keys)

            return ""

        elif arguments.add:
            """
             key add [NAME] [--source=FILENAME]
             key add [NAME] [--source=git]
             key add [NAME] [--source=ssh]
             """
            key = Key()

            source = arguments["--source"]
            if source == "ssh":
                name = arguments.NAME or "ssh"
                key.add(name, "ssh")
            elif source == "git":
                name = arguments.NAME or "git"
                key.add("git", "git")
            elif source is not None:
                name = arguments.NAME or source
                key.add(name, "input")
            else:
                config = Config()
                name = config["cloudmesh.profile.github"]
                kind = "ssh"
                key.add(name, kind)

        elif arguments.upload:
            """
            key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
            key upload [NAMES] [VMS] [--dryrun]
            """

            names = Parameter.expand(arguments.NAMES)

            # this may have a bug if NAMES is ommitted

            #
            # Step 0. Set keyname to variable
            #

            if names is None or len(names) == 0:

                config = Config()
                username = config["cloudmesh.profile.user"]
                names = [username]

            if len(names) == 1:
                name = names[0]
                variables = Variables()
                if "key" in variables:
                    old = variables["key"]
                    if old != name:
                        Console.msg(
                            f"Changing defualt key from {old} to {name}")
                        variables["key"] = name

            #
            # Step 1. keys = find keys to upload
            #

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            keys = []
            for key in db_keys:
                if key["name"] in names:
                    keys.append(key)

            if len(keys) == 0:
                Console.error(
                    f"No keys with the names {names} found in cloudmesh. \n"
                    "       Use the command 'key add' to add the key.")

            #
            # Step 2. iterate over the clouds to upload
            #

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                for key in db_keys:
                    name = key['name']
                    if name in names:
                        try:
                            r = provider.key_upload(key)
                            Console.ok(f"upload key '{name} successful'. ")
                        except ValueError as e:
                            Console.error(
                                f"key '{name} already exists in {cloud}.")

            return ""

        elif arguments.delete and arguments.cloud and arguments.NAMES:

            # key delete NAMES --cloud=CLOUDS [--dryrun]
            names = Parameter.expand(arguments.NAMES)
            clouds = Parameter.expand(arguments.cloud)

            for cloud in clouds:
                provider = Provider(name=cloud)
                for name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name} in {cloud}")
                    else:
                        images = provider.key_delete(name)

            return ""

        elif arguments.delete and arguments.NAMES:
            # key delete NAMES [--dryrun]

            names = Parameter.expand(arguments.NAMES)

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            error = []
            for key in db_keys:
                name = key['name']
                if name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name}")
                    else:
                        db.delete(collection="local-key", name=name)
                        Console.ok(f"delete {name}")
            return ""

        elif arguments.group:

            raise NotImplementedError

        return ""
Exemple #15
0
class Provider(VolumeABC):
    kind = "openstack"

    sample = """
    cloudmesh:
      volume:
        openstack:
          cm:
            active: true
            heading: Chameleon
            host: chameleoncloud.org
            label: chameleon
            kind: openstack
            version: train
            service: volume
          credentials:
            auth:
              username: TBD
              password: TBD
              auth_url: https://kvm.tacc.chameleoncloud.org:5000/v3
              project_id: TBD
              project_name: cloudmesh
              user_domain_name: Default
            region_name: KVM@TACC
            interface: public
            identity_api_version: '3'
            key_path: TBD/id_rsa.pub
          default:
            size: 1
            volume_type: __DEFAULT__
       
    """
    vm_state = [
        'ACTIVE',
        'BUILDING',
        'DELETED',
        'ERROR',
        'HARD_REBOOT',
        'PASSWORD',
        'PAUSED',
        'REBOOT',
        'REBUILD',
        'RESCUED',
        'RESIZED',
        'REVERT_RESIZE',
        'SHUTOFF',
        'SOFT_DELETED',
        'STOPPED',
        'SUSPENDED',
        'UNKNOWN',
        'VERIFY_RESIZE'
    ]

    output = {

        "volume": {
            "sort_keys": ["cm.name"],
            "order": ["cm.name",
                      "cm.cloud",
                      "cm.kind",
                      "availability_zone",
                      "created_at",
                      "size",
                      "status",
                      "id",
                      "volume_type"
                      ],
            "header": ["Name",
                       "Cloud",
                       "Kind",
                       "Availability Zone",
                       "Created At",
                       "Size",
                       "Status",
                       "Id",
                       "Volume Type"
                       ],
        }
    }

    def __init__(self, name):
        """
        Initialize provider. The default parameters are read from the
        configuration file that is defined in yaml format.

        :param name: name of cloud
        """
        self.cloud = name
        self.config = Config()["cloudmesh.volume.openstack.credentials"]
        self.defaults = Config()["cloudmesh.volume.openstack.default"]
        self.cm = CmDatabase()

    def update_dict(self, results):
        """
        This function adds a cloudmesh cm dict to each dict in the list
        elements. Typically this method is used
        internally.

        :param results: the original dicts.
        :return: The list with the modified dicts
        """

        if results is None:
            return None

        d = []

        for entry in results:
            volume_name = entry['name']
            if "cm" not in entry:
                entry['cm'] = {}

            entry["cm"].update({
                "cloud": self.cloud,
                "kind": "volume",
                "name": volume_name,
            })
            d.append(entry)
        return d

    def status(self, volume_name):
        """
        This function get volume status, such as "in-use", "available"

        :param volume_name: Volume name
        :return: Volume_status
        """
        con = openstack.connect(**self.config)
        result = con.get_volume(name_or_id=volume_name)
        result = [result]
        result = self.update_dict(result)
        return result

    def list(self, **kwargs):
        """
        This function list all volumes as following:
        If NAME (volume_name) is specified, it will print out info of NAME
        If NAME (volume_name) is not specified, it will print out info of all
          volumes

        :param kwargs: contains name of volume, vm name (optional)
        :return: Dictionary of volumes
        """
        try:
            if kwargs and kwargs['refresh'] is False:
                result = self.cm.find(cloud=self.cloud, kind='volume')
                for key in kwargs:
                    if key == 'NAME' and kwargs['NAME']:
                        result = self.cm.find_name(name=kwargs['NAME'])
                    elif key == 'NAMES' and kwargs['NAMES']:
                        result = self.cm.find_names(names=kwargs['NAMES'])
            else:
                con = openstack.connect(**self.config)
                results = con.list_volumes()
                if kwargs and kwargs['NAME']:
                    result = con.get_volume(name_or_id=kwargs["NAME"])
                    result = [result]
                    result = self.update_dict(result)
                if kwargs and kwargs['vm']:
                    server_id = con.get_server_id(name_or_id=kwargs['vm'])
                    vol_list = []
                    for entry in results:
                        attach_list = entry['attachments']
                        if len(attach_list) != 0:
                            if attach_list[0]['server_id'] == server_id:
                                vol_list.append(entry)
                    result = self.update_dict(vol_list)
                else:
                    result = self.update_dict(results)

        except Exception as e:
            Console.error("Problem listing volumes", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def create(self, **kwargs):
        """
        This function creates a new volume with default volume type __DEFAULT__.
        Default parameters are read from self.config.

        :param kwargs: Contains Volume name,size
        :return: Volume dictionary
        """
        try:
            con = openstack.connect(**self.config)
            arguments = dotdict(kwargs)
            if arguments.volume_type is None:
                arguments.volume_type = self.defaults["volume_type"]
            if arguments.size is None:
                arguments.size = self.defaults["size"]
            r = con.create_volume(name=arguments.NAME,
                                  size=arguments.size,
                                  volume_type=arguments.volume_type
                                  )
            r = [r]
            result = self.update_dict(r)
        except Exception as e:
            Console.error("Problem creating volume", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def attach(self, names=None, vm=None):
        """
        This function attaches a given volume to a given instance

        :param names: Names of Volumes
        :param vm: Instance name
        :return: Dictionary of volumes
        """
        try:
            con = openstack.connect(**self.config)
            server = con.get_server(vm)
            volume = con.get_volume(name_or_id=names[0])
            con.attach_volume(server, volume, device=None, wait=True,
                              timeout=None)
        except Exception as e:
            Console.error("Problem attaching volume", traceflag=True)
            print(e)
            raise RuntimeError
        return self.list(NAME=names[0], refresh=True)

    def detach(self, name=None):
        """
        This function detaches a given volume from an instance

        :param name: Volume name
        :return: Dictionary of volumes
        """
        try:
            con = openstack.connect(**self.config)
            volume = con.get_volume(name_or_id=name)
            attachments = volume['attachments']
            server = con.get_server(attachments[0]['server_id'])
            con.detach_volume(server, volume, wait=True, timeout=None)
        except Exception as e:
            Console.error("Problem detaching volume", traceflag=True)
            print(e)
            raise RuntimeError
        # return of self.list(NAME=NAME)[0] throwing error:cm attribute
        # not found inside CmDatabase.py. So manipulating result as below
        t = self.list(NAME=name, refresh=True)[0]
        result = {}
        result.update(
            {"cm": t["cm"],
             "availability_zone": t["availability_zone"],
             "created_at": t["created_at"],
             "size": t["size"], "id": t["id"],
             "status": t["status"],
             "volume_type": t["volume_type"]
             }
        )
        return result

    def delete(self, name=None):
        """
        This function delete one volume.

        :param name: Volume name
        :return: Dictionary of volumes
        """
        try:
            con = openstack.connect(**self.config)
            con.delete_volume(name_or_id=name)
            results = con.list_volumes()
            result = self.update_dict(results)
        except Exception as e:
            Console.error("Problem deleting volume", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def add_tag(self, **kwargs):
        """
        This function add tag to a volume.
        :param kwargs:
                    NAME: name of volume
                    key: name of tag
                    value: value of tag
        :return: Dictionary of volume
        """
        try:
            con = openstack.connect(**self.config)
            name = kwargs['NAME']
            key = kwargs['key']
            value = kwargs['value']
            metadata = {key: value}
            con.update_volume(name_or_id=name, metadata=metadata)
        except Exception as e:
            Console.error("Problem in tagging volume", traceflag=True)
            print(e)
            raise RuntimeError
        # return of self.list(NAME=NAME)[0] throwing error:cm attribute
        # not found inside CmDatabase.py. So manipulating result as below
        t = self.list(NAME=name, refresh=True)[0]
        result = {}
        result.update(
            {"cm": t["cm"],
             "availability_zone": t["availability_zone"],
             "created_at": t["created_at"],
             "size": t["size"], "id": t["id"],
             "status": t["status"],
             "volume_type": t["volume_type"]
             }
        )
        return result

    def migrate(self,
                name=None,
                fvm=None,
                tvm=None,
                fregion=None,
                tregion=None,
                fservice=None,
                tservice=None,
                fcloud=None,
                tcloud=None,
                cloud=None,
                region=None,
                service=None):
        """
        Migrate volume from one vm to another vm.

        :param name: name of volume
        :param fvm: name of vm where volume will be moved from
        :param tvm: name of vm where volume will be moved to
        :param fregion: the region where the volume will be moved from
        :param tregion: region where the volume will be moved to
        :param fservice: the service where the volume will be moved from
        :param tservice: the service where the volume will be moved to
        :param fcloud: the provider where the volume will be moved from
        :param tcloud: the provider where the volume will be moved to
        :param cloud: the provider where the volume will be moved within
        :param region: the region where the volume will be moved within
        :param service: the service where the volume will be moved within
        :return: dict
        """
        raise NotImplementedError

    def sync(self,
             volume_id=None,
             zone=None,
             cloud=None):
        """
        sync contents of one volume to another volume

        :param volume_id: id of volume A
        :param zone: zone where new volume will be created
        :param cloud: the provider where volumes will be hosted
        :return: str
        """
        raise NotImplementedError
Exemple #16
0
class TestMongo:
    def setup(self):
        self.database = CmDatabase()

        self.name = Name(experiment="exp",
                         group="grp",
                         user="******",
                         kind="vm",
                         counter=1)

    def test_00_status(self):
        HEADING()

        #print(self.name)
        #print(self.name.counter)
        #print(self.name.id(counter=100))

        self.database.clear()

        r = self.database.find()
        pprint(r)

        assert len(r) == 0

    def test_01_status(self):
        HEADING()
        r = self.database.status()
        # pprint(r)
        assert "Connection refused" not in r

        d = {}
        for field in ['uptime', 'pid', 'version', 'host']:
            d[field] = r[field]

        print(Printer.attribute(d))

        assert d is not None

    def test_02_update(self):
        HEADING()

        entries = [{"name": "Gregor"}, {"name": "Laszewski"}]

        for entry in entries:
            entry["cmid"] = str(self.name)
            entry["cmcounter"] = self.name.counter
            self.name.incr()
        self.database.update(entries)

        r = self.database.find()

        pprint(r)
        assert len(r) == 2

    def test_03_update(self):
        HEADING()

        r = self.database.find(name="Gregor")
        pprint(r)

        assert r[0]['name'] == "Gregor"

    def test_04_update(self):
        HEADING()
        entries = [{
            "cmcounter": 1,
            "name": "gregor"
        }, {
            "cmcounter": 2,
            "name": "laszewski"
        }]
        pprint(entries)
        for entry in entries:
            counter = entry["cmcounter"]
            print("Counter:", counter)
            entry["cmid"] = self.name.id(counter=counter)
        self.database.update(entries, replace=False)
        r = self.database.find()
        pprint(r)

    def test_05_update(self):
        HEADING()
        r = self.database.find(name="gregor")
        pprint(r)
        assert r[0]["name"] == "gregor"

    def test_06_find_by_counter(self):
        HEADING()
        r = self.database.find_by_counter(1)
        pprint(r)
        assert r[0]["name"] == "gregor"

        r = self.database.find_by_counter(2)
        pprint(r)
        assert r[0]["name"] == "laszewski"

    def test_07_decorator_update(self):
        HEADING()

        @DatabaseUpdate(collection="cloudmesh")
        def entry():
            name = Name()
            print(name)
            print("OOOO", str(name), name.counter)
            d = {
                "cmid": str(name),
                "cmcounter": name.counter,
                "name": "albert"
            }
            name.incr()
            pprint(d)
            return d

        a = entry()

        r = self.database.find_by_counter(3)

        pprint(r)

    def test_08_decorator_add(self):
        HEADING()

        @DatabaseAdd(collection="cloudmesh")
        def entry():
            d = {"name": "zweistein"}
            return d

        a = entry()

        r = self.database.find()

        pprint(r)

        assert len(r) == 4

    def test_09_overwrite(self):
        HEADING()
        r = self.database.find(name="gregor")[0]
        pprint(r)
        r["color"] = "red"

        self.database.update([r], replace=True)

        r = self.database.find(color="red")

        pprint(r)

        assert len(r) == 1

    def test_10_fancy(self):
        HEADING()

        counter = 1

        n = Name(experiment="exp",
                 group="grp",
                 user="******",
                 kind="vm",
                 counter=counter)

        print(n)

        entries = [{
            "cmcounter": counter,
            "cmid": str(n),
            "name": "gregor",
            "phone": "android"
        }]
        self.database.update(entries, replace=True)

        r = self.database.find()

        pprint(r)

        assert len(r) == 4
Exemple #17
0
    def do_aws(self, args, arguments):
        """
        ::

            Usage:
                vm ping [NAMES] [--cloud=CLOUDS] [--count=N] [--processors=PROCESSORS]
                vm check [NAMES] [--cloud=CLOUDS] [--username=USERNAME] [--processors=PROCESSORS]
                vm status [NAMES] [--cloud=CLOUDS]
                vm console [NAME] [--force]
                vm start [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]
                vm stop [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]
                vm terminate [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]
                vm delete [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]
                vm refresh [--cloud=CLOUDS]
                vm list [NAMES]
                        [--cloud=CLOUDS]
                        [--output=OUTPUT]
                        [--refresh]
                vm boot [--name=VMNAMES]
                        [--cloud=CLOUD]
                        [--username=USERNAME]
                        [--image=IMAGE]
                        [--flavor=FLAVOR]
                        [--public]
                        [--secgroup=SECGROUPs]
                        [--key=KEY]
                        [--dryrun]
                vm boot [--n=COUNT]
                        [--cloud=CLOUD]
                        [--username=USERNAME]
                        [--image=IMAGE]
                        [--flavor=FLAVOR]
                        [--public]
                        [--secgroup=SECGROUPS]
                        [--key=KEY]
                        [--dryrun]
                vm run [--name=VMNAMES] [--username=USERNAME] [--dryrun] COMMAND
                vm script [--name=NAMES] [--username=USERNAME] [--dryrun] SCRIPT
                vm ip assign [NAMES]
                          [--cloud=CLOUD]
                vm ip show [NAMES]
                           [--group=GROUP]
                           [--cloud=CLOUD],
                           [--output=OUTPUT]
                           [--refresh]
                vm ip inventory [NAMES]
                vm ssh [NAMES] [--username=USER]
                         [--quiet]
                         [--ip=IP]
                         [--key=KEY]
                         [--command=COMMAND]
                         [--modify-knownhosts]
                vm rename [OLDNAMES] [NEWNAMES] [--force] [--dryrun]
                vm wait [--cloud=CLOUD] [--interval=SECONDS]
                vm info [--cloud=CLOUD]
                        [--output=OUTPUT]
                vm username USERNAME [NAMES] [--cloud=CLOUD]
                vm resize [NAMES] [--size=SIZE]
                vm debug [NAMES]

            Arguments:
                OUTPUT         the output format
                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 vm 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:
                --output=OUTPUT   the output format [default: table]
                -H --modify-knownhosts  Do not modify ~/.ssh/known_hosts file
                                      when ssh'ing into a machine
                --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, 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 vm 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
                --parallel       execute commands in parallel


            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.

                vm refresh [--cloud=CLOUDS]
                    this command refreshes the data for virtual machines,
                    images and flavors for the specified clouds.

                vm ping [NAMES] [--cloud=CLOUDS] [--count=N] [--processors=PROCESSORS]
                     pings the specified virtual machines, while using at most N pings.
                     The ping is executed in parallel.
                     If names are specifies the ping is restricted to the given names in
                     parameter format. If clouds are specified, names that are not in
                     these clouds are ignored. If the name is set in the variables
                     this name is used.

            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\"

            Limitations:

        """

        map_parameters(arguments, 'active', 'cloud', 'command', 'dryrun',
                       'flavor', 'force', 'output', 'group', 'image',
                       'interval', 'ip', 'key', 'modify-knownhosts', 'n',
                       'name', 'public', 'quiet', 'secgroup', 'size',
                       'username')

        # VERBOSE.print(arguments, verbose=9)

        variables = Variables()

        # pprint(arguments)
        # pprint(variables)

        provider = Provider()
        database = CmDatabase()

        # ok, but not tested
        if arguments.refresh:
            """vm refresh [--cloud=CLOUDS]"""
            provider.list()
            provider.flavors()
            provider.images()

        # ok
        elif arguments.ping:
            """vm ping [NAMES] [--cloud=CLOUDS] [--count=N] [--processors=PROCESSORS]"""
            # cms aws ping t --cloud=aws --count=3 --processors=3
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "ping", arguments, variables)

            params = {}

            count = arguments['--count']
            if count:
                params['count'] = int(count)

            processors = arguments['--processors']
            if processors:
                params['processors'] = int(processors[0])

            # gets public ips from database
            public_ips = []
            cursor = database.db['aws-node']
            for name in names:
                for node in cursor.find({'name': name}):
                    public_ips.append(node['public_ips'])
            public_ips = [y for x in public_ips for y in x]
            # print(public_ips)

            Shell3.pings(ips=public_ips, **params)

        # ok
        elif arguments.check:
            """vm check [NAMES] [--cloud=CLOUDS] [--username=USERNAME] [--processors=PROCESSORS]"""
            # cms aws check t --cloud=aws --username=ubuntu --processors=3
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "ping", arguments, variables)

            params = {}

            params['key'] = provider.p.spec["credentials"][
                'EC2_PRIVATE_KEY_FILE_PATH'] + provider.p.spec["credentials"][
                    'EC2_PRIVATE_KEY_FILE_NAME']

            params['username'] = arguments['--username']  # or get from db

            processors = arguments['--processors']
            if processors:
                params['processors'] = int(processors[0])

            # gets public ips from database
            public_ips = []
            cursor = database.db['aws-node']
            for name in names:
                for node in cursor.find({'name': name}):
                    public_ips.append(node['public_ips'])
            public_ips = [y for x in public_ips for y in x]

            Shell3.checks(hosts=public_ips, **params)

        # ok
        elif arguments.status:
            """vm status [NAMES] [--cloud=CLOUDS]"""
            # cms aws status t --cloud=aws
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "status", arguments, variables)

            # gets status from database
            status = {}
            cursor = database.db['aws-node']
            for name in names:
                for node in cursor.find({'name': name}):
                    status[name] = node['state']

            pprint(status)

        #ok
        elif arguments.start:
            """vm start [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]"""
            # cms aws start t --parallel --processors=3
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "start", arguments, variables)

            params = {}

            processors = arguments['--processors']

            if arguments['--parallel']:
                params['option'] = 'pool'
                if processors:
                    params['processors'] = int(processors[0])
            else:
                params['option'] = 'iter'

            if arguments['--dryrun']:
                print("start nodes {}\noption - {}\nprocessors - {}".format(
                    names, params['option'], processors))
            else:
                pprint(provider.start(names, **params))

        #ok
        elif arguments.stop:
            """vm stop [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]"""
            # cms aws stop t --parallel --processors=2
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "stop", arguments, variables)

            params = {}

            processors = arguments['--processors']

            if arguments['--parallel']:
                params['option'] = 'pool'
                if processors:
                    params['processors'] = int(processors[0])
            else:
                params['option'] = 'iter'

            if arguments['--dryrun']:
                print("stop nodes {}\noption - {}\nprocessors - {}".format(
                    names, params['option'], processors))
            else:
                vms = provider.stop(names, **params)
                order = provider.p.output['vm']['order']
                header = provider.p.output['vm']['header']
                print(
                    Printer.flatwrite(vms,
                                      order=order,
                                      header=header,
                                      output='table'))

        #ok
        elif arguments.terminate:
            """vm terminate [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]"""
            # cms aws terminate t --parallel --processors=2
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "terminate", arguments, variables)

            params = {}

            processors = arguments['--processors']

            if arguments['--parallel']:
                params['option'] = 'pool'
                if processors:
                    params['processors'] = int(processors[0])
            else:
                params['option'] = 'iter'

            if arguments['--dryrun']:
                print(
                    "terminate nodes {}\noption - {}\nprocessors - {}".format(
                        names, params['option'], processors))
            else:
                pprint(provider.destroy(names, **params))

        #ok
        elif arguments.delete:
            """vm delete [NAMES] [--cloud=CLOUD] [--parallel] [--processors=PROCESSORS] [--dryrun]"""
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "terminate", arguments, variables)

            params = {}

            processors = arguments['--processors']

            if arguments['--parallel']:
                params['option'] = 'pool'
                if processors:
                    params['processors'] = int(processors[0])
            else:
                params['option'] = 'iter'

            if arguments['--dryrun']:
                print("delete nodes {}\noption - {}\nprocessors - {}".format(
                    names, params['option'], processors))
            else:
                pprint(provider.destroy(names, **params))

        # TODO: username, secgroup
        elif arguments.boot:
            """
                            vm boot [--name=VMNAMES]
                                    [--cloud=CLOUD]
                                    [--username=USERNAME]
                                    [--image=IMAGE]
                                    [--flavor=FLAVOR]
                                    [--public]
                                    [--secgroup=SECGROUPs]
                                    [--key=KEY]
                                    [--dryrun]
                            vm boot [--n=COUNT]
                                    [--cloud=CLOUD]
                                    [--username=USERNAME]
                                    [--image=IMAGE]
                                    [--flavor=FLAVOR]
                                    [--public]
                                    [--secgroup=SECGROUPS]
                                    [--key=KEY]
                                    [--dryrun]
            """
            if arguments['--name']:
                # cms aws boot --name=t --cloud=aws --username=root --image=ami-08692d171e3cf02d6  --flavor=t2.micro --public --secgroup=group1 --key=aws_cert
                # cms aws boot --name=t --image=ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20190212  --flavor=t2.micro --key=aws_cert
                names = Parameter.expand(arguments['--name'])

            elif arguments['n']:
                # cms aws boot --n=2 --cloud=aws --username=root --image=ami-08692d171e3cf02d6  --flavor=t2.micro --public --secgroup=group1 --key=aws_cert
                n = int(arguments['n'])
                names = []
                for i in range(n):  # generate random names
                    m = hashlib.blake2b(digest_size=8)
                    m.update(str(datetime.utcnow()).encode('utf-8'))
                    names.append(m.hexdigest())

            else:
                print("please provide name or count to boot vm")

            # username = arguments['--username']
            image = arguments['--image']
            flavor = arguments['--flavor']

            params = {}

            public = arguments['--public']
            if public:
                params['ex_assign_public_ip'] = public

            secgroup = Parameter.expand(arguments['--secgroup'])
            if secgroup:
                params['ex_security_groups'] = secgroup

            key = arguments['--key']
            if key:
                params['ex_keyname'] = key

            if arguments['--dryrun']:
                print("""create nodes {}
image - {}
flavor - {}
assign public ip - {}
security groups - {}
keypair name - {}""".format(names, image, flavor, public, secgroup, key))
            else:
                order = provider.p.output['vm']['order']
                header = provider.p.output['vm']['header']
                vm = provider.create(names=names,
                                     image=image,
                                     size=flavor,
                                     **params)
                print(
                    Printer.write(vm,
                                  order=order,
                                  header=header,
                                  output='table'))

        #ok
        elif arguments.list:
            """
            vm list [NAMES]
                    [--cloud=CLOUDS]
                    [--output=OUTPUT]
                    [--refresh]
            """
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            params = {}

            params['order'] = provider.p.output['vm']['order']
            params['header'] = provider.p.output['vm']['header']
            params['output'] = 'table'

            if arguments['--refresh']:
                provider.list()

            if arguments.NAMES:
                vms = []
                for name in names:
                    vms += database.find(collection='aws-node', name=name)
            else:
                vms = database.find(collection='aws-node')

            print(Printer.flatwrite(vms, **params))

        # TODO
        elif arguments.info:
            """
            vm info [--cloud=CLOUD]
                    [--output=OUTPUT]
            """
            print("functionality not implemented")

        # TODO
        elif arguments.rename:
            """vm rename [OLDNAMES] [NEWNAMES] [--force] [--dryrun]"""
            print("functionality not implemented")

        # TODO
        elif arguments.ip and arguments.show:
            """vm ip show [NAMES]
                       [--group=GROUP]
                       [--cloud=CLOUD]
                       [--output=OUTPUT]
                       [--refresh]
            """
            clouds, names = Arguments.get_cloud_and_names(
                "ip", arguments, variables)
            pprint(get_publicIPs(names))

        # TODO
        elif arguments.ip and arguments.assign:
            """
            vm ip assign [NAMES]
                      [--cloud=CLOUD]
            """
            clouds, names = Arguments.get_cloud_and_names(
                "ip", arguments, variables)

            pprint(provider.assign_public_ip(names))

        # TODO
        elif arguments.ip and arguments.inventory:
            """vm ip inventory [NAMES]"""
            print("list ips that could be assigned")

        # TODO
        elif arguments.default:
            """vm default [options...]"""
            print("functionality not implemented")

        # ok
        elif arguments.run:
            """vm run [--name=VMNAMES] [--username=USERNAME] [--dryrun] [COMMAND ...]"""
            # cms aws run --name=t --username=ubuntu uname
            clouds, names = Arguments.get_cloud_and_names(
                "run", arguments, variables)
            username = arguments['--username']
            command = arguments.COMMAND

            name_ips = {}
            cursor = database.db['aws-node']
            for name in names:
                for node in cursor.find({'name': name}):
                    name_ips[name] = node['public_ips']

            if arguments['--dryrun']:
                print("run command {} on vms: {}".format(command, names))
            else:
                provider.ssh(name_ips, username=username, command=command)

        # BUG in call command
        elif arguments.script:
            """vm script [--name=NAMES] [--username=USERNAME] [--dryrun] SCRIPT"""
            # cms aws script --name=t --username=ubuntu tests/test_aws.sh
            clouds, names = Arguments.get_cloud_and_names(
                "run", arguments, variables)
            username = arguments['--username']
            script = arguments.SCRIPT

            name_ips = {}
            cursor = database.db['aws-node']
            for name in names:
                for node in cursor.find({'name': name}):
                    name_ips[name] = node['public_ips']

            if arguments['--dryrun']:
                print("run script {} on vms: {}".format(script, names))
            else:
                provider.ssh(name_ips, username=username, script=script)

        # TODO
        elif arguments.resize:
            """vm resize [NAMES] [--size=SIZE]"""
            pass

        # TODO
        # shh run command in implemented as aws run
        # not sure what to do with this command
        # since ssh into multiple vms at the same time doesn't make a lot of sense
        elif arguments.ssh:
            """
            vm ssh [NAMES] [--username=USER]
                     [--quiet]
                     [--ip=IP]
                     [--key=KEY]
                     [--command=COMMAND]
                     [--modify-knownhosts]
            """
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            ips = {}
            cursor = database.db['aws-node']
            for name in names:
                for node in cursor.find({'name': name}):
                    pprint(node)

            username = arguments['--username']
            ip = arguments['--ip']

            params = {}

            quiet = arguments['--quiet']
            if quiet:
                params['quiet'] = quiet

            command = arguments['--command']
            if command:
                params['command'] = command

            modify_host = arguments['--modify-knownhosts']
            if modify_host:
                params['modify_host'] = modify_host

            provider.ssh(username=username, ip=ip, **params)

        # TODO
        elif arguments.wait:
            """vm wait [--cloud=CLOUD] [--interval=SECONDS]"""
            print("waits for the vm till its ready and one can login")

        # TODO
        elif arguments.username:
            """vm username USERNAME [NAMES] [--cloud=CLOUD]"""
            print("sets the username for the vm")

        elif arguments.debug:
            pprint(provider.p.driver)
            # print(provider.p.cloudman.ex_list_floating_ips())
            # print(provider.loop(names, abs, option='iter',processors=3))

        return
Exemple #18
0
    def do_image(self, args, arguments):
        """
        ::

            Usage:
                image list [NAMES] [--cloud=CLOUD] [--refresh] [--output=OUTPUT] [--query=QUERY]

            Options:
               --output=OUTPUT  the output format [default: table]
               --cloud=CLOUD    the cloud name
               --refresh        live data taken from the cloud

            Description:
                image list
                image list --cloud=aws --refresh
                image list --output=csv
                image list 58c9552c-8d93-42c0-9dea-5f48d90a3188 --refresh
        """

        map_parameters(arguments, "query", "refresh", "cloud", "output")

        variables = Variables()

        arguments.output = Parameter.find("output", arguments, variables,
                                          "table")

        arguments.refresh = Parameter.find_bool("refresh", arguments,
                                                variables)
        if arguments.list and arguments["--query"]:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud} query={arguments.query}")
                provider = Provider(name=cloud)
                if arguments.query is not None:
                    query = eval(arguments.query)
                    images = provider.images(**query)
                else:
                    images = provider.images()

                provider.Print(images, output=arguments.output, kind="image")

            return ""

        if arguments.list and arguments.refresh:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                images = provider.images()

                provider.Print(images, output=arguments.output, kind="image")

            return ""

        elif arguments.list:

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            print(clouds)
            print("find images")

            try:

                for cloud in clouds:
                    print(f"List {cloud} images")
                    provider = Provider(name=cloud)

                    db = CmDatabase()

                    images = db.find(collection=f"{cloud}-image")

                    provider.Print(images,
                                   output=arguments.output,
                                   kind="image")

            except Exception as e:

                VERBOSE(e)

            return ""
    def do_key(self, args, arguments):
        """
        ::

           Usage:
             key  -h | --help
             key list --cloud=CLOUDS [--output=OUTPUT]
             key list --source=ssh [--dir=DIR] [--output=OUTPUT]
             key list --source=git [--output=OUTPUT] [--username=USERNAME]
             key list [--output=OUTPUT]
             key init
             key add NAME --filename=FILENAME [--output=OUTPUT]
             key add [NAME] [--source=FILENAME]
             key add [NAME] [--source=git]
             key add [NAME] [--source=ssh]
             key delete NAMES [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [VMS] [--dryrun]
             key group upload [NAMES] [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group add [NAMES] [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group delete [--group=GROUPNAMES] [NAMES] [--dryrun]
             key group list [--group=GROUPNAMES] [--output=OUTPUT]
             key group export --group=GROUNAMES --filename=FILENAME
             key gen (ssh | pem) [--filename=FILENAME] [--nopass] [--set_path] [--force]
             key reformat (ssh | pem) [--filename=FILENAME] [--format=FORMAT]
                                      [--nopass] [--pub]
             key verify (ssh | pem) [--filename=FILENAME] [--pub] [--check_pass]

           Arguments:
             VMS        Parameterized list of virtual machines
             CLOUDS     The clouds
             NAME       The name of the key.
             SOURCE     db, ssh, all
             OUTPUT     The format of the output (table, json, yaml)
             FILENAME   The filename with full path in which the key is located
             FORMAT     Desired key format (SubjectInfo, SSH, OpenSSL, PKCS8)

           Options:
              --dir=DIR             the directory with keys [default: ~/.ssh]
              --check_pass          Flag where program query user for password
              --filename=FILENAME   the name and full path to the file
              --nopass              Flag indicating if the key has no password
              --output=OUTPUT       the format of the output [default: table]
              --pub                 Indicates that the public key is passed in
              --set_path            Sets the cloudmesh encryption key path to
                                    the full path of the generated keys
              --source=SOURCE       the source for the keys
              --username=USERNAME   the source for the keys [default: none]


           Description:

               Please note that some values are read from the cloudmesh.yaml
               file. One such value is cloudmesh.profile.user

               Management of public keys is an essential component of accessing
               virtual machines in the cloud. There are a number of sources
               where you can find public keys. This includes the ~/.ssh
               directory and for example github. If you do not already have a
               public-private key pair they can be generated using cloudmesh

               key gen ssh 
                   This will create the public-private keypair of ~/.ssh/id_rsa
                   and ~/.ssh/id_rsa.pub in OpenSSH format

               key gen pem 
                   This will create the public-private keypair of ~/.ssh/id_rsa
                   and ~/.ssh/id_rsa.pub in PEM format

               key gen (ssh | pem) --filename=~/.cloudmesh/foobar
                   This will generate the public-private key pair of 
                   ~/.cloudmesh/foobar and ~/.cloudmesh/foobar.pub

               key gen (ssh | pem) --filename=~/.cloudmesh/foobar --set_path
                   This will generate the keys as stated above, but it will
                   also set cloudmesh to use these keys for encryption.

               Keys can also be verified for their formatting and passwords.
               By default cloudmesh checks ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub
               If the key is password protected the formatting can only be
               verified if the password is provided (--check_pass argument)

               key verify pem
                   Verifies that ~/.ssh/id_rsa has PEM format

               key verify ssh --pub
                   Verifies that ~/.ssh/id_rsa.pub has OpenSSH format

               key verify pem --filename=~/.cloudmesh/foobar
                   Verifies if the private key located at ~/.cloudmesh/foobar
                   is password protected

               key verify pem --filenam=~/.cloudmesh/foobar --check_pass
                   Request the password to the file, then checks if the
                   key is in proper PEM format

               You may find the need to keep the values of your keys but
               different encodings or formats. These aspects of your key can
               also be changed using cloudmesh.

               key reformat pem
                   Will reformat the ~/.id_rsa.pub key from PEM to OpenSSH

               key reformat ssh
                   Will reformat the ~/.id_rsa.pub key from OpenSSH to PEM

               key reformat --filename=~/.id_rsa --format=PKCS8
                   Will reformat the private key to PKCS8 format

               Keys will be uploaded into cloudmesh database with the add
               command under the given NAME. If the name is not specified the name
               cloudmesh.profile.user is assumed.

                key add NAME  --source=ssh
                    adds the default key in ~/.ssh/id_rsa.pub
                key add NAME  --source=FILENAME
                    adds the key specified by the filename with the given name
                key add NAME --git --username=username
                    adds a named github key from a user with the given github
                    username.

                key set
                    adds the ~/.ssh/id_rsa.pub key with the name specified in
                    cloudmesh.profile.user.
                    It also sets the variable key to that user.


               Once the keys are uploaded to github, they can be listed
               To list these keys the following list functions are provided.

                key list --source=git  [--username=USERNAME]
                    lists all keys in git for the specified user. If the
                    name is not specified it is read from cloudmesh.yaml
                key list --source=ssh  [--dir=DIR] [--output=OUTPUT]
                    lists all keys in the directory. If the directory is not
                    specified the default will be ~/.ssh

                key list NAMES
                    lists all keys in the named virtual machines.


                List command can use the [--output=OUTPUT] option

                    list the keys loaded to cloudmesh in the given format:
                    json, yaml, table. table is default. The NAME can be
                    specified and if omitted the name cloudmesh.profile.user
                    is assumed.

                To get keys from the cloudmesh database the following commands
                are available:

                key delete NAMES
                    deletes the Named keys. This may also have an impact on groups
                key rename NAME NEW
                    renames the key from NAME to NEW in the cloudmesh database.

               Group management of keys is an important concept in cloudmesh,
               allowing multiple users to be added to virtual machines while
               managing the keys associated with them. The keys must be uploaded
               to cloudmesh database with a name so they can be used in a
               group. The --dryrun option executes the command without
               uploading the information to the clouds. If no group name is
               specified the group name default is assumed. If no cloudnamesh
               are specified, all active clouds are assumed. active clouds
               can be set in the cloudmesh.yaml file.

                key group delete [GROUPNAMES] [NAMES] [--dryrun]
                    deletes the named keys from the named groups.

                key group list [GROUPNAMES] [--output=OUTPUT]
                    list the key names and details in the group.

                key group upload [GROUPNAMES] [CLOUDS] [--dryrun]
                    uploads the named groups to the specified clouds.

               In some cases you may want to store the public keys in files. For
               this reason we support the following commands.

                key group add --group=GROUPNAME --file=FILENAME
                    the command adds the keys to the given group. The keys are
                    written in the files in yaml format.


                key group export --group=GROUNAMES --filename=FILENAME
                    the command exports the keys to the given group. The keys are
                    written in the files in yaml format.


                The yaml format is as follows:

                cloudmesh:
                  keys:
                    NAMEOFKEY:
                      name: NAMEOFKEY
                      key: ssh-rsa AAAA..... comment
                      group:
                      - GROUPNAME
                    ...

                If a key is included in multiple groups they will be added
                to the grouplist of the key
        """

        def print_keys(keys):
            print(Printer.write(
                keys,
                sort_keys=["name"],
                order=["name", "type", "fingerprint", "comment"],
                header=["Name", "Type", "Fingerprint", "Comment"],
                output=arguments.output)
            )

        map_parameters(arguments,
                       'check_pass',
                       'cloud',
                       'dir',
                       'dryrun',
                       'filename',
                       'force',
                       'format',
                       'name',
                       'nopass',
                       'output',
                       'pub',
                       'pwd',
                       'set_path',
                       'source')

        variables = Variables()

        if arguments.list and arguments.source == "git":

            config = Config()
            username = config["cloudmesh.profile.github"]
            keys = SSHkey().get_from_git(username)

            print_keys(keys)

            return ""

        elif arguments.list and arguments.source == "ssh":
            # this is much simpler

            sshkey = SSHkey()

            print_keys([sshkey])

            return ""

        elif arguments.list and arguments.cloud:

            clouds = Parameter.expand(arguments.cloud)

            if len(clouds) == 0:
                variables = Variables()
                cloudname = variables['cloud']
                clouds = [cloudname]
            keys = []

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                keys = provider.keys()

                provider.Print(keys, output=arguments.output, kind="key")

            return ""

        elif arguments.list:

            cloud = "local"
            db = CmDatabase()
            keys = db.find(collection=f"{cloud}-key")

            print_keys(keys)

            return ""


        elif arguments.add:

            """
            key add [NAME] [--source=FILENAME] # NOT IMPLEMENTED YET
            key add [NAME] [--source=git]
            key add [NAME] [--source=ssh]
            """

            key = Key()

            if arguments["--source"] == "ssh":
                name = arguments.NAME or "ssh"
                key.add(name, "ssh")
            elif arguments["--source"] == "git":
                name = arguments.NAME or "git"
                key.add("git", "git")
            else:
                config = Config()
                name = config["cloudmesh.profile.user"]
                kind = "ssh"
                key.add(name, kind)

        elif arguments.init:

            """
            key init 
            """

            config = Config()
            username = config["cloudmesh.profile.user"]

            if username == "TBD":
                Console.error(
                    "Please set cloudmesh.profile.user in ~/.cloudmesh.yaml")
                u = os.environ["USER"].lower().replace(" ", "")
                Console.msg(
                    f"To change it you can use the command. Define a NAME such as '{u}' e.g.")
                Console.msg("")
                Console.msg(f"  cms config set cloudmesh.profile.user={u}")
                Console.msg("")
                return ""

            key = Key()

            key.add(username, "ssh")
            variables['key'] = username

        elif arguments.upload:

            """
            key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
            key upload [NAMES] [VMS] [--dryrun]
            """

            names = Parameter.expand(arguments.NAMES)

            # this may have a bug if NAMES is ommitted

            #
            # Step 0. Set keyname to variable
            #

            if names is None or len(names) == 0:
                config = Config()
                username = config["cloudmesh.profile.user"]
                names = [username]

            if len(names) == 1:
                name = names[0]
                variables = Variables()
                if "key" in variables:
                    old = variables["key"]
                    if old != name:
                        Console.msg(
                            f"Changing default key from {old} to {name}")
                        variables["key"] = name

            #
            # Step 1. keys = find keys to upload
            #

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            keys = []
            for key in db_keys:
                if key["name"] in names:
                    keys.append(key)

            if len(keys) == 0:
                Console.error(
                    f"No keys with the names {names} found in cloudmesh. \n"
                    "       Use the command 'key add' to add the key.")

            #
            # Step 2. iterate over the clouds to upload
            #

            clouds, vmnames = Arguments.get_cloud_and_names("list",
                                                            arguments,
                                                            variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                for key in db_keys:
                    name = key['name']
                    if name in names:
                        try:
                            r = provider.key_upload(key)
                            Console.ok(f"upload key '{name} successful'. ")
                        except ValueError as e:
                            Console.error(
                                f"key '{name} already exists in {cloud}.")

            return ""


        elif arguments.delete and arguments.cloud and arguments.NAMES:

            # key delete NAMES --cloud=CLOUDS [--dryrun]
            names = Parameter.expand(arguments.NAMES)
            clouds = Parameter.expand(arguments.cloud)

            for cloud in clouds:
                provider = Provider(name=cloud)
                for name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name} in {cloud}")
                    else:
                        images = provider.key_delete(name)

            return ""

        elif arguments.group:

            raise NotImplementedError

        return ""
Exemple #20
0
    def do_image(self, args, arguments):
        """
        ::

            Usage:
                image list [NAMES] [--cloud=CLOUD] [--refresh] [--output=OUTPUT]

            Options:
               --output=OUTPUT  the output format [default: table]
               --cloud=CLOUD    the cloud name
               --refresh        live data taken from the cloud

            Description:
                cm image list
                cm image list --output=csv
                cm image list 58c9552c-8d93-42c0-9dea-5f48d90a3188 --refresh
        """

        map_parameters(arguments, "refresh", "cloud", "output")

        VERBOSE.print(arguments, verbose=9)

        variables = Variables()

        if arguments.list and arguments.refresh:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            print("AAA", clouds, names)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                images = provider.images()

                order = provider.p.output['vm']['order']  # not pretty
                header = provider.p.output['vm']['header']  # not pretty

                print(
                    Printer.flatwrite(images,
                                      sort_keys=["name"],
                                      order=order,
                                      header=header,
                                      output=arguments.output))
            return ""

        elif arguments.list:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            print(clouds, names)
            try:

                for cloud in clouds:
                    print(f"List {cloud}")
                    p = Provider(cloud)
                    kind = p.kind

                    collection = "{cloud}-image".format(cloud=cloud,
                                                        kind=p.kind)
                    db = CmDatabase()
                    vms = db.find(collection=collection)

                    order = p.p.output['vm']['order']  # not pretty
                    header = p.p.output['vm']['header']  # not pretty

                    print(
                        Printer.flatwrite(vms,
                                          sort_keys=["name"],
                                          order=order,
                                          header=header,
                                          output=arguments.output))

            except Exception as e:

                VERBOSE.print(e, verbose=9)

            return ""
Exemple #21
0
    def ssh(self, vm=None, command=None):
        def key_selector(keys):
            '''
           This is a helper method for ssh key selection
           THIS IS JUST A SAFETY MEASURE, PLEASE DON'T MIND IT
            :param keys:
            :return:
            '''
            tmp_keys = keys[:]
            # indices = range(1,len(tmp_keys)+1)
            for key_idx, key in enumerate(keys):
                key['idx'] = key_idx + 1
            print(
                Printer.flatwrite(
                    tmp_keys,
                    sort_keys=["idx"],
                    order=['idx', 'KeyName', 'KeyFingerprint'],
                    header=['Index', 'Key Name', "Key Fingerprint"],
                    output="table",
                    humanize=None))
            # Console.msg("Please select one of the AWS key indices from the table above: ")
            picked = 0
            while picked < 1 or picked > len(keys):
                try:
                    picked = int(
                        input(
                            "Please select one of the AWS key indices from the table above: "
                        ))
                except ValueError:
                    pass
            return keys[picked - 1]

        cm = CmDatabase()
        ip = vm['public_ips']

        try:
            key_name = vm['KeyName']
            keys = cm.find_all_by_name(name=key_name, kind="key")
            for k in keys:
                if 'location' in k.keys():
                    if 'private' in k['location'].keys():
                        key = k['location']['private']
                        break

        except (KeyError, IndexError):
            aws_keys = cm.find(kind='key', cloud='aws')
            if len(aws_keys) == 0:
                Console.error(
                    f"Could not find a key for the AWS instance '{vm['name']}'"
                )
                Console.error(
                    f"Use `cms help key` to learn how to add and upload a key for AWS"
                )
                return
            aws_key = key_selector(aws_keys)
            for sshkey in cm.find_all_by_name(name=aws_key['KeyName'],
                                              kind="key"):
                if "location" in sshkey.keys():
                    key = sshkey['location']['private']
                    break
        user = "******"  # needs to be set on creation.

        if command is None:
            command = ""

        if user is None:
            location = ip
        else:
            location = user + '@' + ip
        cmd = "ssh " \
              "-o StrictHostKeyChecking=no " \
              "-o UserKnownHostsFile=/dev/null " \
              f"-i {key} {location} {command}"
        cmd = cmd.strip()
        print(cmd)
        # VERBOSE(cmd)

        if command == "":
            if platform.lower() == 'win32':

                class disable_file_system_redirection:
                    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
                    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection

                    def __enter__(self):
                        self.old_value = ctypes.c_long()
                        self.success = self._disable(
                            ctypes.byref(self.old_value))

                    def __exit__(self, type, value, traceback):
                        if self.success:
                            self._revert(self.old_value)

                with disable_file_system_redirection():
                    os.system(cmd)
            else:
                os.system(cmd)

        else:
            if platform.lower() == 'win32':

                class disable_file_system_redirection:
                    _disable = ctypes.windll.kernel32.Wow64DisableWow64FsRedirection
                    _revert = ctypes.windll.kernel32.Wow64RevertWow64FsRedirection

                    def __enter__(self):
                        self.old_value = ctypes.c_long()
                        self.success = self._disable(
                            ctypes.byref(self.old_value))

                    def __exit__(self, type, value, traceback):
                        if self.success:
                            self._revert(self.old_value)

                with disable_file_system_redirection():
                    ssh = subprocess.Popen(cmd,
                                           shell=True,
                                           stdout=subprocess.PIPE,
                                           stderr=subprocess.PIPE)
            else:
                ssh = subprocess.Popen(cmd,
                                       shell=True,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)
            result = ssh.stdout.read().decode("utf-8")
            if not result:
                error = ssh.stderr.readlines()
                print("ERROR: %s" % error)
            else:
                return result
Exemple #22
0
    def do_key(self, args, arguments):
        """
        ::

           Usage:
             key  -h | --help
             key list --cloud=CLOUDS [--output=OUTPUT]
             key list --source=ssh [--dir=DIR] [--output=OUTPUT]
             key list --source=git [--output=OUTPUT] [--username=USERNAME]
             key list [--output=OUTPUT]
             key init
             key add NAME --filename=FILENAME [--output=OUTPUT]
             key add [NAME] [--source=FILENAME]
             key add [NAME] [--source=git]
             key add [NAME] [--source=ssh]
             key delete NAMES [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [VMS] [--dryrun]
             key group upload [NAMES] [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group add [NAMES] [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group delete [--group=GROUPNAMES] [NAMES] [--dryrun]
             key group list [--group=GROUPNAMES] [--output=OUTPUT]
             key group export --group=GROUNAMES --filename=FILENAME
             key gen (ssh | pem) [--filename=FILENAME] [--nopass] [--set_path] [--force]
             key reformat (ssh | pem) [--filename=FILENAME] [--format=FORMAT]
                                      [--nopass] [--pub]
             key verify (ssh | pem) [--filename=FILENAME] [--pub] [--check_pass]

           Arguments:
             VMS        Parameterized list of virtual machines
             CLOUDS     The clouds
             NAME       The name of the key.
             SOURCE     db, ssh, all
             OUTPUT     The format of the output (table, json, yaml)
             FILENAME   The filename with full path in which the key is located
             FORMAT     Desired key format (SubjectInfo, SSH, OpenSSL, PKCS8)

           Options:
              --dir=DIR             the directory with keys [default: ~/.ssh]
              --check_pass          Flag where program query user for password
              --filename=FILENAME   the name and full path to the file
              --nopass              Flag indicating if the key has no password
              --output=OUTPUT       the format of the output [default: table]
              --pub                 Indicates that the public key is passed in
              --set_path            Sets the cloudmesh encryption key path to
                                    the full path of the generated keys
              --source=SOURCE       the source for the keys
              --username=USERNAME   the source for the keys [default: none]


           Description:

               Please note that some values are read from the cloudmesh.yaml
               file. One such value is cloudmesh.profile.user

               Management of public keys is an essential component of accessing
               virtual machines in the cloud. There are a number of sources
               where you can find public keys. This includes the ~/.ssh
               directory and for example github. If you do not already have a
               public-private key pair they can be generated using cloudmesh

               key gen ssh 
                   This will create the public-private keypair of ~/.ssh/id_rsa
                   and ~/.ssh/id_rsa.pub in OpenSSH format

               key gen pem 
                   This will create the public-private keypair of ~/.ssh/id_rsa
                   and ~/.ssh/id_rsa.pub in PEM format

               key gen (ssh | pem) --filename=~/.cloudmesh/foobar
                   This will generate the public-private key pair of 
                   ~/.cloudmesh/foobar and ~/.cloudmesh/foobar.pub

               key gen (ssh | pem) --filename=~/.cloudmesh/foobar --set_path
                   This will generate the keys as stated above, but it will
                   also set cloudmesh to use these keys for encryption.

               Keys can also be verified for their formatting and passwords.
               By default cloudmesh checks ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub
               If the key is password protected the formatting can only be
               verified if the password is provided (--check_pass argument)

               key verify pem
                   Verifies that ~/.ssh/id_rsa has PEM format

               key verify ssh --pub
                   Verifies that ~/.ssh/id_rsa.pub has OpenSSH format

               key verify pem --filename=~/.cloudmesh/foobar
                   Verifies if the private key located at ~/.cloudmesh/foobar
                   is password protected

               key verify pem --filenam=~/.cloudmesh/foobar --check_pass
                   Request the password to the file, then checks if the
                   key is in proper PEM format

               You may find the need to keep the values of your keys but
               different encodings or formats. These aspects of your key can
               also be changed using cloudmesh.

               key reformat pem
                   Will reformat the ~/.id_rsa.pub key from PEM to OpenSSH

               key reformat ssh
                   Will reformat the ~/.id_rsa.pub key from OpenSSH to PEM

               key reformat --filename=~/.id_rsa --format=PKCS8
                   Will reformat the private key to PKCS8 format

               Keys will be uploaded into cloudmesh database with the add
               command under the given NAME. If the name is not specified the name
               cloudmesh.profile.user is assumed.

                key add NAME  --source=ssh
                    adds the default key in ~/.ssh/id_rsa.pub
                key add NAME  --source=FILENAME
                    adds the key specified by the filename with the given name
                key add NAME --git --username=username
                    adds a named github key from a user with the given github
                    username.

                key set
                    adds the ~/.ssh/id_rsa.pub key with the name specified in
                    cloudmesh.profile.user.
                    It also sets the variable key to that user.


               Once the keys are uploaded to github, they can be listed
               To list these keys the following list functions are provided.

                key list --source=git  [--username=USERNAME]
                    lists all keys in git for the specified user. If the
                    name is not specified it is read from cloudmesh.yaml
                key list --source=ssh  [--dir=DIR] [--output=OUTPUT]
                    lists all keys in the directory. If the directory is not
                    specified the default will be ~/.ssh

                key list NAMES
                    lists all keys in the named virtual machines.


                List command can use the [--output=OUTPUT] option

                    list the keys loaded to cloudmesh in the given format:
                    json, yaml, table. table is default. The NAME can be
                    specified and if omitted the name cloudmesh.profile.user
                    is assumed.

                To get keys from the cloudmesh database the following commands
                are available:

                key delete NAMES
                    deletes the Named keys. This may also have an impact on groups
                key rename NAME NEW
                    renames the key from NAME to NEW in the cloudmesh database.

               Group management of keys is an important concept in cloudmesh,
               allowing multiple users to be added to virtual machines while
               managing the keys associated with them. The keys must be uploaded
               to cloudmesh database with a name so they can be used in a
               group. The --dryrun option executes the command without
               uploading the information to the clouds. If no group name is
               specified the group name default is assumed. If no cloudnamesh
               are specified, all active clouds are assumed. active clouds
               can be set in the cloudmesh.yaml file.

                key group delete [GROUPNAMES] [NAMES] [--dryrun]
                    deletes the named keys from the named groups.

                key group list [GROUPNAMES] [--output=OUTPUT]
                    list the key names and details in the group.

                key group upload [GROUPNAMES] [CLOUDS] [--dryrun]
                    uploads the named groups to the specified clouds.

               In some cases you may want to store the public keys in files. For
               this reason we support the following commands.

                key group add --group=GROUPNAME --file=FILENAME
                    the command adds the keys to the given group. The keys are
                    written in the files in yaml format.


                key group export --group=GROUNAMES --filename=FILENAME
                    the command exports the keys to the given group. The keys are
                    written in the files in yaml format.


                The yaml format is as follows:

                cloudmesh:
                  keys:
                    NAMEOFKEY:
                      name: NAMEOFKEY
                      key: ssh-rsa AAAA..... comment
                      group:
                      - GROUPNAME
                    ...

                If a key is included in multiple groups they will be added
                to the grouplist of the key
        """
        def print_keys(keys):
            print(
                Printer.write(
                    keys,
                    sort_keys=["name"],
                    order=["name", "type", "fingerprint", "comment"],
                    header=["Name", "Type", "Fingerprint", "Comment"],
                    output=arguments.output))

        map_parameters(arguments, 'check_pass', 'cloud', 'dir', 'dryrun',
                       'filename', 'force', 'format', 'name', 'nopass',
                       'output', 'pub', 'pwd', 'set_path', 'source')

        variables = Variables()

        if arguments.list and arguments.source == "git":

            config = Config()
            username = config["cloudmesh.profile.github"]
            keys = SSHkey().get_from_git(username)

            print_keys(keys)

            return ""

        elif arguments.list and arguments.source == "ssh":
            # this is much simpler

            sshkey = SSHkey()

            print_keys([sshkey])

            return ""

        elif arguments.list and arguments.cloud:

            clouds = Parameter.expand(arguments.cloud)

            if len(clouds) == 0:
                variables = Variables()
                cloudname = variables['cloud']
                clouds = [cloudname]
            keys = []

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                keys = provider.keys()

                provider.Print(keys, output=arguments.output, kind="key")

            return ""

        elif arguments.list:

            cloud = "local"
            db = CmDatabase()
            keys = db.find(collection=f"{cloud}-key")

            print_keys(keys)

            return ""

        elif arguments.add:
            """
            key add [NAME] [--source=FILENAME] # NOT IMPLEMENTED YET
            key add [NAME] [--source=git]
            key add [NAME] [--source=ssh]
            """

            key = Key()

            if arguments["--source"] == "ssh":
                name = arguments.NAME or "ssh"
                key.add(name, "ssh")
            elif arguments["--source"] == "git":
                name = arguments.NAME or "git"
                key.add("git", "git")
            else:
                config = Config()
                name = config["cloudmesh.profile.user"]
                kind = "ssh"
                key.add(name, kind)

        elif arguments.init:
            """
            key init 
            """

            config = Config()
            username = config["cloudmesh.profile.user"]

            if username == "TBD":
                Console.error(
                    "Please set cloudmesh.profile.user in ~/.cloudmesh.yaml")
                u = os.environ["USER"].lower().replace(" ", "")
                Console.msg(
                    f"To change it you can use the command. Define a NAME such as '{u}' e.g."
                )
                Console.msg("")
                Console.msg(f"  cms config set cloudmesh.profile.user={u}")
                Console.msg("")
                return ""

            key = Key()

            key.add(username, "ssh")
            variables['key'] = username

        elif arguments.upload:
            """
            key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
            key upload [NAMES] [VMS] [--dryrun]
            """

            names = Parameter.expand(arguments.NAMES)

            # this may have a bug if NAMES is ommitted

            #
            # Step 0. Set keyname to variable
            #

            if names is None or len(names) == 0:
                config = Config()
                username = config["cloudmesh.profile.user"]
                names = [username]

            if len(names) == 1:
                name = names[0]
                variables = Variables()
                if "key" in variables:
                    old = variables["key"]
                    if old != name:
                        Console.msg(
                            f"Changing default key from {old} to {name}")
                        variables["key"] = name

            #
            # Step 1. keys = find keys to upload
            #

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            keys = []
            for key in db_keys:
                if key["name"] in names:
                    keys.append(key)

            if len(keys) == 0:
                Console.error(
                    f"No keys with the names {names} found in cloudmesh. \n"
                    "       Use the command 'key add' to add the key.")

            #
            # Step 2. iterate over the clouds to upload
            #

            clouds, vmnames = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                for key in db_keys:
                    name = key['name']
                    if name in names:
                        try:
                            r = provider.key_upload(key)
                            Console.ok(f"upload key '{name} successful'. ")
                        except ValueError as e:
                            Console.error(
                                f"key '{name} already exists in {cloud}.")

            return ""

        elif arguments.delete and arguments.cloud and arguments.NAMES:

            # key delete NAMES --cloud=CLOUDS [--dryrun]
            names = Parameter.expand(arguments.NAMES)
            clouds = Parameter.expand(arguments.cloud)

            for cloud in clouds:
                provider = Provider(name=cloud)
                for name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name} in {cloud}")
                    else:
                        images = provider.key_delete(name)

            return ""

        elif arguments.gen:
            """
            key gen (ssh | pem) [--filename=FILENAME] [--nopass] [--set_path]
                    [--force]
            Generate an RSA key pair with pem or ssh encoding for the public
            key. The private key is always encoded as a PEM file.
            """
            config = Config()

            # Check if password will be requested
            ap = not arguments.nopass

            if not ap:
                Console.warning("Private key will NOT have a password")
                cnt = yn_choice(message="Continue, despite risk?", default="N")
                if not cnt:
                    sys.exit()

            # Discern the name of the public and private keys
            rk_path = None
            uk_path = None
            if arguments.filename:
                fp = path_expand(arguments.filename)
                fname, fext = os.path.splitext(fp)
                if fext == ".pub" or fext == ".ssh":
                    rk_path = fname
                    uk_path = fp
                elif fext == ".priv" or fext == ".pem":
                    rk_path = fp
                    uk_path = fname + ".pub"
                else:
                    rk_path = fp
                    uk_path = rk_path + ".pub"
            else:
                rk_path = path_expand("~/.ssh/id_rsa")
                uk_path = rk_path + ".pub"

            # Check if the file exist, if so confirm overwrite
            def check_exists(path):
                if os.path.exists(path):
                    Console.info(f"{path} already exists")
                    ovwr_r = yn_choice(message=f"overwrite {path}?",
                                       default="N")
                    if not ovwr_r:
                        Console.info(f"Not overwriting {path}. Quitting")
                        sys.exit()

            if not arguments.force:
                check_exists(rk_path)
                check_exists(uk_path)

            # Set the path if requested
            if arguments.set_path:
                config['cloudmesh.security.privatekey'] = rk_path
                config['cloudmesh.security.publickey'] = uk_path
                config.save()

            Console.msg(f"\nPrivate key: {rk_path}")
            Console.msg(f"Public  key: {uk_path}\n")

            # Generate the Private and Public keys
            kh = KeyHandler()
            r = kh.new_rsa_key()
            u = kh.get_pub_key(priv=r)

            # Serialize and write the private key to the path
            sr = kh.serialize_key(key=r,
                                  key_type="PRIV",
                                  encoding="PEM",
                                  format="PKCS8",
                                  ask_pass=ap)

            # Force write the key (since we check file existence above)
            kh.write_key(key=sr, path=rk_path, force=True)

            # Determine the public key format and encoding
            enc = None
            forma = None
            if arguments.ssh:
                enc = "SSH"
                forma = "SSH"
            elif arguments.pem:
                enc = "PEM"
                forma = "SubjectInfo"

            # Serialize and write the public key to the path
            su = kh.serialize_key(key=u,
                                  key_type="PUB",
                                  encoding=enc,
                                  format=forma,
                                  ask_pass=False)

            # Force write the key (since we check file existence above)
            kh.write_key(key=su, path=uk_path, force=True)

            Console.ok("Success")

        elif arguments.verify:
            """
            key verify (ssh | pem) [--filename=FILENAME] [--pub] [--check_pass]
            Verifies the encoding (pem or ssh) of the key (private or public)
            """
            # Initialize variables
            kh = KeyHandler()

            # Determine filepath
            fp = None
            if arguments.filename is None:
                config = Config()
                kp = path_expand("~/.ssh/id_rsa")
                if arguments.pub:
                    fp = kp + ".pub"
                else:
                    fp = kp
            else:
                fp = arguments.filename

            # Discern key type
            kt = enc = None
            ap = True
            if arguments.pub:
                # Load the public key, if no error occurs formatting is correct
                kt, kta, ap = "public", "PUB", False
                # Discern public key encoding
                if arguments.ssh:
                    enc, e = "OpenSSH", "SSH"
                elif arguments.pem:  # PEM encoding
                    enc = e = "PEM"
            else:
                # Load the private key to verify the format and password of the
                # key file. If no error occurs the format and pwd are correct
                kt, kta = "private", "PRIV"
                enc = e = "PEM"
                ap = False
                if arguments.check_pass:
                    ap = True

            try:
                k = kh.load_key(path=fp, key_type=kta, encoding=e, ask_pass=ap)
                m = f"Success the {kt} key {fp} has proper {enc} format"
                Console.ok(m)
            except ValueError as e:
                # The formatting was incorrect
                m = f"Failure, {kt} key {fp} does not have proper {enc} format"
                Console.error(m)
                raise e
            except TypeError as e:
                # Success, we didn't ask the user for the key password and
                # we received an error for not entering the password, thus
                # the key is password protectd
                if not arguments.check_pass:
                    Console.ok("The key is password protected")
                else:
                    # Error Message handled in kh.load_key()
                    raise e

        elif arguments.reformat:
            """
            key reformat (ssh | pem) [--filename=FILENAME] [--format=FORMAT]
                                      [--nopass] [--pub]
            Restructures a key's format, encoding, and password
            """

            # Initialize variables
            kh = KeyHandler()

            # Determine key type
            fname, fext = os.path.splitext(arguments.filename)
            kt = "PRIV"
            if arguments.pub or fext == ".pub":
                kt = "PUB"

            # Determine new encoding
            use_pem = True
            if arguments.ssh:
                use_pem = False

            kh.reformat_key(path=arguments.filename,
                            key_type=kt,
                            use_pem=use_pem,
                            new_format=arguments.format,
                            ask_pass=not arguments.nopass)

        elif arguments.delete and arguments.NAMES:
            # key delete NAMES [--dryrun]

            names = Parameter.expand(arguments.NAMES)

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            error = []
            for key in db_keys:
                name = key['name']
                if name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name}")
                    else:
                        db.delete(collection="local-key", name=name)
                        Console.ok(f"delete {name}")
            return ""

        elif arguments.group:

            raise NotImplementedError

        return ""
Exemple #23
0
    def do_vm(self, args, arguments):
        """
        ::

            Usage:
                vm ping [NAMES] [--cloud=CLOUDS] [--count=N]
                vm check [NAMES] [--cloud=CLOUDS] [--username=USERNAME]
                vm status [NAMES] [--cloud=CLOUDS] [--output=OUTPUT]
                vm console [NAME] [--force]
                vm log [NAME] [--force]
                vm stop [NAMES]  [--dryrun]
                vm start [NAMES] [--dryrun]
                vm terminate [NAMES] [--cloud=CLOUD] [--dryrun]
                vm delete [NAMES] [--cloud=CLOUD] [--dryrun]
                vm refresh [--cloud=CLOUDS]
                vm list [NAMES]
                        [--cloud=CLOUDS]
                        [--output=OUTPUT]
                        [--refresh]
                vm boot [--n=COUNT]
                        [--name=VMNAMES]
                        [--cloud=CLOUD]
                        [--username=USERNAME]
                        [--image=IMAGE]
                        [--flavor=FLAVOR]
                        [--network=NETWORK]
                        [--public]
                        [--secgroup=SECGROUPs]
                        [--group=GROUPs]
                        [--key=KEY]
                        [--dryrun]
                        [-v]
                vm meta list [NAME]
                vm meta set [NAME] KEY=VALUE...
                vm meta delete [NAME] KEY...
                vm script [--name=NAMES]
                          [--username=USERNAME]
                          [--key=KEY]
                          [--dryrun]
                          [--dir=DESTINATION]
                          SCRIPT
                vm ip assign [NAMES]
                          [--cloud=CLOUD]
                vm ip show [NAMES]
                           [--group=GROUP]
                           [--cloud=CLOUD]
                           [--output=OUTPUT]
                           [--refresh]
                vm ip inventory [NAMES]
                vm ssh [NAMES]
                       [--username=USER]
                       [--quiet]
                       [--ip=IP]
                       [--key=KEY]
                       [--command=COMMAND]
                vm put SOURCE DESTINATION [NAMES]
                vm get SOURCE DESTINATION [NAMES]
                vm rename [OLDNAMES] [NEWNAMES] [--force] [--dryrun]
                vm wait [--cloud=CLOUD] [--interval=INTERVAL] [--timeout=TIMEOUT]
                vm info [--cloud=CLOUD]
                        [--output=OUTPUT]
                vm username USERNAME [NAMES] [--cloud=CLOUD]
                vm resize [NAMES] [--size=SIZE]

            Arguments:
                OUTPUT         the output format
                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 vm 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:
                -v             verbose, prints the dict at the end
                --output=OUTPUT   the output format
                -H --modify-knownhosts  Do not modify ~/.ssh/known_hosts file
                                      when ssh'ing into a machine
                --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, 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 vm 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.

                vm refresh [--cloud=CLOUDS]
                    this command refreshes the data for virtual machines,
                    images and flavors for the specified clouds.

                vm ping [NAMES] [--cloud=CLOUDS] [--count=N] [--processors=PROCESSORS]
                     pings the specified virtual machines, while using at most N pings.
                     The ping is executed in parallel.
                     If names are specifies the ping is restricted to the given names in
                     parameter format. If clouds are specified, names that are not in
                     these clouds are ignored. If the name is set in the variables
                     this name is used.

                cms vm ssh --command=\"uname -a\"

                      executes the uname command on the last booted vm

                vm script [--name=NAMES]
                          [--username=USERNAME]
                          [--key=KEY]
                          [--dryrun]
                          [--dir=DESTINATION]
                          [--shell=SHELL]
                          SCRIPT

                   The script command copies a shell script to the specified vms
                   into the DESTINATION directory and than execute it. With
                   SHELL you can set the shell for executing the command,
                   this coudl even be a python interpreter. Examples for
                   SHELL are /bin/sh, /usr/bin/env python

                vm put SOURCE DESTINATION [NAMES]

                    puts the file defined by SOURCE into the DESINATION folder
                    on the specified machines. If the file exists it is
                    overwritten, so be careful.

                vm get SOURCE DESTINATION [NAMES]

                    gets  the file defined by SOURCE into the DESINATION folder
                    on the specified machines. The SOURCE is on the remote
                    machine. If one machine is specified, the SOURCE is the same
                    name as on the remote machine. If multiple machines are
                    specified, the name of the machine will be a prefix to the
                    filename. If the filenames exists, they will be overwritten,
                    so be careful.

            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 gregor-004 --command=\"uname -a\"

            Limitations:

                Azure: rename is not supported
        """

        map_parameters(arguments, 'active', 'cloud', 'command', 'dryrun',
                       'flavor', 'force', 'group'
                       'output', 'group', 'image', 'interval', 'timeout', 'ip',
                       'key', 'modify-knownhosts', 'n', 'name', 'public',
                       'quiet', 'secgroup', 'size', 'username', 'output',
                       'count', 'network', 'refresh')

        variables = Variables()
        database = CmDatabase()

        arguments.output = Parameter.find("output", arguments, variables,
                                          "table")

        arguments.refresh = Parameter.find_bool("refresh", arguments,
                                                variables)

        if (arguments.meta and arguments.list):

            name = arguments.NAME
            if arguments.NAME is None:
                name = variables['vm']
                if name is None:
                    Console.error("No vm specified")

            cloud = "chameleon"
            # cloud = Parameter.find(arguments, variables)
            print(f"vm metadata for {name} on {cloud}")

            provider = Provider(name=cloud)
            r = provider.get_server_metadata(name)
            print(r)

        elif arguments.meta and arguments.set:

            metadata = {}
            pairs = arguments['KEY=VALUE']
            for pair in pairs:
                key, value = pair.split("=", 1)
                metadata[key] = value

            name = arguments.NAME
            if arguments.NAME is None:
                name = variables['vm']
                if name is None:
                    Console.error("No vm specified")

            cloud = "chameleon"
            # cloud = Parameter.find(arguments, variables)
            print(f"cloud {cloud} {name}")

            provider = Provider(name=cloud)
            provider.set_server_metadata(name, **metadata)
            r = provider.get_server_metadata(name)

            pprint(r)

        elif arguments.meta and arguments.delete:

            metadata = {}
            keys = arguments['KEY']

            name = arguments.NAME
            if arguments.NAME is None:
                name = variables['vm']
                if name is None:
                    Console.error("No vm specified")

            cloud = "chameleon"
            # cloud = Parameter.find(arguments, variables)
            print(f"cloud {cloud} {name}")

            provider = Provider(name=cloud)

            for key in keys:
                provider.delete_server_metadata(name, key)

            r = provider.get_server_metadata(name)

            pprint(r)

        elif arguments.list and arguments.refresh:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                vms = provider.list()

                provider.Print(vms, output=arguments.output, kind="vm")

                return ""

        elif arguments.list:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            try:

                for cloud in clouds:
                    print(f"List {cloud}")
                    p = Provider(cloud)
                    kind = p.kind

                    collection = "{cloud}-vm".format(cloud=cloud, kind=p.kind)
                    db = CmDatabase()
                    vms = db.find(collection=collection)

                    p.Print(vms, output=arguments.output, kind="vm")

            except Exception as e:
                Console.error("Error in listing ", traceflag=True)
                VERBOSE(e)

            return ""

        elif arguments.ping:
            """
            vm ping [NAMES] [--cloud=CLOUDS] [--count=N]
            """
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names(
                "status", arguments, variables)

            count = arguments.count
            if arguments.count:
                count = int(count)
            else:
                count = 1

            def get_ips():
                ips = []
                for cloud in clouds:
                    params = {}
                    # gets public ips from database
                    cursor = database.db[f'{cloud}-vm']
                    for name in names:
                        for node in cursor.find({'name': name}):
                            ips.append(node['ip_public'])
                    ips = list(set(ips))
                    pprint(ips)
                return ips

            ips = get_ips()
            if len(ips) == 0:
                Console.warning("no public ip found.")
                for cloud in clouds:
                    print(f"refresh for cloud {cloud}")
                    provider = Provider(name=cloud)
                    vms = provider.list()
                ips = get_ips()

            if len(ips) == 0:
                Console.error("No vms with public IPS found.")
                Console.error("  Make sure to use cms vm list --refresh")

            for ip in ips:
                result = Shell.ping(host=ip, count=count)
                banner(ip)
                print(result)
                print()

        elif arguments.check:

            raise NotImplementedError
            """
            vm check [NAMES] [--cloud=CLOUDS] [--username=USERNAME]
            """
            """
            
            THIS IS ALL WRONG AS PROVIDER DEPENDENT !!!
            
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names("status", arguments, variables)

            for cloud in clouds:
                provider = Provider(cloud)
                params = {}

                params['key'] = \
                    provider.p.spec["credentials"]['EC2_PRIVATE_KEY_FILE_PATH'] + \
                    provider.p.spec["credentials"]['EC2_PRIVATE_KEY_FILE_NAME']

                params['username'] = arguments['--username']  # or get from db

                processors = arguments['--processors']
                if processors:
                    params['processors'] = int(processors[0])

                # gets public ips from database
                public_ips = []
                cursor = database.db['{cloud}-vm']
                for name in names:
                    for node in cursor.find({'name': name}):
                        public_ips.append(node['public_ips'])
                public_ips = [y for x in public_ips for y in x]

                Host.check(hosts=public_ips, **params)
            """

        elif arguments.status:
            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names(
                "status", arguments, variables)

            # gets status from database
            for cloud in clouds:
                provider = Provider(cloud)
                status = []
                cursor = database.db[f'{cloud}-vm']
                print(cloud)
                for name in names:
                    for node in cursor.find({'name': name}):
                        status.append(node)

                provider.Print(status, output=arguments.output, kind="status")
                return ""

        elif arguments.start:
            # TODO: not tested
            if arguments.NAMES:
                names = variables['vm'] = arguments.NAMES

            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names(
                "stop", arguments, variables)

            cloud = clouds[0]
            print(cloud)
            print(names)

            for name in names:

                provider = Provider(cloud)

                if arguments['--dryrun']:
                    print(f"start node {name}")
                else:
                    vms = provider.start(name=name, cloud=cloud)

                    provider.Print(vms, output=arguments.output, kind="vm")

            return ""

        elif arguments.stop:
            # TODO: not tested

            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names(
                "stop", arguments, variables)

            for cloud in clouds:
                params = {}
                provider = Provider(cloud)

                if arguments['--dryrun']:
                    Console.ok(f"Dryrun stop: "
                               f"        {cloud}\n"
                               f"        {names}"
                               f"        {provider}")
                else:
                    for name in names:
                        vms = provider.stop(name)

                    provider.Print(vms, output=arguments.output, kind="vm")

        elif arguments.terminate:
            # TODO: not tested

            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names(
                "stop", arguments, variables)

            for cloud in clouds:
                params = {}
                provider = Provider(cloud)

                if arguments['--dryrun']:
                    Console.ok(f"Dryrun terminate: "
                               f"        {cloud}\n"
                               f"        {names}"
                               f"        {provider}")
                else:
                    for name in names:
                        vms = provider.destroy(name)

                    provider.Print(vms, output=arguments.output, kind="vm")

        elif arguments.delete:

            if arguments.NAMES:
                variables['vm'] = arguments.NAMES
            if arguments['--cloud']:
                variables['cloud'] = arguments['--cloud']
            clouds, names = Arguments.get_cloud_and_names(
                "stop", arguments, variables)

            if names is not None:
                pass
            elif clouds is not None:
                for cloud in clouds:
                    provider = Provider(cloud)
                    vms = provider.list()
                    for vm in vms:
                        r = provider.destroy(name=vm)
                return ""
            else:
                return ""

            for cloud in clouds:
                provider = Provider(cloud)
                vms = provider.list()
                for vm in vms:
                    name = vm["cm"]["name"]
                    if name in names:
                        r = provider.destroy(name=name)

        # TODO: username, secgroup
        elif arguments.boot:
            # not everything works
            """
                vm boot 
                        [--name=VMNAMES]
                        [--cloud=CLOUD]
                        [--username=USERNAME]
                        [--image=IMAGE]
                        [--flavor=FLAVOR]
                        [--network=NETWORK]
                        [--public]
                        [--secgroup=SECGROUP]
                        [--key=KEY]
                        [--group=GROUP]
                        [--dryrun]
            """
            # for name in names:
            #    node = p.create(name=name, size=flavor, image=image)

            # VERBOSE(arguments)
            parameters = dotdict()

            names = Parameter.expand(arguments.name)

            cloud = Parameter.find("cloud", arguments, variables.dict())
            defaults = Config()[f"cloudmesh.cloud.{cloud}.default"]
            groups = Parameter.find("group", arguments, variables.dict(),
                                    {"group": "default"})

            parameters = dotdict()

            # parameters.names = arguments.name

            parameters.group = groups
            for attribute in [
                    "image", "username", "flavor", "key", "network", "secgroup"
            ]:
                parameters[attribute] = Parameter.find(attribute, arguments,
                                                       variables.dict(),
                                                       defaults)

            if arguments.username is None:
                parameters.user = Image.guess_username(parameters.image)

            provider = Provider(name=cloud)

            parameters.secgroup = arguments.secgroup or "default"

            #
            # determine names
            #

            if names and arguments.n and len(names) > 1:
                Console.error(
                    f"When using --n={arguments.n}, you can only specify one name"
                )
                return ""
            # cases
            #

            # only name --name = "a[1,2]"
            # name and count # --name="a" --n=3, names must be of length 1
            # only count --n=2 names are read form var
            # nothing, just use one vm

            # determin names
            _names = []
            if not names:

                if not arguments.n:
                    count = 1
                else:
                    count = int(arguments.n)

                for i in range(0, count):
                    if names is None:
                        n = Name()
                        n.incr()
                        name = str(n)
                    else:
                        n = names[i]
                        name = str(n)
                    _names.append(name)
                names = _names

            elif len(names) == 1 and arguments.n:

                name = names[0]
                for i in range(0, int(arguments.n)):
                    _names.append(f"{name}-{i}")
                names = _names

            # pprint(parameters)

            for name in names:

                parameters.name = name
                if arguments['--dryrun']:
                    banner("boot")

                    pprint(parameters)

                    Console.ok(f"Dryrun boot {name}: \n"
                               f"        cloud={cloud}\n"
                               f"        names={names}\n"
                               f"        provider={provider}")
                    print()
                    for attribute in parameters:
                        value = parameters[attribute]
                        Console.ok(f"        {attribute}={value}")

                else:

                    # parameters.progress = len(parameters.names) < 2

                    try:
                        vms = provider.create(**parameters)
                    except TimeoutError:
                        Console.error(
                            f"Timeout during vm creation. There may be a problem with the cloud {cloud}"
                        )

                    except Exception as e:
                        Console.error("create problem", traceflag=True)
                        print(e)
                        return ""

                    variables['vm'] = str(n)
                    if arguments["-v"]:
                        banner("Details")
                        pprint(vms)

            # provider.Print(arguments.output, "vm", vms)

        elif arguments.info:
            """
            vm info [--cloud=CLOUD] [--output=OUTPUT]
            """
            print("info for the vm")

            cloud, names = Arguments.get_cloud_and_names(
                "info", arguments, variables)

            raise NotImplementedError

        elif arguments.rename:
            raise NotImplementedError
            # Not tested
            print("rename the vm")

            v = Variables()
            cloud = v["cloud"]

            p = Provider(cloud)

            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",
                                  traceflag=False)
                else:
                    print(oldnames)
                    print(newnames)
                    for i in range(0, len(oldnames)):
                        oldname = oldnames[i]
                        newname = newnames[i]
                        if arguments["--dryrun"]:
                            Console.ok("Rename {} to {}".format(
                                oldname, newname))
                        else:
                            print(f"rename {oldname} -> {newname}")

                            p.rename(source=oldname, destination=newname)

                    msg = "info. OK."
                    Console.ok(msg)
            except Exception as e:
                Error.traceback(e)
                Console.error("Problem renaming instances", traceflag=True)

        elif arguments["ip"] and arguments["show"]:
            raise NotImplementedError

            print("show the ips")
            """
            vm ip show [NAMES]
                   [--group=GROUP]
                   [--cloud=CLOUD]
                   [--output=OUTPUT]
                   [--refresh]

            """

        elif arguments["ip"] and arguments["assign"]:
            raise NotImplementedError
            """
            vm ip assign [NAMES] [--cloud=CLOUD]
            """
            print("assign the public ip")

        elif arguments["ip"] and arguments["inventory"]:
            raise NotImplementedError
            """
            vm ip inventory [NAMES]

            """
            print("list ips that could be assigned")

        elif arguments.default:
            raise NotImplementedError

            print("sets defaults for the vm")

        elif arguments.script:
            raise NotImplementedError
            clouds, names = Arguments.get_cloud_and_names(
                "run", arguments, variables)
            username = arguments['--username']
            script = arguments.SCRIPT

            for cloud in clouds:
                provider = Provider(cloud)

                name_ips = {}
                cursor = database.db['{}-node'.format(cloud)]
                for name in names:
                    for node in cursor.find({'name': name}):
                        name_ips[name] = node['public_ips']

                if arguments['--dryrun']:
                    print("run script {} on vms: {}".format(script, names))
                else:
                    provider.ssh(name_ips, username=username, script=script)

        elif arguments.username:
            raise NotImplementedError
            """
            vm username USERNAME [NAMES] [--cloud=CLOUD]
            """
            print("sets the username for the vm")

        elif arguments.resize:
            raise NotImplementedError
            """
            vm resize [NAMES] [--size=SIZE]
            """
            pass

        elif arguments.ssh:
            """
            vm ssh [NAMES] [--username=USER]
                 [--quiet]
                 [--ip=IP]
                 [--key=KEY]
                 [--command=COMMAND]
            """

            # VERBOSE(arguments)
            clouds, names, command = Arguments.get_commands(
                "ssh", arguments, variables)

            # print (clouds)
            # print(names)
            # print (command)

            if arguments.command is None and len(names) > 1:
                Console.error("Interactive shell can only be done on one vm")
                return ""
            elif arguments.command is None and len(names) == 1:
                name = names[0]
                cloud = clouds[0]
                cm = CmDatabase()
                try:
                    vm = cm.find_name(name, "vm")[0]
                except IndexError:
                    Console.error(f"could not find vm {name}")
                    return ""
                # VERBOSE(vm)
                cloud = vm["cm"]["cloud"]
                provider = Provider(name=cloud)
                try:
                    provider.ssh(vm=vm)
                except KeyError:
                    vms = provider.list()

                    provider.Print(vms, output=arguments.output, kind="vm")

                    provider.ssh(vm=vm)
                return ""
            else:
                # command on all vms

                if clouds is None or names is None or command is None:
                    return ""
                else:
                    for cloud in clouds:
                        p = Provider(cloud)
                        for name in names:
                            cm = CmDatabase()
                            try:
                                vm = cm.find_name(name, "vm")[0]
                            except IndexError:
                                Console.error(f"could not find vm {name}")
                                continue
                            r = p.ssh(vm=vm, command=command)
                            print(r)
            return ""

        elif arguments.console:

            # why is this not vm
            clouds, names, command = Arguments.get_commands(
                "ssh", arguments, variables)

            print(clouds)
            print(names)
            print(command)

            for cloud in clouds:
                p = Provider(cloud)
                for name in names:
                    cm = CmDatabase()
                    try:
                        vm = cm.find_name(name, "vm")[0]
                    except IndexError:
                        Console.error(f"could not find vm {name}")
                        continue
                    r = p.console(vm=vm)
                    print(r)

            return ""

        elif arguments.log:

            # why is this not vm
            clouds, names, command = Arguments.get_commands(
                "ssh", arguments, variables)

            print(clouds)
            print(names)
            print(command)

            for cloud in clouds:
                p = Provider(cloud)
                for name in names:
                    cm = CmDatabase()
                    try:
                        vm = cm.find_name(name, "vm")[0]
                    except IndexError:
                        Console.error(f"could not find vm {name}")
                        continue
                    r = p.log(vm=vm)
                    print(r)

            return ""

        elif arguments.wait:
            """
            vm wait [--cloud=CLOUD] [--interval=INTERVAL] [--timeout=TIMEOUT]
            """

            # why is this not vm
            clouds, names, command = Arguments.get_commands(
                "ssh", arguments, variables)

            # print (clouds)
            # print (names)
            # print (command)

            for cloud in clouds:
                p = Provider(cloud)
                for name in names:
                    cm = CmDatabase()
                    try:
                        vm = cm.find_name(name, "vm")[0]
                    except IndexError:
                        Console.error(f"could not find vm {name}")
                        continue
                    r = p.wait(vm=vm,
                               interval=arguments.interval,
                               timeout=arguments.timeout)
                    if r:
                        Console.ok("Instance available for SSH")
                    else:
                        Console.error(
                            f"Instance unavailable after timeout of {arguments.timeout}"
                        )
                    # print(r)

            return ""

        elif arguments.put:
            """
            vm put SOURCE DESTINATION
            """
            clouds, names, command = Arguments.get_commands(
                "ssh", arguments, variables)

            key = variables['key']

            source = arguments['SOURCE']
            destination = arguments['DESTINATION']
            for cloud in clouds:
                p = Provider(name=cloud)
                cm = CmDatabase()
                for name in names:
                    try:
                        vms = cm.find_name(name, "vm")
                    except IndexError:
                        Console.error(f"could not find vm {name}")
                        return ""
                    # VERBOSE(vm)
                    for vm in vms:
                        try:
                            ip = vm['public_ips']
                        except:
                            try:
                                ip = p.get_public_ip(name=name)
                            except:
                                Console.error(
                                    f"could not find a public ip for vm {name}",
                                    traceflag=True)
                                return
                            Console.error(
                                f"could not find a public ip for vm {name}",
                                traceflag=True)
                            return

                        # get the username
                        try:
                            # username not in vm...guessing
                            imagename = list(
                                cm.collection(cloud + '-image').find(
                                    {'ImageId': vm['ImageId']}))[0]['name']
                            print(imagename)
                            user = Image.guess_username(image=imagename,
                                                        cloud=cloud)
                        except:
                            try:
                                user = vm['os_profile']['admin_username']
                            except:
                                Console.error(
                                    f"could not find a valid username for "
                                    f"{name}, try refreshing the image list",
                                    traceflag=True)
                                return
                            Console.error(
                                f"could not find a valid username for {name}, try refreshing the image list"
                            )
                            return

                        cmd = f'scp -i {key} {source} {user}@{ip}:{destination}'
                        print(cmd)
                        os.system(cmd)
            return ""
Exemple #24
0
class Provider(VolumeABC):
    kind = "oracle"

    sample = """
    cloudmesh:
      volume:
        {name}:
          cm:
            active: true
            heading: {name}
            host: TBD
            label: {name}
            kind: oracle
            version: TBD
            service: volume
          credentials:
            version: TBD
            user: TBD
            fingerprint: TBD
            key_file: oci_api_key.pem
            pass_phrase: TBD
            tenancy: TBD
            compartment_id: TBD
            region: TBD
            availability_domain: TBD
          default:
    """

    output = {

        "volume": {
            "sort_keys": ["cm.name"],
            "order": ["cm.name",
                      "cm.cloud",
                      "cm.kind",
                      "availability_domain",
                      "time_created",
                      "size_in_gbs",
                      "lifecycle_state",
                      "id"
                      ],
            "header": ["Name",
                       "Cloud",
                       "Kind",
                       "Availability Zone",
                       "Created At",
                       "Size(Gb)",
                       "Status",
                       "Id"
                       ],
        }
    }

    def update_dict(self, results):
        """
        This function adds a cloudmesh cm dict to each dict in the list
        elements.
        Libcloud
        returns an object or list of objects With the dict method
        this object is converted to a dict. Typically this method is used
        internally.

        :param results: the original dicts.
        :return: The list with the modified dicts
        """

        if results is None:
            return None

        d = []

        for entry in results:
            display_name = entry.__getattribute__("display_name")
            availability_domain = entry.__getattribute__("availability_domain")
            time_created = entry.__getattribute__("time_created")
            size_in_gbs = entry.__getattribute__("size_in_gbs")
            lifecycle_state = entry.__getattribute__("lifecycle_state")
            attribute_id = entry.__getattribute__("id")

            entry = {
                "availability_domain": availability_domain,
                "time_created": time_created,
                "size_in_gbs": size_in_gbs,
                "id": attribute_id,
                "lifecycle_state": lifecycle_state
            }

            if "cm" not in entry:
                entry['cm'] = {}

            entry["cm"].update({
                "cloud": self.cloud,
                "kind": "volume",
                "name": display_name,
            })
            d.append(entry)

        return d

    def __init__(self, name):
        """
        Initialize provider. The default parameters are read from the
        configuration file that is defined in yaml format.

        :param name: name of cloud
        """
        self.cloud = name
        self.config = Config()["cloudmesh.volume.oracle.credentials"]
        self.defaults = Config()["cloudmesh.volume.oracle.default"]
        self.cm = CmDatabase()

    def get_volume_id_from_name(self, block_storage, name):
        """
        This function get volume id from volume name

        :param block_storage: Block storage client object
        :param name: volume name
        :return: volume id
        """
        v = block_storage.list_volumes(self.config['compartment_id'])
        results = v.data
        volume_id = None
        for entry in results:
            display_name = entry.__getattribute__("display_name")
            if name == display_name:
                volume_id = entry.__getattribute__("id")
                break
        return volume_id

    def get_attachment_id_from_name(self, block_storage, name):
        """
        This function get attachment id from volume name

        :param block_storage: Block storage client object
        :param name: Name of the volume
        :return: Volume attachment id
        """
        v = block_storage.list_volumes(self.config['compartment_id'])
        results = v.data
        attachment_id = None
        for entry in results:
            display_name = entry.__getattribute__("display_name")
            if name == display_name:
                tags = entry.__getattribute__("freeform_tags")
                attachment_id = tags['attachment_id']
                break
        return attachment_id

    def status(self, name):
        """
        This function get volume status, such as "in-use", "available"

        :param name: Volume name
        :return: Volume_status
        """
        try:
            block_storage = oci.core.BlockstorageClient(self.config)
            v = block_storage.list_volumes(self.config['compartment_id'])
            volumes = v.data
            result = []
            entry = None
            for entry in volumes:
                display_name = entry.__getattribute__("display_name")
                if name == display_name:
                    break
            result.append(entry)
            result = self.update_dict(result)
        except Exception as e:
            Console.error("Problem finding status", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def list(self, **kwargs):
        """
        This function list all volumes as following:
        If NAME (volume_name) is specified, it will print out info of NAME
        If NAME (volume_name) is not specified, it will print out info of all
          volumes

        :param kwargs: contains name of volume
        :return: Dictionary of volumes
        """
        try:
            if kwargs and kwargs['refresh'] is False:
                result = self.cm.find(cloud=self.cloud, kind='volume')
                for key in kwargs:
                    if key == 'NAME' and kwargs['NAME']:
                        result = self.cm.find_name(name=kwargs['NAME'])
                    elif key == 'NAMES' and kwargs['NAMES']:
                        result = self.cm.find_names(names=kwargs['NAMES'])
            else:
                block_storage = oci.core.BlockstorageClient(self.config)
                if kwargs and kwargs['NAME']:
                    v = block_storage.list_volumes(
                        self.config['compartment_id'])
                    results = v.data
                    entry = None
                    for entry in results:
                        display_name = entry.__getattribute__("display_name")
                        if kwargs["NAME"] == display_name:
                            break
                    result = [entry]
                    result = self.update_dict(result)
                else:
                    v = block_storage.list_volumes(
                        self.config['compartment_id'])
                    results = v.data
                    result = self.update_dict(results)
        except Exception as e:
            Console.error("Problem listing volume", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def create(self, **kwargs):
        """
        This function creates a new volume with default size of 50gb.
        Default parameters are read from self.config.

        :param kwargs: Contains Volume name
        :return: Volume dictionary
        """
        try:
            arguments = dotdict(kwargs)
            block_storage = oci.core.BlockstorageClient(self.config)
            result = block_storage.create_volume(
                oci.core.models.CreateVolumeDetails(
                    compartment_id=self.config['compartment_id'],
                    availability_domain=self.config['availability_domain'],
                    display_name=arguments.NAME
                ))
            # wait for availability of volume
            oci.wait_until(
                block_storage,
                block_storage.get_volume(result.data.id),
                'lifecycle_state',
                'AVAILABLE'
            ).data

            v = block_storage.list_volumes(self.config['compartment_id'])
            results = v.data
            result = self.update_dict(results)
        except Exception as e:
            Console.error("Problem creating volume", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def attach(self, names=None, vm=None):
        """
        This function attaches a given volume to a given instance

        :param names: Names of Volumes
        :param vm: Instance name
        :return: Dictionary of volumes
        """
        try:
            compute_client = oci.core.ComputeClient(self.config)
            # get instance id from VM name
            i = compute_client.list_instances(self.config['compartment_id'])
            instances = i.data
            instance_id = None
            for entry in instances:
                display_name = entry.__getattribute__("display_name")
                if vm == display_name:
                    instance_id = entry.__getattribute__("id")
                    break

            # get volumeId from Volume name
            block_storage = oci.core.BlockstorageClient(self.config)
            volume_id = self.get_volume_id_from_name(block_storage, names[0])
            # attach volume to vm
            a = compute_client.attach_volume(
                oci.core.models.AttachIScsiVolumeDetails(
                    display_name='IscsiVolAttachment',
                    instance_id=instance_id,
                    volume_id=volume_id
                )
            )

            # tag volume with attachment id. This needed during detach.
            block_storage.update_volume(
                volume_id,
                oci.core.models.UpdateVolumeDetails(
                    freeform_tags={'attachment_id': a.data.id},
                ))

            # wait until attached
            oci.wait_until(
                compute_client,
                compute_client.get_volume_attachment(
                    a.data.id),
                'lifecycle_state',
                'ATTACHED'
            )
            # return result after attach
            v = block_storage.list_volumes(self.config['compartment_id'])
            results = v.data
            results = self.update_dict(results)
        except Exception as e:
            Console.error("Problem attaching volume", traceflag=True)
            print(e)
            raise RuntimeError
        return results

    def detach(self, name=None):
        """
        This function detaches a given volume from an instance

        :param name: Volume name
        :return: Dictionary of volumes
        """
        try:
            compute_client = oci.core.ComputeClient(self.config)
            block_storage = oci.core.BlockstorageClient(self.config)
            attachment_id = self.get_attachment_id_from_name(block_storage,
                                                             name)
            compute_client.detach_volume(attachment_id)
            # wait for detachment
            oci.wait_until(
                compute_client,
                compute_client.get_volume_attachment(attachment_id),
                'lifecycle_state',
                'DETACHED'
            )
            # return result after detach
            v = block_storage.list_volumes(self.config['compartment_id'])
            results = v.data
            results = self.update_dict(results)
        except Exception as e:
            Console.error("Problem detaching volume", traceflag=True)
            print(e)
            raise RuntimeError
        return results[0]

    def delete(self, name=None):
        """
        This function delete one volume.

        :param name: Volume name
        :return: Dictionary of volumes
        """
        try:
            block_storage = oci.core.BlockstorageClient(self.config)
            volume_id = self.get_volume_id_from_name(block_storage, name)
            if volume_id is not None:
                block_storage.delete_volume(volume_id=volume_id)
                # wait for termination
                oci.wait_until(
                    block_storage,
                    block_storage.get_volume(volume_id),
                    'lifecycle_state',
                    'TERMINATED'
                ).data
            v = block_storage.list_volumes(self.config['compartment_id'])
            results = v.data
            result = self.update_dict(results)
        except Exception as e:
            Console.error("Problem deleting volume", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def add_tag(self, **kwargs):
        """
        This function add tag to a volume.

        :param kwargs:
                    NAME: name of volume
                    key: name of tag
                    value: value of tag
        :return: Dictionary of volume
        """
        try:
            name = kwargs['NAME']
            key = kwargs['key']
            value = kwargs['value']
            block_storage = oci.core.BlockstorageClient(self.config)
            volume_id = self.get_volume_id_from_name(block_storage, name)
            block_storage.update_volume(
                volume_id,
                oci.core.models.UpdateVolumeDetails(
                    freeform_tags={key: value},
                )
            )
            result = self.list(NAME=name, refresh=True)[0]
        except Exception as e:
            Console.error("Problem adding tag", traceflag=True)
            print(e)
            raise RuntimeError
        return result

    def migrate(self,
                name=None,
                fvm=None,
                tvm=None,
                fregion=None,
                tregion=None,
                fservice=None,
                tservice=None,
                fcloud=None,
                tcloud=None,
                cloud=None,
                region=None,
                service=None):
        """
        Migrate volume from one vm to another vm.

        :param name: name of volume
        :param fvm: name of vm where volume will be moved from
        :param tvm: name of vm where volume will be moved to
        :param fregion: the region where the volume will be moved from
        :param tregion: region where the volume will be moved to
        :param fservice: the service where the volume will be moved from
        :param tservice: the service where the volume will be moved to
        :param fcloud: the provider where the volume will be moved from
        :param tcloud: the provider where the volume will be moved to
        :param cloud: the provider where the volume will be moved within
        :param region: the region where the volume will be moved within
        :param service: the service where the volume will be moved within
        :return: dict

        """

        raise NotImplementedError

    def sync(self,
             volume_id=None,
             zone=None,
             cloud=None):
        """
        sync contents of one volume to another volume

        :param volume_id: id of volume A
        :param zone: zone where new volume will be created
        :param cloud: the provider where volumes will be hosted
        :return: str
        """
        raise NotImplementedError
Exemple #25
0
class Vm(ComputeNodeABC):
    def __init__(self, cloud):
        self.mongo = CmDatabase()
        self.config = Config().data["cloudmesh"]
        self.kind = self.config["cloudmesh"][cloud]["cm"]["kind"]
        super().__init__(cloud, self.config)

        if self.kind == 'azure':
            self.provider = LibcloudProvider(self.config)
        elif self.kind == 'aws':
            self.provider = LibcloudProvider(self.config)
        elif self.kind == 'vm':
            self.provider = LibcloudProvider("chameleon")
        elif self.kind == "vbox":  # not sure about vbox vs vagrant in vbox provider
            self.provider = VboxProvider("vagrant")
        else:
            raise NotImplementedError(f"Cloud `{self.kind}` not supported.")

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_default)
    def start(self, name):
        """
        start the node based on the id
        :param name:
        :return: VM document
        """
        info = self.info(name)
        if info["state"] != "running":
            info = self.provider.start(name)
        return info

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_default)
    def stop(self, name=None):
        """
        stop the node based on the ide
        :param name:
        :return: VM document
        """
        return self.provider.stop(name)

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_default)
    def resume(self, name=None):
        """
        start the node based on id
        :param name:
        """
        return self.start(name)

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_default)
    def suspend(self, name=None):
        """
        stop the node based on id
        :param name:
        """
        return self.provider.suspend(name)

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_default)
    def destroy(self, name=None):
        """
        delete the node based on id
        :param name:
        :return: True/False
        """
        result = self.provider.destroy(name)
        # self.mongo.delete_document('cloud', 'name', name)
        return result

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_vm_create)
    def create(self, name=None):
        """
        create a new node
        :param name: the name for the new node
        :return:
        """
        name = name or self.new_name()
        return self.provider.create(name=name)

    @DatabaseUpdate(collection="cloudmesh", ComputeNodeABC.map_default)
    def nodes(self):
        return self.provider.vagrant_nodes()

    # @DatabaseUpdate(collection="cloudmesh", ComputeNodeManagerABC._map_default)
    def info(self, name=None):
        """
        show node information based on id

        TODO: seems like this should look in mongo, not self.nodes
            probably the solution is a more broad change to dynamically
            set the provider based on a name/cloud lookup in mongo.

        :param name:
        :return: all information about one node
        """
        return self.provider.info(name)

    def new_name(self, experiment=None, group=None, user=None):
        """
        Generate a VM name with the format `experiment-group-name-<counter>` where `counter`
        represents a running count of VMs created.

        Defaults can be modified in the cloudmesh4.yaml file.

        :param experiment:
        :param group:
        :param user:
        :return: The generated name.
        """
        experiment = experiment or self.config["default"]["experiment"]
        group = group or self.config["default"]["group"]
        user = user or getpass.getuser()

        counter = Counter()
        count = counter.get()
        name = Name()
        name_format = {
            'experiment': experiment,
            'group': group,
            'user': user,
            'counter': count
        }
        name.set_schema('instance')
        counter.incr()
        return name.get(name_format)

    def get_public_ips(self, name=None):
        """
        Returns all the public ips available if a name is not given.
        If a name is provided, the ip of the vm name would be returned.
        :param name: name of the VM.
        :return: Dictionary of VMs with their public ips
        """
        if name is None:
            filters = {"$exists": True, "$not": {"$size": 0}}
            documents = self.mongo.find('cloud', 'public_ips', filters)
            if documents is None:
                return None
            else:
                result = {}
                for document in documents:
                    result[document['name']] = document['public_ips']
                return result
        else:
            public_ips = self.mongo.find_document('cloud', 'name',
                                                  name)['public_ips']
            if not public_ips:
                return None
            else:
                return {name: public_ips}

    def set_public_ip(self, name, public_ip):
        """
        Assign the given public ip to the given VM.
        :param name: name of the VM
        :param public_ip: public ip to be assigned.
        """
        if name is not None and public_ip is not None:
            self.provider.set_public_ip(name, public_ip)

    def remove_public_ip(self, name):
        """
        Deletes the public ip of the given VM.
        :param name: name of the VM
        """
        if name is not None:
            self.provider.remove_public_ip(name)
Exemple #26
0
    def do_key(self, args, arguments):
        """
        ::

           Usage:
             key  -h | --help
             key list --cloud=CLOUDS [--output=OUTPUT]
             key list --source=ssh [--dir=DIR] [--output=OUTPUT]
             key list --source=git [--output=OUTPUT] [--username=USERNAME]
             key list [--output=OUTPUT]
             key init
             key add NAME --filename=FILENAME [--output=OUTPUT]
             key add [NAME] [--source=FILENAME]
             key add [NAME] [--source=git]
             key add [NAME] [--source=ssh]
             key delete NAMES [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
             key upload [NAMES] [VMS] [--dryrun]
             key group upload [NAMES] [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group add [--group=GROUPNAMES] [--cloud=CLOUDS] [--dryrun]
             key group add --file=FILENAME
             key group delete [--group=GROUPNAMES] [NAMES] [--dryrun]
             key group list [--group=GROUPNAMES] [--output=OUTPUT]
             key group export --group=GROUNAMES --filename=FILENAME
             key gen (rsa | ssh) [--filename=FILENAME] [--nopass] [--set_path]
             key verify (ssh | pem) --filename=FILENAME [--pub]

           Arguments:
             VMS            Parameterized list of virtual machines
             CLOUDS         The clouds
             NAME           The name of the key.
             SOURCE         db, ssh, all
             KEYNAME        The desired full path name to the key file
             OUTPUT         The format of the output (table, json, yaml)
             FILENAME       The filename with full path in which the key is located

           Options:
              --dir=DIR             the directory with keys [default: ~/.ssh]
              --filename=FILENAME   the name and full path to the file
              --nopass              Flag indicating if the key has no password
              --output=OUTPUT       the format of the output [default: table]
              --pub                 Indicates that the public key is passed in
              --set_path            Sets the security key paths to KEYNAME
              --source=SOURCE       the source for the keys
              --username=USERNAME   the source for the keys [default: none]


           Description:

               Please note that some values are read from the cloudmesh.yaml
               file. One such value is cloudmesh.profile.user

               Manages public keys is an essential component of accessing
               virtual machine sin the cloud. There are a number of sources
               where you can find public keys. This includes teh ~/.ssh
               directory and for example github.

               Keys will be uploaded into cloudmesh database with the add
               command under the given NAME. If the name is not specified the name
               cloudmesh.profile.user is assumed.

                key add NAME  --source=ssh
                    adds the default key in ~/.ssh/id_rsa.pub
                key add NAME  --source=FILENAME
                    adds the key specified by the filename with the given name
                key add NAME --git --username=username
                    adds a named github key from a user with the given github
                    username.

                key set
                    adds the ~/.ssh/id_rsa.pub key with the name specified in
                    cloudmesh.profile.user.
                    It also sets the variable key to that user.


               Once the keys are uploaded to github, they can be listed
               To list these keys the following list functions are provided.

                key list --source=git  [--username=USERNAME]
                    lists all keys in git for the specified user. If the
                    name is not specified it is read from cloudmesh.yaml
                key list --source=ssh  [--dir=DIR] [--output=OUTPUT]
                    lists all keys in the directory. If the directory is not
                    specified the default will be ~/.ssh

                key list NAMES
                    lists all keys in the named virtual machines.


                List command can use the [--output=OUTPUT] option

                    list the keys loaded to cloudmesh in the given format:
                    json, yaml, table. table is default. The NAME can be
                    specified and if omitted the name cloudmesh.profile.user
                    is assumed.

                To get keys from the cloudmesh database the following commands
                are available:

                key delete NAMES
                    deletes the Named keys. This may also have an impact on groups
                key rename NAME NEW
                    renames the key from NAME to NEW in the cloudmesh database.

               Group management of keys is an important concept in cloudmesh,
               allowing multiple users to be added to virtual machines while
               managing the keys associated with them. The keys must be uploaded
               to cloudmesh database with a name so they can be used in a
               group. The --dryrun option executes the command without
               uploading the information to the clouds. If no group name is
               specified the group name default is assumed. If no cloudnamesh
               are specified, all active clouds are assumed. active clouds
               can be set in the cloudmesh.yaml file.

                key group delete [GROUPNAMES] [NAMES] [--dryrun]
                    deletes the named keys from the named groups.

                key group list [GROUPNAMES] [--output=OUTPUT]
                    list the key names and details in the group.

                key group upload [GROUPNAMES] [CLOUDS] [--dryrun]
                    uploads the named groups to the specified clouds.

               In some cases you may want to store the public keys in files. For
               this reason we support the following commands.

                key group add --group=GROUPNAME --file=FILENAME
                    the command adds the keys to the given group. The keys are
                    written in the files in yaml format.


                key group export --group=GROUNAMES --filename=FILENAME
                    the command exports the keys to the given group. The keys are
                    written in the files in yaml format.


                The yaml format is as follows:

                cloudmesh:
                  keys:
                    NAMEOFKEY:
                      name: NAMEOFKEY
                      key: ssh-rsa AAAA..... comment
                      group:
                      - GROUPNAME
                    ...

                If a key is included in multiple groups they will be added
                to the grouplist of the key
        """
        def print_keys(keys):
            print(
                Printer.write(
                    keys,
                    sort_keys=["name"],
                    order=["name", "type", "fingerprint", "comment"],
                    header=["Name", "Type", "Fingerprint", "Comment"],
                    output=arguments.output))

        map_parameters(arguments, 'cloud', 'dir', 'dryrun', 'filename', 'name',
                       'nopass', 'output', 'pub', 'pwd', 'set_path', 'source')

        variables = Variables()

        if arguments.list and arguments.source == "git":

            config = Config()
            username = config["cloudmesh.profile.github"]
            keys = SSHkey().get_from_git(username)

            print_keys(keys)

            return ""

        elif arguments.list and arguments.source == "ssh":
            # this is much simpler

            sshkey = SSHkey()

            print_keys([sshkey])

            return ""

        elif arguments.list and arguments.cloud:

            clouds = Parameter.expand(arguments.cloud)

            if len(clouds) == 0:
                variables = Variables()
                cloudname = variables['cloud']
                clouds = [cloudname]
            keys = []

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                keys = provider.keys()

                provider.Print(keys, output=arguments.output, kind="key")

            return ""

        elif arguments.list:

            cloud = "local"
            db = CmDatabase()
            keys = db.find(collection=f"{cloud}-key")

            print_keys(keys)

            return ""

        elif arguments.add:
            """
             key add [NAME] [--source=FILENAME] # NOT IMPLEMENTED YET
             key add [NAME] [--source=git]
             key add [NAME] [--source=ssh]
             """
            key = Key()

            if arguments["--source"] == "ssh":
                name = arguments.NAME or "ssh"
                key.add(name, "ssh")
            elif arguments["--source"] == "git":
                name = arguments.NAME or "git"
                key.add("git", "git")
            else:
                config = Config()
                name = config["cloudmesh.profile.user"]
                kind = "ssh"
                key.add(name, kind)

        elif arguments.init:
            """
            key init 
            """

            config = Config()
            username = config["cloudmesh.profile.user"]

            if username == "TBD":
                Console.error(
                    "Please set cloudmesh.profile.user in ~/.cloudmesh.yaml")
                u = os.environ["USER"].lower().replace(" ", "")
                Console.msg(
                    f"To change it you can use the command. Define a NAME such as '{u}' e.g."
                )
                Console.msg("")
                Console.msg(f"  cms config set cloudmesh.profile.user={u}")
                Console.msg("")
                return ""

            key = Key()

            key.add(username, "ssh")
            variables['key'] = username

        elif arguments.upload:
            """
            key upload [NAMES] [--cloud=CLOUDS] [--dryrun]
            key upload [NAMES] [VMS] [--dryrun]
            """

            names = Parameter.expand(arguments.NAMES)

            # this may have a bug if NAMES is ommitted

            #
            # Step 0. Set keyname to variable
            #

            if names is None or len(names) == 0:
                config = Config()
                username = config["cloudmesh.profile.user"]
                names = [username]

            if len(names) == 1:
                name = names[0]
                variables = Variables()
                if "key" in variables:
                    old = variables["key"]
                    if old != name:
                        Console.msg(
                            f"Changing defualt key from {old} to {name}")
                        variables["key"] = name

            #
            # Step 1. keys = find keys to upload
            #

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            keys = []
            for key in db_keys:
                if key["name"] in names:
                    keys.append(key)

            if len(keys) == 0:
                Console.error(
                    f"No keys with the names {names} found in cloudmesh. \n"
                    "       Use the command 'key add' to add the key.")

            #
            # Step 2. iterate over the clouds to upload
            #

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud}")
                provider = Provider(name=cloud)
                for key in db_keys:
                    name = key['name']
                    if name in names:
                        try:
                            r = provider.key_upload(key)
                            Console.ok(f"upload key '{name} successful'. ")
                        except ValueError as e:
                            Console.error(
                                f"key '{name} already exists in {cloud}.")

            return ""

        elif arguments.delete and arguments.cloud and arguments.NAMES:

            # key delete NAMES --cloud=CLOUDS [--dryrun]
            names = Parameter.expand(arguments.NAMES)
            clouds = Parameter.expand(arguments.cloud)

            for cloud in clouds:
                provider = Provider(name=cloud)
                for name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name} in {cloud}")
                    else:
                        images = provider.key_delete(name)

            return ""

        elif arguments.gen:
            """
            key gen (rsa | ssh) [--filename=FILENAME] [--nopass] [--set_path]
            Generate an RSA key pair with pem or ssh encoding for the public
            key. The private key is always encoded as a PEM file.
            """
            config = Config()

            # Check if password will be requested
            ap = not arguments.nopass

            if not ap:
                Console.warning("Private key will NOT have a password")
                cnt = yn_choice(message="Continue, despite risk?", default="N")
                if not cnt:
                    sys.exit()

            # Discern the name of the public and private keys
            rk_path = None
            uk_path = None
            if arguments.filename:
                if arguments.filename[-4:] == ".pub":
                    rk_path = path_expand(arguments.name[-4:])
                    uk_path = path_expand(arguments.name)
                elif arguments.filename[-5:] == ".priv":
                    rk_path = path_expand(arguments.name)
                    uk_path = path_expand(arguments.name[-5:])
                else:
                    rk_path = path_expand(arguments.filename)
                    uk_path = rk_path + ".pub"
            else:
                rk_path = path_expand(config['cloudmesh.security.privatekey'])
                uk_path = path_expand(config['cloudmesh.security.publickey'])

            # Set the path if requested
            if arguments.set_path and arguments.filename:
                config['cloudmesh.security.privatekey'] = rk_path
                config['cloudmesh.security.publickey'] = uk_path
                config.save()

            Console.msg(f"\nPrivate key: {rk_path}")
            Console.msg(f"Public  key: {uk_path}\n")

            # Generate the Private and Public keys
            kh = KeyHandler()
            r = kh.new_rsa_key()
            u = kh.get_pub_key(priv=r)

            # Serialize and write the private key to the path
            sr = kh.serialize_key(key=r,
                                  key_type="PRIV",
                                  encoding="PEM",
                                  format="PKCS8",
                                  ask_pass=ap)
            kh.write_key(key=sr, path=rk_path)

            # Determine the public key format and encoding
            enc = None
            forma = None
            if arguments.ssh:
                enc = "SSH"
                forma = "SSH"
            elif arguments.rsa:
                enc = "PEM"
                forma = "SubjectInfo"

            # Serialize and write the public key to the path
            su = kh.serialize_key(key=u,
                                  key_type="PUB",
                                  encoding=enc,
                                  format=forma,
                                  ask_pass=False)
            kh.write_key(key=su, path=uk_path)

            Console.ok("Success")

        elif arguments.verify:
            """
            key verify (ssh | pem) --filename=FILENAME --pub
            Verifies the encoding (pem or ssh) of the key (private or public)
            """
            kh = KeyHandler()
            fp = arguments.filename
            kt = None
            enc = None

            # Discern key type
            if arguments.pub:
                kt = "public"
                # Discern public key encoding
                if arguments.ssh:
                    enc, e = "OpenSSH", "SSH"
                elif arguments.pem:  #PEM encoding
                    enc = e = "PEM"

                # Load the public key, if no error occurs formatting is correct
                u = kh.load_key(path=fp,
                                key_type="PUB",
                                encoding=e,
                                ask_pass=False)

            else:
                kt, enc = "private", "PEM"

                # Load the private key to verify the formatting and password of
                # the key file. If no error occurs the format and pwd are correct
                r = kh.load_key(path=fp,
                                key_type="PRIV",
                                encoding=enc,
                                ask_pass=True)

            m = f"Success the {kt} key {fp} has proper {enc} format"
            Console.ok(m)

        elif arguments.delete and arguments.NAMES:
            # key delete NAMES [--dryrun]

            names = Parameter.expand(arguments.NAMES)

            cloud = "local"
            db = CmDatabase()
            db_keys = db.find(collection=f"{cloud}-key")

            error = []
            for key in db_keys:
                name = key['name']
                if name in names:
                    if arguments.dryrun:
                        Console.ok(f"Dryrun: delete {name}")
                    else:
                        db.delete(collection="local-key", name=name)
                        Console.ok(f"delete {name}")
            return ""

        elif arguments.group:

            raise NotImplementedError

        return ""
Exemple #27
0
 def monitor(self, rate):
     cm = CmDatabase()
     while True:
         entries = cm.find(cloud=self.name, kind='storage')
         os.system("clear")
         print(entries)  # use a pretty table
Exemple #28
0
    def do_flavor(self, args, arguments):
        """
        ::

            Usage:
                flavor list [NAMES] [--cloud=CLOUD] [--refresh] [--output=OUTPUT] [--query=QUERY]


            Options:
               --output=OUTPUT  the output format [default: table]
               --cloud=CLOUD    the ycloud name
               --refresh        refreshes the data before displaying it

            Description:

                This lists out the flavors present for a cloud

            Examples:
                cm flavor list --refresh
                cm flavor list
                cm flavor list --output=csv
                cm flavor list 58c9552c-8d93-42c0-9dea-5f48d90a3188 --refresh

                please remember that a uuid or the flavor name can be used to
                identify a flavor.


                cms flavor list --refresh --query=\'{\"a\": \"b\"}\'

            OpenStack Query Example:

                cms flavor list --refresh --query=\'{\"minDisk\": \"80\"}\'
                cms flavor list --refresh --query=\'{\"name\": \"m1.large\"}\'

                supported query parameters for OpenStack:

                        min_disk
                        min_ram
                        name


        """

        map_parameters(arguments, "query", "refresh", "cloud", "output")

        variables = Variables()

        arguments.output = Parameter.find("output", arguments, variables,
                                          "table")

        arguments.refresh = Parameter.find_bool("refresh", arguments,
                                                variables)

        if arguments.list and arguments.refresh:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            for cloud in clouds:
                print(f"cloud {cloud} query={arguments.query}")
                provider = Provider(name=cloud)
                if arguments.query is not None:
                    query = eval(arguments.query)
                    flavors = provider.flavors(**query)
                else:
                    flavors = provider.flavors()

                provider.Print(flavors, output=arguments.output, kind="flavor")

            return ""

        elif arguments.list:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            try:

                for cloud in clouds:
                    if arguments.output in ["table"]:
                        print(f"List {cloud}")
                    provider = Provider(name=cloud)

                    db = CmDatabase()
                    flavors = db.find(collection=f"{cloud}-flavor")
                    provider.Print(flavors,
                                   output=arguments.output,
                                   kind="flavor")

            except Exception as e:

                VERBOSE(e)

            return ""
Exemple #29
0
class Provider(VolumeABC):
    kind = "volume"

    sample = """
    cloudmesh:
      volume:
        {name}:
          cm:
            active: '1'
            heading: multipass
            host: TBD
            kind: multipass
            version: TBD
            service: volume
          default:
            path: /Volumes/multipass
    """

    output = {
        "volume": {
            "sort_keys": ["cm.name"],
            "order": [
                "cm.name", "cm.cloud", "cm.kind", "State", "path",
                'machine_path', "AttachedToVm", "tags", "time"
            ],
            "header": [
                "Name", "Cloud", "Kind", "State", "Path", 'Machine Path',
                "AttachedToVm", "Tags", "Update Time"
            ]
        }
    }

    def generate_volume_info(self, NAME, path):
        """
        generate volume info dict.
        info['AttachedToVm'] is a list of vm names where the volume is attached
        to. (volume can attach to multiple vm and vm can have multiple
        attachments) info['machine_path'] is the volume path in vm
        info['time"] is the created time, will be updated as updated time

        :param NAME: volume name
        :param path: volume path
        :return: dict
        """
        info = {
            'tags': [],
            'name': NAME,
            'path': path,
            'AttachedToVm': [],
            'State': 'available',
            'machine_path': None,
            'time': datetime.datetime.now()
        }
        return info

    def update_volume_after_attached_to_vm(self, info, vms):
        """
        Update volume info after attached to a vm.
        info['AttachedToVm'] is a list of vm names where the volume is attached
        to. info['machine_path'] is the volume path in vm
        info['time"] is the updated as updated time

        :param info: volume info got from MongoDB database
        :param vms: attached to vms
        :return: list of one dict
        """
        path = info[0]['path']
        path_list = path.split(sep='/')
        machine_path_list = ["~", "Home"]
        machine_path_list.extend(path_list[3:])
        info[0]['machine_path'] = "/".join(machine_path_list)
        info[0]['AttachedToVm'] = vms
        info[0]['State'] = 'in-use'
        info[0]['time'] = datetime.datetime.now()
        return info

    def update_volume_after_detach(self, info, vms):
        """
        update volume info after detaching from a vm
        info['AttachedToVm'] is a list of vm names where the volume is attached
        to. info['time"] is the updated time

        :param info: volume info
        :param vms: attached to vms
        :return: list of one dict
        """
        info[0]['AttachedToVm'] = vms
        if len(vms) == 0:
            info[0]['machine_path'] = None
            info[0]['State'] = 'available'
        info[0]['time'] = datetime.datetime.now()
        return info

    def update_volume_tag(self, info, key, value):
        """
        Update volume tag.
        Tags is a key-value pair, with key as tag name and value as tag value,
        tag = {key: value}. A volume can have multipale tags.
        If given duplicated tag name, update the value to the current tag value.

        :param value: value
        :param key: key
        :param info: volume info
        :param vms: attached to vms
        :return: list of one dict
        """
        keys = []
        for tag in info[0]['tags']:
            if key == list(tag.keys())[0]:
                if len(value) == 0:
                    info[0]['tags'].remove(tag)
                    keys.append(list(tag.keys())[0])
                else:
                    tag.update({key: value})
                    keys.append(list(tag.keys())[0])
        if key not in keys:
            tag = {key: value}
            info[0]['tags'].append(tag)
        info[0]['time'] = datetime.datetime.now()
        return info

    def __init__(self, name):
        """
        Initialize provider.
        set cloudtype to "multipass", get the default dict, create a cloudmesh
        database object.

        :param name: name of cloud
        """
        self.cloud = name
        self.cloudtype = "multipass"
        config = Config()
        self.default = config[f"cloudmesh.volume.{self.cloud}.default"]
        self.cm = CmDatabase()

    def update_dict(self, elements, kind=None):
        """
        converts the dict into a list.

        :param elements: the list of original dicts. If elements is a single
                         dict a list with a single element is returned.
        :param kind: "multipass"
        :return: The list with the modified dicts
        """
        if elements is None:
            return None

        d = []
        for element in elements:
            if "cm" not in element.keys():
                element['cm'] = {}
            element["cm"].update({
                "kind": "volume",
                "cloud": self.cloud,
                "name": element['name'],
            })
            d.append(element)
        return d

    def create(self, **kwargs):
        """
        This function create a new volume.
        Default parameters from self.default, such as:
        path="/Users/username/multipass".
        Note: Windows users should also use "/" in file path.

        :param NAME (string): the name of volume
        :param path (string): path of volume
        :return: dict
        """
        for key in self.default.keys():
            if key not in kwargs.keys():
                kwargs[key] = self.default[key]
            elif kwargs[key] is None:
                kwargs[key] = self.default[key]
        name = kwargs['NAME']
        path = Path(kwargs['path'])
        new_path = Path(f'{path}/{name}')
        result = os.system(f"mkdir {new_path}")
        if result == 0:
            result = self.generate_volume_info(NAME=name, path=kwargs['path'])
        result = self.update_dict([result])
        return result

    def delete(self, name):
        """
        Delete volumes.
        If name is not given, delete the most recent volume.

        :param name: volume name
        :return:
        """
        result = self.cm.find_name(name)
        path = result[0]['path']
        delete_path = Path(f'{path}/{name}')
        try:
            os.system(f"rmdir {delete_path}")
            result[0]['State'] = 'deleted'
            result = self.update_dict(result)
        except:
            Console.error("volume is either not empty or not exist")
        return result

    def list(self, **kwargs):
        """
        This function list all volumes as following:
        If NAME (volume name) is specified, it will print out info of NAME.
        If NAME (volume name) is not specified, it will print out info of all
          volumes under current cloud.
        If vm is specified, it will print out all the volumes attached to vm.
        If region(path) is specified, it will print out
          all the volumes in that region. i.e. /Users/username/multipass

        :param NAME: name of volume
        :param vm: name of vm
        :param region: for multipass, it is the same with "path"
        :return: dict
        """
        if kwargs:
            result = self.cm.find(cloud='multipass', kind='volume')
            for key in kwargs:
                if key == 'NAME' and kwargs['NAME']:
                    result = self.cm.find_name(name=kwargs['NAME'])
                elif key == 'NAMES' and kwargs['NAMES']:
                    result = self.cm.find_names(names=kwargs['NAMES'])
                elif key == 'vm' and kwargs['vm']:
                    result = self.cm.find(collection=f"{self.cloud}-volume",
                                          query={'AttachedToVm': kwargs['vm']})
                elif key == 'region' and kwargs['region']:
                    result = self.cm.find(collection=f"{self.cloud}-volume",
                                          query={'path': kwargs['region']})
        else:
            result = self.cm.find(cloud='multipass', kind='volume')
        return result

    def _get_vm_status(self, name=None) -> dict:
        """
        Get vm status.

        :param name (string): vm name
        :return: dict
        """
        dict_result = {}
        result = Shell.run(f"multipass info {name} --format=json")
        if f'instance "{name}" does not exist' in result:
            dict_result = {'name': name, 'status': "instance does not exist"}
        else:
            result = json.loads(result)
            dict_result = {
                'name': name,
                'status': result["info"][name]['State']
            }
        return dict_result

    def attach(self, names, vm):
        """
        This function attach one or more volumes to vm. It returns info of
        updated volume. The updated dict with "AttachedToVm" showing
        the name of vm where the volume attached to.

        :param names (string): names of volumes
        :param vm (string): name of vm
        :return: dict
        """
        results = []
        for name in names:
            volume_info = self.cm.find_name(name)
            if volume_info and volume_info[0]['State'] != "deleted":
                vms = volume_info[0]['AttachedToVm']
                path = volume_info[0]['path']
                if vm in vms:
                    Console.error(f"{name} already attached to {vm}")
                else:
                    result = self.mount(path=f"{path}/{name}", vm=vm)
                    mounts = result['mounts']
                    if f"{path}/{name}" in mounts.keys():
                        vms.append(vm)

                result = self.update_volume_after_attached_to_vm(
                    info=volume_info, vms=vms)
                results.append(result)
            else:
                Console.error(
                    "volume is not existed or volume had been deleted")
        return results[0]

    def mount(self, path=None, vm=None):
        """
        mount volume to vm

        :param path (string): path of volume
        :param vm (string): name of vm
        :return: dict
        """
        os.system(f"multipass mount {path} {vm}")
        dict_result = self._get_mount_status(vm=vm)
        return dict_result

    def _get_mount_status(self, vm=None):
        """
        Get mount status of vm

        :param vm (string): name of vm
        :return:
        """
        result = Shell.run(f"multipass info {vm} --format=json")

        if f'instance "{vm}" does not exist' in result:
            dict_result = {'name': vm, 'status': "instance does not exist"}
        else:
            result = json.loads(result)
            dict_result = {
                'name': vm,
                'status': result["info"][vm]['state'],
                'mounts': result["info"][vm]['mounts']
            }
        return dict_result

    def unmount(self, path=None, vm=None):
        """
        Unmount volume from vm

        :param path (string): path of volume
        :param vm (string): name of vm
        :return:
        """
        os.system(f"multipass unmount {vm}:{path}")
        dict_result = self._get_mount_status(vm=vm)
        return dict_result

    def detach(self, name):
        """
        This function detach a volume from vm. It returns the info of
        the updated volume.
        The vm under "AttachedToVm" will be removed if
        volume is successfully detached.
        Will detach volume from all vms.

        :param name: name of volume to be detached
        :return: dict
        """
        volume_info = self.cm.find_name(name)
        if volume_info and volume_info[0]['State'] != "deleted":
            vms = volume_info[0]['AttachedToVm']
            path = volume_info[0]['path']
            if len(vms) == 0:
                Console.error(f"{name} is not attached to any vm")
            else:
                removed = []
                for vm in vms:
                    result = self.unmount(path=f"{path}/{name}", vm=vm)
                    mounts = result['mounts']
                    if f"{path}/{name}" not in mounts.keys():
                        removed.append(vm)
                for vm in removed:
                    vms.remove(vm)
                result = self.update_volume_after_detach(volume_info, vms)
                return result[0]
        else:
            Console.error("volume does not exist or volume had been deleted")

    def add_tag(self, **kwargs):
        """
        This function add tag to a volume.
        If volume name is not specified, then tag will be added to the last
        volume.

        :param NAME: name of volume
        :param key: name of tag
        :param value: value of tag
        :return: dict
        """
        key = kwargs['key']
        value = kwargs['value']
        volume_info = self.cm.find_name(name=kwargs['NAME'])
        volume_info = self.update_volume_tag(info=volume_info,
                                             key=key,
                                             value=value)
        return volume_info[0]

    def status(self, name=None):
        """
        This function get volume status, such as "in-use", "available",
        "deleted"

        :param name: volume name
        :return: dict
        """
        volume_info = self.cm.find_name(name)
        if volume_info:
            status = volume_info[0]['State']
        else:
            Console.error("volume is not existed")
        return volume_info

    def migrate(self, **kwargs):
        """
        Migrate volume from one vm to another vm. "region" is volume path.
        If vm and volume are in the same region (path), migrate within the same
        region (path). If vm and volume are in different regions, migrate
        between two regions (path)

        :param NAME (string): the volume name
        :param vm (string): the vm name
        :return: dict
        """
        volume_name = kwargs['NAME']
        vm = kwargs['vm']
        volume_info = self.cm.find_name(name=volume_name)
        volume_attached_vm = volume_info[0]['AttachedToVm']
        vm_info = Shell.run(f"multipass info {vm} --format=json")
        vm_info = json.loads(vm_info)
        vm_status = vm_info["info"][vm]['state']
        if vm_status == 'running':
            param = {'NAME': volume_name}
            self.detach(**param)
            self.attach(**param, vm=vm)
        try:
            for old_vm in volume_attached_vm:
                volume_info[0]['AttachedToVm'].remove(old_vm)
        except:
            pass
        volume_info[0]['AttachedToVm'].append(vm)
        return volume_info

    def sync(self, **kwargs):
        """
        sync contents of one volume to another volume

        :param names (list): list of volume names
        :return: list of dict
        """
        volume_1 = kwargs['NAMES'][0]
        volume_2 = kwargs['NAMES'][1]
        path1 = f"{self.cm.find_name(name=volume_1)[0]['path']}/{volume_1}/"
        path2 = f"{self.cm.find_name(name=volume_2)[0]['path']}/{volume_2}/"
        os.system(f"rsync -avzh {path2} {path1}")
        kwargs1 = {'NAME': volume_1, 'key': "sync_with", 'value': volume_2}
        volume_info1 = self.add_tag(**kwargs1)
        result = [volume_info1]
        return result
Exemple #30
0
    def do_vm(self, args, arguments):
        """
        ::

            Usage:
                vm ping [NAMES] [--cloud=CLOUDS] [--count=N] [--processors=PROCESSORS]
                vm check [NAMES] [--cloud=CLOUDS] [--processors=PROCESSORS]
                vm status [NAMES] [--cloud=CLOUDS]
                vm console [NAME] [--force]
                vm start [NAMES] [--cloud=CLOUD] [--dryrun]
                vm stop [NAMES] [--cloud=CLOUD] [--dryrun]
                vm terminate [NAMES] [--cloud=CLOUD] [--dryrun]
                vm delete [NAMES] [--cloud=CLOUD] [--dryrun]
                vm refresh [--cloud=CLOUDS]
                vm list [NAMES]
                        [--cloud=CLOUDS]
                        [--output=OUTPUT]
                        [--refresh]
                vm boot [--name=VMNAMES]
                        [--cloud=CLOUD]
                        [--username=USERNAME]
                        [--image=IMAGE]
                        [--flavor=FLAVOR]
                        [--public]
                        [--secgroup=SECGROUPs]
                        [--key=KEY]
                        [--dryrun]
                vm boot [--n=COUNT]
                        [--cloud=CLOUD]
                        [--username=USERNAME]
                        [--image=IMAGE]
                        [--flavor=FLAVOR]
                        [--public]
                        [--secgroup=SECGROUPS]
                        [--key=KEY]
                        [--dryrun]
                vm run [--name=VMNAMES] [--username=USERNAME] [--dryrun] COMMAND
                vm script [--name=NAMES] [--username=USERNAME] [--dryrun] SCRIPT
                vm ip assign [NAMES]
                          [--cloud=CLOUD]
                vm ip show [NAMES]
                           [--group=GROUP]
                           [--cloud=CLOUD]
                           [--output=OUTPUT]
                           [--refresh]
                vm ip inventory [NAMES]
                vm ssh [NAMES] [--username=USER]
                         [--quiet]
                         [--ip=IP]
                         [--key=KEY]
                         [--command=COMMAND]
                         [--modify-knownhosts]
                vm rename [OLDNAMES] [NEWNAMES] [--force] [--dryrun]
                vm wait [--cloud=CLOUD] [--interval=SECONDS]
                vm info [--cloud=CLOUD]
                        [--output=OUTPUT]
                vm username USERNAME [NAMES] [--cloud=CLOUD]
                vm resize [NAMES] [--size=SIZE]

            Arguments:
                OUTPUT         the output format
                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 vm 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:
                --output=OUTPUT   the output format [default: table]
                -H --modify-knownhosts  Do not modify ~/.ssh/known_hosts file
                                      when ssh'ing into a machine
                --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, 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 vm 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.

                vm refresh [--cloud=CLOUDS]
                    this command refreshes the data for virtual machines,
                    images and flavors for the specified clouds.

                vm ping [NAMES] [--cloud=CLOUDS] [--count=N] [--processors=PROCESSORS]
                     pings the specified virtual machines, while using at most N pings.
                     The ping is executed in parallel.
                     If names are specifies the ping is restricted to the given names in
                     parameter format. If clouds are specified, names that are not in
                     these clouds are ignored. If the name is set in the variables
                     this name is used.

            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\"

            Limitations:

                Azure: rename is not supported
        """

        map_parameters(arguments, 'active', 'cloud', 'command', 'dryrun',
                       'flavor', 'force', 'output', 'group', 'image',
                       'interval', 'ip', 'key', 'modify-knownhosts', 'n',
                       'name', 'public', 'quiet', 'secgroup', 'size',
                       'username')

        VERBOSE(arguments)

        variables = Variables()

        if arguments.refresh:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "refresh", arguments, variables)

            return ""

        elif arguments.ping:

            # TODO: IMPLEMENT
            names = []
            pings = int(arguments.N or 3)

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "ping", arguments, variables)

            for name in names:

                ping = Shell.live("ping -c {N} {name}".format(name=name,
                                                              N=arguments.N))
                print(ping)
            else:
                return True

            return ""

        elif arguments.check:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "check", arguments, variables)

            return ""

        elif arguments.status:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "status", arguments, variables)

            return ""

        elif arguments.start:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "start", arguments, variables)

            return ""

        elif arguments.stop:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "stop", arguments, variables)

            return ""

        elif arguments.terminate:

            names = []

            clouds, names = Arguments.get_cloud_and_names(
                "terminate", arguments, variables)

            return ""

        elif arguments.delete:

            clouds, names = Arguments.get_cloud_and_names(
                "delete", arguments, variables)

            return ""

        elif arguments.boot:

            print("boot the vm")

        elif arguments.list:
            # vm list [NAMES]
            #   [--cloud=CLOUDS]
            #   [--output=OUPTUT]
            #   [--refresh]

            # if no clouds find the clouds of all specified vms by name
            # find all vms of the clouds,
            # print only those vms specified by name, if no name is given print all for the cloud
            # print("list the vms")

            clouds, names = Arguments.get_cloud_and_names(
                "list", arguments, variables)

            # print("Clouds:", clouds)

            if arguments.NAMES is not None:
                names = Parameter.expand(arguments.NAMES)
                Console.error("NAMES, not yet implemented" + str(names))

                try:
                    if arguments["--refresh"]:
                        pass
                        # find all clouds in db
                        # iterate over the clouds
                        # for each name in name queue, find it and add it to the cloud vm list
                        # for each cloud print the vms
                    else:
                        pass
                        # find all clouds in db
                        # iterate over all clouds
                        # find the vm with the name
                        # add it to the cloud list
                        # for each cloud print the vms
                except Exception as e:

                    VERBOSE(e)

                return ""
            else:
                try:
                    if arguments["--refresh"]:
                        for cloud in clouds:
                            Console.ok("refresh " + cloud)

                            p = Provider(cloud)
                            vms = p.list()

                            order = p.p.output['vm']['order']  # not pretty
                            header = p.p.output['vm']['header']  # not pretty

                            print(
                                Printer.flatwrite(vms,
                                                  sort_keys=["cm.name"],
                                                  order=order,
                                                  header=header,
                                                  output=arguments.output))

                    else:
                        for cloud in clouds:
                            p = Provider(cloud)
                            kind = p.kind

                            # pprint(p.__dict__)
                            # pprint(p.p.__dict__) # not pretty

                            collection = "{cloud}-node".format(cloud=cloud,
                                                               kind=p.kind)
                            db = CmDatabase()
                            vms = db.find(collection=collection)

                            # pprint(vms)
                            # print(arguments.output)
                            # print(p.p.output['vm'])

                            order = p.p.output['vm']['order']  # not pretty
                            header = p.p.output['vm']['header']  # not pretty

                            print(
                                Printer.flatwrite(vms,
                                                  sort_keys=["cm.name"],
                                                  order=order,
                                                  header=header,
                                                  output=arguments.output))

                except Exception as e:

                    VERBOSE(e)

            return ""

        elif arguments.info:
            """
            vm info [--cloud=CLOUD] [--output=OUTPUT]
            """
            print("info for the vm")

            cloud, names = Arguments.get_cloud_and_names(
                "info", arguments, variables)

        elif arguments.rename:

            print("rename the vm")

            v = Variables()
            cloud = v["cloud"]

            p = Provider(cloud)

            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",
                                  traceflag=False)
                else:
                    print(oldnames)
                    print(newnames)
                    for i in range(0, len(oldnames)):
                        oldname = oldnames[i]
                        newname = newnames[i]
                        if arguments["--dryrun"]:
                            Console.ok("Rename {} to {}".format(
                                oldname, newname))
                        else:
                            print(f"rename {oldname} -> {newname}")

                            p.rename(source=oldname, destination=newname)

                    msg = "info. OK."
                    Console.ok(msg)
            except Exception as e:
                Error.traceback(e)
                Console.error("Problem renameing instances", traceflag=True)

        elif arguments["ip"] and arguments["show"]:

            print("show the ips")
            """
            vm ip show [NAMES]
                   [--group=GROUP]
                   [--cloud=CLOUD]
                   [--output=OUTPUT]
                   [--refresh]

            """

        elif arguments["ip"] and arguments["assign"]:
            """
            vm ip assign [NAMES] [--cloud=CLOUD]
            """
            print("assign the public ip")

        elif arguments["ip"] and arguments["inventory"]:
            """
            vm ip inventory [NAMES]

            """
            print("list ips that could be assigned")

        elif arguments.username:
            """
            vm username USERNAME [NAMES] [--cloud=CLOUD]
            """
            print("sets the username for the vm")

        elif arguments.default:

            print("sets defaults for the vm")

        elif arguments.run:
            """
            vm run [--name=NAMES] [--username=USERNAME] [--dryrun] COMMAND

            """
            pass
        elif arguments.script:
            """
            vm script [--name=NAMES] [--username=USERNAME] [--dryrun] SCRIPT
            """
            pass

        elif arguments.resize:
            """
            vm resize [NAMES] [--size=SIZE]
            """
            pass

        elif arguments.ssh:
            """
            vm ssh [NAMES] [--username=USER]
                 [--quiet]
                 [--ip=IP]
                 [--key=KEY]
                 [--command=COMMAND]
                 [--modify-knownhosts]
            """
            print("ssh  the vm")

        elif arguments.console:
            # vm console [NAME] [--force]

            names = Arguments.get_names(arguments, variables)

            for name in names:
                # r = vm.console(name,force=argument.force)
                Console.msg("{label} {name}".format(label="console",
                                                    name=name))
            return

        elif arguments.wait:
            """
            vm wait [--cloud=CLOUD] [--interval=SECONDS]
            """
            print("waits for the vm till its ready and one can login")