Exemple #1
0
    def process(self, cli=None):
        """
        Process the parsed command.
        """
        if cli:
            #Directly invoking code though a wrapper to catch unsupported
            #operations.
            self.c = Proxy(cli())
            self.c.plugin_register(self.uri, self.password, self.tmo)
            self.cleanup = self.c.plugin_unregister
        else:
            #Going across the ipc pipe
            self.c = Proxy(Client(self.uri, self.password, self.tmo))

            if os.getenv('LSM_DEBUG_PLUGIN'):
                raw_input(
                    "Attach debugger to plug-in, press <return> when ready...")

            self.cleanup = self.c.close

        self.args.func(self.args)
        self.shutdown()
Exemple #2
0
class CmdLine:
    """
    Command line interface class.
    """

    ##
    # Warn of imminent data loss
    # @param    deleting    Indicate data will be lost vs. may be lost
    #                       (re-size)
    # @return True if operation confirmed, else False
    def confirm_prompt(self, deleting):
        """
        Give the user a chance to bail.
        """
        if not self.args.force:
            msg = "will" if deleting else "may"
            out("Warning: You are about to do an operation that %s cause data "
                "to be lost!\nPress [Y|y] to continue, any other key to abort"
                % msg)

            pressed = getch()
            if pressed.upper() == 'Y':
                return True
            else:
                out('Operation aborted!')
                return False
        else:
            return True

    ##
    # Tries to make the output better when it varies considerably from
    # plug-in to plug-in.
    # @param    objects    Data, first row is header all other data.
    def display_data(self, objects):
        display_all = False

        if len(objects) == 0:
            return

        display_way = DisplayData.DISPLAY_WAY_DEFAULT

        flag_with_header = True
        if self.args.sep:
            flag_with_header = False
        if self.args.header:
            flag_with_header = True

        if self.args.script:
            display_way = DisplayData.DISPLAY_WAY_SCRIPT

        DisplayData.display_data(
            objects, display_way=display_way, flag_human=self.args.human,
            flag_enum=self.args.enum,
            splitter=self.args.sep, flag_with_header=flag_with_header,
            flag_dsp_all_data=display_all)

    def display_available_plugins(self):
        d = []
        sep = '<}{>'
        plugins = Client.available_plugins(sep)

        for p in plugins:
            desc, version = p.split(sep)
            d.append(PlugData(desc, version))

        self.display_data(d)

    def handle_alias(self, args):
        cmd_arguments = args.cmd
        cmd_arguments.extend(self.unknown_args)
        new_args = self.parser.parse_args(cmd_arguments)
        new_args.func(new_args)

    ## All the command line arguments and options are created in this method
    def cli(self):
        """
        Command line interface parameters
        """
        parent_parser = ArgumentParser(add_help=False)

        parent_parser.add_argument(
            '-v', '--version', action='version',
            version="%s %s" % (sys.argv[0], VERSION))

        parent_parser.add_argument(
            '-u', '--uri', action="store", type=str, metavar='<URI>',
            dest="uri", help='Uniform resource identifier (env LSMCLI_URI)')

        parent_parser.add_argument(
            '-P', '--prompt', action="store_true", dest="prompt",
            help='Prompt for password (env LSMCLI_PASSWORD)')

        parent_parser.add_argument(
            '-H', '--human', action="store_true", dest="human",
            help='Print sizes in human readable format\n'
                 '(e.g., MiB, GiB, TiB)')

        parent_parser.add_argument(
            '-t', '--terse', action="store", dest="sep", metavar='<SEP>',
            help='Print output in terse form with "SEP" '
                 'as a record separator')

        parent_parser.add_argument(
            '-e', '--enum', action="store_true", dest="enum", default=False,
            help='Display enumerated types as numbers instead of text')

        parent_parser.add_argument(
            '-f', '--force', action="store_true", dest="force", default=False,
            help='Bypass confirmation prompt for data loss operations')

        parent_parser.add_argument(
            '-w', '--wait', action="store", type=int, dest="wait",
            default=30000, help="Command timeout value in ms (default = 30s)")

        parent_parser.add_argument(
            '--header', action="store_true", dest="header",
            help='Include the header with terse')

        parent_parser.add_argument(
            '-b', action="store_true", dest="async", default=False,
            help='Run the command async. Instead of waiting for completion.\n '
                 'Command will exit(7) and job id written to stdout.')

        parent_parser.add_argument(
            '-s', '--script', action="store_true", dest="script",
            default=False, help='Displaying data in script friendly way.')

        parser = ArgumentParser(
            description='The libStorageMgmt command line interface.'
                        ' Run %(prog)s <command> -h for more on each command.',
            epilog='Copyright 2012-2015 Red Hat, Inc.\n'
                   'Please report bugs to '
                   '<*****@*****.**>\n',
            formatter_class=RawTextHelpFormatter,
            parents=[parent_parser])

        subparsers = parser.add_subparsers(metavar="command")

        # Walk the command list and add all of them to the parser
        for cmd in cmds:
            sub_parser = subparsers.add_parser(
                cmd['name'], help=cmd['help'], parents=[parent_parser],
                formatter_class=RawTextHelpFormatter)

            group = sub_parser.add_argument_group("cmd required arguments")
            for arg in cmd.get('args', []):
                name = arg['name']
                del arg['name']
                group.add_argument(name, required=True, **arg)

            group = sub_parser.add_argument_group("cmd optional arguments")
            for arg in cmd.get('optional', []):
                flags = arg['name']
                del arg['name']
                if not isinstance(flags, tuple):
                    flags = (flags,)
                group.add_argument(*flags, **arg)

            sub_parser.set_defaults(
                func=getattr(self, cmd['name'].replace("-", "_")))

        for alias in aliases:
            sub_parser = subparsers.add_parser(
                alias[0], help="Alias of '%s'" % alias[1],
                parents=[parent_parser],
                formatter_class=RawTextHelpFormatter, add_help=False)
            sub_parser.set_defaults(
                cmd=alias[1].split(" "), func=self.handle_alias)

        self.parser = parser
        known_agrs, self.unknown_args = parser.parse_known_args()
        return known_agrs

    ## Display the types of nfs client authentication that are supported.
    # @return None
    def display_nfs_client_authentication(self):
        """
        Dump the supported nfs client authentication types
        """
        if self.args.sep:
            out(self.args.sep.join(self.c.export_auth()))
        else:
            out(", ".join(self.c.export_auth()))

    ## Method that calls the appropriate method based on what the list type is
    # @param    args    Argparse argument object
    def list(self, args):
        search_key = None
        search_value = None
        if args.sys:
            search_key = 'system_id'
            search_value = args.sys
        if args.pool:
            search_key = 'pool_id'
            search_value = args.pool
        if args.vol:
            search_key = 'volume_id'
            search_value = args.vol
        if args.disk:
            search_key = 'disk_id'
            search_value = args.disk
        if args.ag:
            search_key = 'access_group_id'
            search_value = args.ag
        if args.fs:
            search_key = 'fs_id'
            search_value = args.ag
        if args.nfs_export:
            search_key = 'nfs_export_id'
            search_value = args.nfs_export
        if args.tgt:
            search_key = 'tgt_port_id'
            search_value = args.tgt

        if args.type == 'VOLUMES':
            if search_key == 'volume_id':
                search_key = 'id'
            if search_key == 'access_group_id':
                lsm_ag = _get_item(self.c.access_groups(), args.ag,
                                   "Access Group", raise_error=False)
                if lsm_ag:
                    return self.display_data(
                        self.c.volumes_accessible_by_access_group(lsm_ag))
                else:
                    return self.display_data([])
            elif search_key and search_key not in Volume.SUPPORTED_SEARCH_KEYS:
                raise ArgError("Search key '%s' is not supported by "
                               "volume listing." % search_key)
            self.display_data(self.c.volumes(search_key, search_value))
        elif args.type == 'POOLS':
            if search_key == 'pool_id':
                search_key = 'id'
            if search_key and search_key not in Pool.SUPPORTED_SEARCH_KEYS:
                raise ArgError("Search key '%s' is not supported by "
                               "pool listing." % search_key)
            self.display_data(
                self.c.pools(search_key, search_value))
        elif args.type == 'FS':
            if search_key == 'fs_id':
                search_key = 'id'
            if search_key and \
               search_key not in FileSystem.SUPPORTED_SEARCH_KEYS:
                raise ArgError("Search key '%s' is not supported by "
                               "volume listing." % search_key)
            self.display_data(self.c.fs(search_key, search_value))
        elif args.type == 'SNAPSHOTS':
            if args.fs is None:
                raise ArgError("--fs <file system id> required")
            fs = _get_item(self.c.fs(), args.fs, 'File System')
            self.display_data(self.c.fs_snapshots(fs))
        elif args.type == 'EXPORTS':
            if search_key == 'nfs_export_id':
                search_key = 'id'
            if search_key and \
               search_key not in NfsExport.SUPPORTED_SEARCH_KEYS:
                raise ArgError("Search key '%s' is not supported by "
                               "NFS Export listing" % search_key)
            self.display_data(self.c.exports(search_key, search_value))
        elif args.type == 'NFS_CLIENT_AUTH':
            self.display_nfs_client_authentication()
        elif args.type == 'ACCESS_GROUPS':
            if search_key == 'access_group_id':
                search_key = 'id'
            if search_key == 'volume_id':
                lsm_vol = _get_item(self.c.volumes(), args.vol,
                                    "Volume", raise_error=False)
                if lsm_vol:
                    return self.display_data(
                        self.c.access_groups_granted_to_volume(lsm_vol))
                else:
                    return self.display_data([])
            elif (search_key and
                  search_key not in AccessGroup.SUPPORTED_SEARCH_KEYS):
                raise ArgError("Search key '%s' is not supported by "
                               "Access Group listing" % search_key)
            self.display_data(
                self.c.access_groups(search_key, search_value))
        elif args.type == 'SYSTEMS':
            if search_key:
                raise ArgError("System listing with search is not supported")
            self.display_data(self.c.systems())
        elif args.type == 'DISKS':
            if search_key == 'disk_id':
                search_key = 'id'
            if search_key and search_key not in Disk.SUPPORTED_SEARCH_KEYS:
                raise ArgError("Search key '%s' is not supported by "
                               "disk listing" % search_key)
            self.display_data(
                self.c.disks(search_key, search_value))
        elif args.type == 'TARGET_PORTS':
            if search_key == 'tgt_port_id':
                search_key = 'id'
            if search_key and \
               search_key not in TargetPort.SUPPORTED_SEARCH_KEYS:
                raise ArgError("Search key '%s' is not supported by "
                               "target port listing" % search_key)
            self.display_data(
                self.c.target_ports(search_key, search_value))
        elif args.type == 'PLUGINS':
            self.display_available_plugins()
        else:
            raise ArgError("unsupported listing type=%s" % args.type)

    ## Creates an access group.
    def access_group_create(self, args):
        system = _get_item(self.c.systems(), args.sys, "System")
        (init_id, init_type) = parse_convert_init(args.init)
        access_group = self.c.access_group_create(args.name, init_id,
                                                  init_type, system)
        self.display_data([access_group])

    def _add_rm_access_grp_init(self, args, op):
        lsm_ag = _get_item(self.c.access_groups(), args.ag, "Access Group")
        (init_id, init_type) = parse_convert_init(args.init)

        if op:
            return self.c.access_group_initiator_add(lsm_ag, init_id,
                                                     init_type)
        else:
            return self.c.access_group_initiator_delete(lsm_ag, init_id,
                                                        init_type)

    ## Adds an initiator from an access group
    def access_group_add(self, args):
        self.display_data([self._add_rm_access_grp_init(args, True)])

    ## Removes an initiator from an access group
    def access_group_remove(self, args):
        self.display_data([self._add_rm_access_grp_init(args, False)])

    def access_group_volumes(self, args):
        agl = self.c.access_groups()
        group = _get_item(agl, args.ag, "Access Group")
        vols = self.c.volumes_accessible_by_access_group(group)
        self.display_data(vols)

    def iscsi_chap(self, args):
        (init_id, init_type) = parse_convert_init(args.init)
        if init_type != AccessGroup.INIT_TYPE_ISCSI_IQN:
            raise ArgError("--init-id %s is not a valid iSCSI IQN" % args.init)

        self.c.iscsi_chap_auth(init_id, args.in_user,
                               self.args.in_pass,
                               self.args.out_user,
                               self.args.out_pass)

    def volume_access_group(self, args):
        vol = _get_item(self.c.volumes(), args.vol, "Volume")
        groups = self.c.access_groups_granted_to_volume(vol)
        self.display_data(groups)

    ## Used to delete access group
    def access_group_delete(self, args):
        agl = self.c.access_groups()
        group = _get_item(agl, args.ag, "Access Group")
        return self.c.access_group_delete(group)

    ## Used to delete a file system
    def fs_delete(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")
        if self.confirm_prompt(True):
            self._wait_for_it("fs-delete", self.c.fs_delete(fs), None)

    ## Used to create a file system
    def fs_create(self, args):
        p = _get_item(self.c.pools(), args.pool, "Pool")
        fs = self._wait_for_it("fs-create",
                               *self.c.fs_create(p, args.name,
                                                 self._size(args.size)))
        self.display_data([fs])

    ## Used to resize a file system
    def fs_resize(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")
        size = self._size(args.size)

        if self.confirm_prompt(False):
            fs = self._wait_for_it("fs-resize",
                                   *self.c.fs_resize(fs, size))
            self.display_data([fs])

    ## Used to clone a file system
    def fs_clone(self, args):
        src_fs = _get_item(
            self.c.fs(), args.src_fs, "Source File System")

        ss = None
        if args.backing_snapshot:
            #go get the snapshot
            ss = _get_item(self.c.fs_snapshots(src_fs),
                           args.backing_snapshot, "Snapshot")

        fs = self._wait_for_it(
            "fs_clone", *self.c.fs_clone(src_fs, args.dst_name, ss))
        self.display_data([fs])

    ## Used to clone a file(s)
    def file_clone(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")
        if self.args.backing_snapshot:
            #go get the snapshot
            ss = _get_item(self.c.fs_snapshots(fs),
                           args.backing_snapshot, "Snapshot")
        else:
            ss = None

        self._wait_for_it(
            "fs_file_clone", self.c.fs_file_clone(fs, args.src, args.dst, ss),
            None)

    ##Converts a size parameter into the appropriate number of bytes
    # @param    s   Size to convert to bytes handles B, K, M, G, T, P postfix
    # @return Size in bytes
    @staticmethod
    def _size(s):
        size_bytes = size_human_2_size_bytes(s)
        if size_bytes <= 0:
            raise ArgError("Incorrect size argument format: '%s'" % s)
        return size_bytes

    def _cp(self, cap, val):
        if self.args.sep is not None:
            s = self.args.sep
        else:
            s = ':'

        if val:
            v = "SUPPORTED"
        else:
            v = "UNSUPPORTED"

        out("%s%s%s" % (cap, s, v))

    def capabilities(self, args):
        s = _get_item(self.c.systems(), args.sys, "System")

        cap = self.c.capabilities(s)
        sup_caps = sorted(cap.get_supported().values())
        all_caps = sorted(cap.get_supported(True).values())

        sep = DisplayData.DEFAULT_SPLITTER
        if self.args.sep is not None:
            sep = self.args.sep

        cap_data = OrderedDict()
        # Show support capabilities first
        for v in sup_caps:
            cap_data[v] = 'SUPPORTED'

        for v in all_caps:
            if v not in sup_caps:
                cap_data[v] = 'UNSUPPORTED'

        DisplayData.display_data_script_way([cap_data], sep)

    def plugin_info(self, args):
        desc, version = self.c.plugin_info()

        if args.sep:
            out("%s%s%s" % (desc, args.sep, version))
        else:
            out("Description: %s Version: %s" % (desc, version))

    ## Creates a volume
    def volume_create(self, args):
        #Get pool
        p = _get_item(self.c.pools(), args.pool, "Pool")
        vol = self._wait_for_it(
            "volume-create",
            *self.c.volume_create(
                p,
                args.name,
                self._size(args.size),
                vol_provision_str_to_type(args.provisioning)))
        self.display_data([vol])

    ## Creates a snapshot
    def fs_snap_create(self, args):
        #Get fs
        fs = _get_item(self.c.fs(), args.fs, "File System")
        ss = self._wait_for_it("snapshot-create",
                               *self.c.fs_snapshot_create(
                                   fs,
                                   args.name))

        self.display_data([ss])

    ## Restores a snap shot
    def fs_snap_restore(self, args):
        #Get snapshot
        fs = _get_item(self.c.fs(), args.fs, "File System")
        ss = _get_item(self.c.fs_snapshots(fs), args.snap, "Snapshot")

        flag_all_files = True

        if self.args.file:
            flag_all_files = False
            if self.args.fileas:
                if len(self.args.file) != len(self.args.fileas):
                    raise ArgError(
                        "number of --file not equal to --fileas")

        if self.confirm_prompt(True):
            self._wait_for_it(
                'fs-snap-restore',
                self.c.fs_snapshot_restore(
                    fs, ss, self.args.file, self.args.fileas, flag_all_files),
                None)

    ## Deletes a volume
    def volume_delete(self, args):
        v = _get_item(self.c.volumes(), args.vol, "Volume")
        if self.confirm_prompt(True):
            self._wait_for_it("volume-delete", self.c.volume_delete(v),
                              None)

    ## Deletes a snap shot
    def fs_snap_delete(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")
        ss = _get_item(self.c.fs_snapshots(fs), args.snap, "Snapshot")

        if self.confirm_prompt(True):
            self._wait_for_it("fs_snap_delete",
                              self.c.fs_snapshot_delete(fs, ss), None)

    ## Waits for an operation to complete by polling for the status of the
    # operations.
    # @param    msg     Message to display if this job fails
    # @param    job     The job id to wait on
    # @param    item    The item that could be available now if there is no job
    def _wait_for_it(self, msg, job, item):
        if not job:
            return item
        else:
            #If a user doesn't want to wait, return the job id to stdout
            #and exit with job in progress
            if self.args.async:
                out(job)
                self.shutdown(ErrorNumber.JOB_STARTED)

            while True:
                (s, percent, item) = self.c.job_status(job)

                if s == JobStatus.INPROGRESS:
                    #Add an option to spit out progress?
                    #print "%s - Percent %s complete" % (job, percent)
                    time.sleep(0.25)
                elif s == JobStatus.COMPLETE:
                    self.c.job_free(job)
                    return item
                else:
                    #Something better to do here?
                    raise ArgError(msg + " job error code= " + str(s))

    ## Retrieves the status of the specified job
    def job_status(self, args):
        (s, percent, item) = self.c.job_status(args.job)

        if s == JobStatus.COMPLETE:
            if item:
                self.display_data([item])

            self.c.job_free(args.job)
        else:
            out(str(percent))
            self.shutdown(ErrorNumber.JOB_STARTED)

    ## Replicates a volume
    def volume_replicate(self, args):
        p = None
        if args.pool:
            p = _get_item(self.c.pools(), args.pool, "Pool")

        v = _get_item(self.c.volumes(), args.vol, "Volume")

        rep_type = vol_rep_type_str_to_type(args.rep_type)
        if rep_type == Volume.REPLICATE_UNKNOWN:
            raise ArgError("invalid replication type= %s" % rep_type)

        vol = self._wait_for_it(
            "replicate volume",
            *self.c.volume_replicate(p, rep_type, v, args.name))
        self.display_data([vol])

    ## Replicates a range of a volume
    def volume_replicate_range(self, args):
        src = _get_item(self.c.volumes(), args.src_vol, "Source Volume")
        dst = _get_item(self.c.volumes(), args.dst_vol,
                        "Destination Volume")

        rep_type = vol_rep_type_str_to_type(args.rep_type)
        if rep_type == Volume.REPLICATE_UNKNOWN:
            raise ArgError("invalid replication type= %s" % rep_type)

        src_starts = args.src_start
        dst_starts = args.dst_start
        counts = args.count

        if not len(src_starts) \
                or not (len(src_starts) == len(dst_starts) == len(counts)):
            raise ArgError("Differing numbers of src_start, dest_start, "
                           "and count parameters")

        ranges = []
        for b in range(len(src_starts)):
            ranges.append(BlockRange(src_starts[b], dst_starts[b], counts[b]))

        if self.confirm_prompt(False):
            self.c.volume_replicate_range(rep_type, src, dst, ranges)

    ##
    # Returns the block size in bytes for each block represented in
    # volume_replicate_range
    def volume_replicate_range_block_size(self, args):
        s = _get_item(self.c.systems(), args.sys, "System")
        out(self.c.volume_replicate_range_block_size(s))

    def volume_mask(self, args):
        vol = _get_item(self.c.volumes(), args.vol, 'Volume')
        ag = _get_item(self.c.access_groups(), args.ag, 'Access Group')
        self.c.volume_mask(ag, vol)

    def volume_unmask(self, args):
        ag = _get_item(self.c.access_groups(), args.ag, "Access Group")
        vol = _get_item(self.c.volumes(), args.vol, "Volume")
        return self.c.volume_unmask(ag, vol)

    ## Re-sizes a volume
    def volume_resize(self, args):
        v = _get_item(self.c.volumes(), args.vol, "Volume")
        size = self._size(args.size)

        if self.confirm_prompt(False):
            vol = self._wait_for_it("resize",
                                    *self.c.volume_resize(v, size))
            self.display_data([vol])

    ## Enable a volume
    def volume_enable(self, args):
        v = _get_item(self.c.volumes(), args.vol, "Volume")
        self.c.volume_enable(v)

    ## Disable a volume
    def volume_disable(self, args):
        v = _get_item(self.c.volumes(), args.vol, "Volume")
        self.c.volume_disable(v)

    ## Removes a nfs export
    def fs_unexport(self, args):
        export = _get_item(self.c.exports(), args.export, "NFS Export")
        self.c.export_remove(export)

    ## Exports a file system as a NFS export
    def fs_export(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")

        # Check to see if we have some type of access specified
        if len(args.rw_host) == 0 \
                and len(args.ro_host) == 0:
            raise ArgError(" please specify --ro-host or --rw-host")

        export = self.c.export_fs(
            fs.id,
            args.exportpath,
            args.root_host,
            args.rw_host,
            args.ro_host,
            args.anonuid,
            args.anongid,
            args.auth_type,
            None)
        self.display_data([export])

    ## Displays volume dependants.
    def volume_dependants(self, args):
        v = _get_item(self.c.volumes(), args.vol, "Volume")
        rc = self.c.volume_child_dependency(v)
        out(rc)

    ## Removes volume dependants.
    def volume_dependants_rm(self, args):
        v = _get_item(self.c.volumes(), args.vol, "Volume")
        self._wait_for_it("volume-dependant-rm",
                          self.c.volume_child_dependency_rm(v), None)

    def volume_raid_info(self, args):
        lsm_vol = _get_item(self.c.volumes(), args.vol, "Volume")
        self.display_data(
            [
                VolumeRAIDInfo(
                    lsm_vol.id, *self.c.volume_raid_info(lsm_vol))])

    ## Displays file system dependants
    def fs_dependants(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")
        rc = self.c.fs_child_dependency(fs, args.file)
        out(rc)

    ## Removes file system dependants
    def fs_dependants_rm(self, args):
        fs = _get_item(self.c.fs(), args.fs, "File System")
        self._wait_for_it("fs-dependants-rm",
                          self.c.fs_child_dependency_rm(fs,
                                                        args.file),
                          None)

    def _read_configfile(self):
        """
        Set uri from config file. Will be overridden by cmdline option or
        env var if present.
        """

        allowed_config_options = ("uri",)

        config_path = os.path.expanduser("~") + "/.lsmcli"
        if not os.path.exists(config_path):
            return

        with open(config_path) as f:
            for line in f:

                if line.lstrip().startswith("#"):
                    continue

                try:
                    name, val = [x.strip() for x in line.split("=", 1)]
                    if name in allowed_config_options:
                        setattr(self, name, val)
                except ValueError:
                    pass

    ## Class constructor.
    def __init__(self):
        self.uri = None
        self.c = None
        self.parser = None
        self.unknown_args = None
        self.args = self.cli()

        self.cleanup = None

        self.tmo = int(self.args.wait)
        if not self.tmo or self.tmo < 0:
            raise ArgError("[-w|--wait] requires a non-zero positive integer")

        self._read_configfile()
        if os.getenv('LSMCLI_URI') is not None:
            self.uri = os.getenv('LSMCLI_URI')
        self.password = os.getenv('LSMCLI_PASSWORD')
        if self.args.uri is not None:
            self.uri = self.args.uri

        if self.uri is None:
            # We need a valid plug-in to instantiate even if all we are trying
            # to do is list the plug-ins at the moment to keep that code
            # the same in all cases, even though it isn't technically
            # required for the client library (static method)
            # TODO: Make this not necessary.
            if ('type' in self.args and self.args.type == "PLUGINS"):
                self.uri = "sim://"
                self.password = None
            else:
                raise ArgError("--uri missing or export LSMCLI_URI")

        # Lastly get the password if requested.
        if self.args.prompt:
            self.password = getpass.getpass()

        if self.password is not None:
            #Check for username
            u = uri_parse(self.uri)
            if u['username'] is None:
                raise ArgError("password specified with no user name in uri")

    ## Does appropriate clean-up
    # @param    ec      The exit code
    def shutdown(self, ec=None):
        if self.cleanup:
            self.cleanup()

        if ec:
            sys.exit(ec)

    ## Process the specified command
    # @param    cli     The object instance to invoke methods on.
    def process(self, cli=None):
        """
        Process the parsed command.
        """
        if cli:
            #Directly invoking code though a wrapper to catch unsupported
            #operations.
            self.c = Proxy(cli())
            self.c.plugin_register(self.uri, self.password, self.tmo)
            self.cleanup = self.c.plugin_unregister
        else:
            #Going across the ipc pipe
            self.c = Proxy(Client(self.uri, self.password, self.tmo))

            if os.getenv('LSM_DEBUG_PLUGIN'):
                raw_input(
                    "Attach debugger to plug-in, press <return> when ready...")

            self.cleanup = self.c.close

        self.args.func(self.args)
        self.shutdown()