예제 #1
0
    def ui_command_create(self, gateway_name, ip_address, nosync=False,
                          skipchecks='false'):
        """
        Define a gateway to the gateway group for this iscsi target. The
        first host added should be the gateway running the command

        gateway_name ... should resolve to the hostname of the gateway
        ip_address ..... is the IPv4/IPv6 address of the interface the iscsi
                         portal should use
        nosync ......... by default new gateways are sync'd with the
                         existing configuration by cli. By specifying nosync
                         the sync step is bypassed - so the new gateway
                         will need to have it's rbd-target-gw daemon
                         restarted to apply the current configuration
                         (default = False)
        skipchecks ..... set this to true to force gateway validity checks
                         to be bypassed(default = False). This is a developer
                         option ONLY. Skipping these checks has the potential
                         to result in an unstable configuration.
        """

        ip_address = normalize_ip_address(ip_address)
        self.logger.debug("CMD: ../gateways/ create {} {} "
                          "nosync={} skipchecks={}".format(gateway_name,
                                                           ip_address,
                                                           nosync,
                                                           skipchecks))

        local_gw = this_host()
        current_gateways = [tgt.name for tgt in self.children]

        if gateway_name != local_gw and len(current_gateways) == 0:
            # the first gateway defined must be the local machine. By doing
            # this the initial create uses localhost, and places it's portal IP
            # in the gateway ip list. Once the gateway ip list is defined, the
            # api server can resolve against the gateways - until the list is
            # defined only a request from localhost is acceptable to the api
            self.logger.error("The first gateway defined must be the local "
                              "machine")
            return

        if skipchecks not in ['true', 'false']:
            self.logger.error("skipchecks must be either true or false")
            return

        if local_gw in current_gateways:
            current_gateways.remove(local_gw)

        config = self.parent.parent.parent._get_config()
        if not config:
            self.logger.error("Unable to refresh local config"
                              " over API - sync aborted, restart rbd-target-gw"
                              " on {} to sync".format(gateway_name))

        target_iqn = self.parent.name
        target_config = config['targets'][target_iqn]
        if nosync:
            sync_text = "sync skipped"
        else:
            sync_text = ("sync'ing {} disk(s) and "
                         "{} client(s)".format(len(target_config['disks']),
                                               len(target_config['clients'])))
        if skipchecks == 'true':
            self.logger.warning("OS version/package checks have been bypassed")

        self.logger.info("Adding gateway, {}".format(sync_text))

        gw_api = '{}://{}:{}/api'.format(self.http_mode,
                                         "localhost",
                                         settings.config.api_port)
        gw_rqst = gw_api + '/gateway/{}/{}'.format(target_iqn, gateway_name)
        gw_vars = {"nosync": nosync,
                   "skipchecks": skipchecks,
                   "ip_address": ip_address}

        api = APIRequest(gw_rqst, data=gw_vars)
        api.put()

        msg = response_message(api.response, self.logger)
        if api.response.status_code != 200:
            self.logger.error("Failed : {}".format(msg))
            return

        self.logger.debug("{}".format(msg))
        self.logger.debug("Adding gw to UI")

        # Target created OK, get the details back from the gateway and
        # add to the UI. We have to use the new gateway to ensure what
        # we get back is current (the other gateways will lag until they see
        # epoch xattr change on the config object)
        new_gw_endpoint = ('{}://{}:{}/'
                           'api'.format(self.http_mode,
                                        gateway_name,
                                        settings.config.api_port))

        config = self.parent.parent.parent._get_config(endpoint=new_gw_endpoint)
        target_config = config['targets'][target_iqn]
        portal_config = target_config['portals'][gateway_name]
        Gateway(self, gateway_name, portal_config)

        self.logger.info('ok')
예제 #2
0
    def ui_command_host(self, action, client_iqn):
        """
        use the 'host' sub-command to add and remove hosts from a host group.
        Adding a host will automatically map the host group's disks to that
        specific host. Removing a host however, does not change the hosts
        disk masking - it simply removes the host from group.

        e.g.
        host add|remove iqn.1994-05.com.redhat:rh7-client
        """

        if action not in HostGroup.valid_actions:
            self.logger.error("Invalid request - must be "
                              "host add|remove <client_iqn>")
            return

        target_iqn = self.parent.parent.name

        # basic checks
        client_group = self._get_client_group(target_iqn)
        client_map = client_group.client_map
        if client_iqn not in client_map:
            self.logger.error("'{}' is not managed by a "
                              "group".format(client_iqn))
            return

        current_group = client_map[client_iqn].group_name
        if action == 'add' and current_group:
            self.logger.error("'{}' already belongs to "
                              "'{}'".format(client_iqn, current_group))
            return
        elif action == 'remove' and current_group != self.name:
            self.logger.error("'{}' does not belong to this "
                              "group".format(client_iqn))
            return

        # Basic checks passed, hand-off to the API now
        group_api = ('{}://{}:{}/api/hostgroup/'
                     '{}/{}'.format(self.http_mode, "localhost",
                                    settings.config.api_port, target_iqn,
                                    self.name))

        api_vars = {"action": action, "members": client_iqn}

        api = APIRequest(group_api, data=api_vars)
        api.put()
        self.logger.debug("- api call responded "
                          "{}".format(api.response.status_code))
        if api.response.status_code != 200:
            self.logger.error("Failed :"
                              "{}".format(
                                  response_message(api.response, self.logger)))
            return

        # group updated, so update the UI
        self.logger.debug("Updating the UI")
        if action == 'add':
            HostGroupMember(self, 'host', client_iqn)
            self.update_clients_UI([client_iqn], target_iqn)

        elif action == 'remove':
            child = [
                child for child in self.children if child.name == client_iqn
            ][0]
            self.delete(child)

        self.logger.info('ok')
예제 #3
0
    def ui_command_discovery_auth(self,
                                  username=None,
                                  password=None,
                                  mutual_username=None,
                                  mutual_password=None):
        """
        Discovery authentication can be set to use CHAP/CHAP_MUTUAL by supplying
        username, password, mutual_username, mutual_password

        Specifying 'nochap' will remove discovery authentication.

        e.g.
        auth username=<user> password=<pass> mutual_username=<m_user> mutual_password=<m_pass>

        """

        self.logger.warn(
            "discovery username={}, password={}, mutual_username={}, "
            "mutual_password={}".format(username, password, mutual_username,
                                        mutual_password))

        self.logger.debug("CMD: /iscsi discovery_auth")

        if not username:
            self.logger.error(
                "To set or reset discovery authentication, specify either "
                "username=<user> password=<password> [mutual_username]=<user> "
                "[mutual_password]=<password> or nochap")
            return

        if username == 'nochap':
            username = ''
            password = ''
            mutual_username = ''
            mutual_password = ''

        self.logger.debug(
            "discovery auth to be set to username='******', password='******', "
            "mutual_username='******', mutual_password='******'".format(
                username, password, mutual_username, mutual_password))

        api_vars = {
            "username": username,
            "password": password,
            "mutual_username": mutual_username,
            "mutual_password": mutual_password
        }
        discoveryauth_api = ('{}://localhost:{}/api/'
                             'discoveryauth'.format(self.http_mode,
                                                    settings.config.api_port))
        api = APIRequest(discoveryauth_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            self._set_auth(username, password, mutual_username,
                           mutual_password)
            self.logger.info('ok')
        else:
            self.logger.error("Error: {}".format(
                response_message(api.response, self.logger)))
            return
예제 #4
0
    def ui_command_delete(self, gateway_name, confirm=None):
        """
        Delete a gateway from the group. This will stop and delete the target
        running on the gateway.

        If this is the last gateway the target is mapped to all objects added
        to it will be removed, and confirm=True is required.
        """

        self.logger.debug("CMD: ../gateways/ delete {} confirm {}".format(
            gateway_name, confirm))

        self.logger.info("Deleting gateway, {}".format(gateway_name))

        config = self.parent.parent.parent._get_config()
        if not config:
            self.logger.error("Unable to refresh local config over API - sync "
                              "aborted, restart rbd-target-api on {} to "
                              "sync".format(gateway_name))
            return

        target_iqn = self.parent.name

        gw_cnt = len(config['targets'][target_iqn]['portals'])
        if gw_cnt == 0:
            self.logger.error("Target is not mapped to any gateways.")
            return

        if gw_cnt == 1:
            confirm = self.ui_eval_param(confirm, 'bool', False)
            if not confirm:
                self.logger.error("Deleting the last gateway will remove all "
                                  "objects on this target. Use confirm=true")
                return

        gw_api = '{}://{}:{}/api'.format(self.http_mode, "localhost",
                                         settings.config.api_port)
        gw_rqst = gw_api + '/gateway/{}/{}'.format(target_iqn, gateway_name)

        api = APIRequest(gw_rqst)
        api.delete()

        msg = response_message(api.response, self.logger)
        if api.response.status_code != 200:
            self.logger.error("Failed : {}".format(msg))
            return

        self.logger.debug("{}".format(msg))
        self.logger.debug("Removing gw from UI")

        gw_object = self.get_child(gateway_name)
        self.remove_child(gw_object)

        config = self.parent.parent.parent._get_config()
        if not config:
            self.logger.error("Could not refresh disaply. Restart gwcli.")
        elif not config['targets'][target_iqn]['portals']:
            # no more gws so everything but the target is dropped.
            disks_object = self.parent.get_child("disks")
            disks_object.reset()

            hosts_grp_object = self.parent.get_child("host-groups")
            hosts_grp_object.reset()

            hosts_object = self.parent.get_child("hosts")
            hosts_object.reset()
예제 #5
0
    def create_disk(self,
                    pool=None,
                    image=None,
                    size=None,
                    count=1,
                    parent=None,
                    create_image=True,
                    backstore=None):

        rc = 0

        if not parent:
            parent = self

        local_gw = this_host()

        disk_key = "{}/{}".format(pool, image)

        if not self._valid_pool(pool):
            return

        self.logger.debug("Creating/mapping disk {}/{}".format(pool, image))

        # make call to local api server's disk endpoint
        disk_api = '{}://localhost:{}/api/disk/{}'.format(
            self.http_mode, settings.config.api_port, disk_key)
        api_vars = {
            'pool': pool,
            'owner': local_gw,
            'count': count,
            'mode': 'create',
            'create_image': 'true' if create_image else 'false',
            'backstore': backstore
        }
        if size:
            api_vars['size'] = size.upper()

        self.logger.debug("Issuing disk create request")

        api = APIRequest(disk_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            # rbd create and map successful across all gateways so request
            # it's details and add to the UI
            self.logger.debug("- LUN(s) ready on all gateways")
            self.logger.info("ok")

            self.logger.debug("Updating UI for the new disk(s)")
            for n in range(1, (int(count) + 1), 1):

                if int(count) > 1:
                    disk_key = "{}/{}{}".format(pool, image, n)
                else:
                    disk_key = "{}/{}".format(pool, image)

                disk_api = ('{}://localhost:{}/api/disk/'
                            '{}'.format(self.http_mode,
                                        settings.config.api_port, disk_key))

                api = APIRequest(disk_api)
                api.get()

                if api.response.status_code == 200:
                    try:
                        image_config = api.response.json()
                    except Exception:
                        raise GatewayAPIError("Malformed REST API response")

                    disk_pool = None
                    for current_disk_pool in self.children:
                        if current_disk_pool.name == pool:
                            disk_pool = current_disk_pool
                            break
                    if disk_pool:
                        Disk(disk_pool, disk_key, image_config)
                    else:
                        DiskPool(parent, pool, [image_config])
                    self.logger.debug("{} added to the UI".format(disk_key))
                else:
                    raise GatewayAPIError(
                        "Unable to retrieve disk details "
                        "for '{}' from the API".format(disk_key))

            ceph_pools = self.parent.ceph.cluster.pools
            ceph_pools.refresh()

        else:
            self.logger.error("Failed : {}".format(
                response_message(api.response, self.logger)))
            rc = 8

        return rc
예제 #6
0
    def ui_command_disk(self, action='add', disk=None, size=None):
        """
        Disks can be added or removed from the client one at a time using
        the 'disk' sub-command. Note that if the disk does not currently exist
        in the configuration, the cli will attempt to create it for you.

        e.g.
        disk add <pool_name.image_name> <size>
        disk remove <pool_name.image_name>

        Adding a disk will result in the disk occupying the client's next
        available lun id. Once allocated removing a LUN will not change the
        LUN id associations for the client.

        Note that if the client is a member of a host group, disk management
        *must* be performed at the group level. Attempting to add/remove disks
        at the client level will fail.

        """

        self.logger.debug("CMD: ../hosts/<client_iqn> disk action={}"
                          " disk={}".format(action, disk))

        valid_actions = ['add', 'remove']

        if not disk:
            self.logger.critical("You must supply a disk name to add/remove "
                                 "for this client")
            return

        if action not in valid_actions:
            self.logger.error("you can only add and remove disks - {} is "
                              "invalid ".format(action))
            return

        lun_list = [(lun.rbd_name, lun.lun_id) for lun in self.children]
        current_luns = Client.get_srtd_names(lun_list)

        if action == 'add':

            if disk not in current_luns:
                ui_root = self.get_ui_root()
                valid_disk_names = [
                    defined_disk.image_id
                    for defined_disk in ui_root.disks.children
                ]
            else:
                # disk provided is already mapped, so remind the user
                self.logger.error("Disk {} already mapped".format(disk))
                return
        else:
            valid_disk_names = current_luns

        if disk not in valid_disk_names:

            # if this is an add operation, we can create the disk on-the-fly
            # for the admin

            if action == 'add':
                ui_root = self.get_ui_root()
                ui_disks = ui_root.disks
                if not size:
                    self.logger.error("To auto-define the disk to the client"
                                      " you must provide a disk size")
                    return

                # a disk given here would be of the form pool.image
                try:
                    pool, image = disk.split('.')
                except ValueError:
                    self.logger.error(
                        "Invalid format. Use pool_name.disk_name")
                    return

                rc = ui_disks.create_disk(pool=pool, image=image, size=size)
                if rc == 0:
                    self.logger.debug("disk auto-define successful")
                else:
                    self.logger.error("disk auto-define failed({}), try "
                                      "using the /disks create "
                                      "command".format(rc))
                    return

            else:
                self.logger.error("disk '{}' is not mapped to this "
                                  "client ".format(disk))
                return

        # At this point we are either in add/remove mode, with a valid disk
        # to act upon
        self.logger.debug("Client '{}' update - {} disk "
                          "{}".format(self.client_iqn, action, disk))

        api_vars = {"disk": disk}

        clientlun_api = ('{}://localhost:{}/api/'
                         'clientlun/{}'.format(self.http_mode,
                                               settings.config.api_port,
                                               self.client_iqn))

        api = APIRequest(clientlun_api, data=api_vars)
        if action == 'add':
            api.put()
        else:
            api.delete()

        if api.response.status_code == 200:

            self.logger.debug("disk mapping updated successfully")

            if action == 'add':

                # The addition of the lun will get a lun id assigned so
                # we need to query the api server to get the new configuration
                # to be able to set the local cli entry correctly
                get_api_vars = {"disk": disk}

                clientlun_api = clientlun_api.replace('/clientlun/',
                                                      '/_clientlun/')

                self.logger.debug("Querying API to get mapped LUN information")
                api = APIRequest(clientlun_api, data=get_api_vars)
                api.get()

                if api.response.status_code == 200:
                    try:
                        lun_dict = api.response.json()['message']
                    except Exception:
                        self.logger.error("Malformed REST API response")
                        return

                    # now update the UI
                    lun_id = lun_dict[disk]['lun_id']
                    self.add_lun(disk, lun_id)

                else:
                    self.logger.error("Query for disk '{}' meta data "
                                      "failed".format(disk))
                    return

            else:

                # this was a remove request, so simply delete the child
                # MappedLun object corresponding to this rbd name
                mlun = [lun for lun in self.children
                        if lun.rbd_name == disk][0]
                self.remove_lun(mlun)

            self.logger.debug("configuration update successful")
            self.logger.info('ok')

        else:
            # the request to add/remove the disk for the client failed
            self.logger.error("disk {} for '{}' against {} failed"
                              "\n{}".format(
                                  action, disk, self.client_iqn,
                                  response_message(api.response, self.logger)))
            return
예제 #7
0
    def set_auth(self,
                 username=None,
                 password=None,
                 mutual_username=None,
                 mutual_password=None):

        self.logger.debug("username={}, password={}, mutual_username={}, "
                          "mutual_password={}".format(username, password,
                                                      mutual_username,
                                                      mutual_password))

        self.logger.debug("CMD: ../hosts/<client_iqn> auth *")

        if not username:
            self.logger.error(
                "To set or reset authentication, specify either "
                "username=<user> password=<password> "
                "[mutual_username]=<user> [mutual_password]=<password> "
                "or nochap")
            return

        if username == 'nochap':
            username = ''
            password = ''
            mutual_username = ''
            mutual_password = ''

        self.logger.debug(
            "auth to be set to username='******', password='******', mutual_username='******', "
            "mutual_password='******' for '{}'".format(username, password,
                                                   mutual_username,
                                                   mutual_password,
                                                   self.client_iqn))

        target_iqn = self.parent.parent.name

        api_vars = {
            "username": username,
            "password": password,
            "mutual_username": mutual_username,
            "mutual_password": mutual_password
        }

        clientauth_api = ('{}://localhost:{}/api/'
                          'clientauth/{}/{}'.format(self.http_mode,
                                                    settings.config.api_port,
                                                    target_iqn,
                                                    self.client_iqn))

        api = APIRequest(clientauth_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            self.logger.debug("- client credentials updated")
            self.auth['username'] = username
            self.auth['password'] = password
            self.auth['mutual_username'] = mutual_username
            self.auth['mutual_password'] = mutual_password
            self.logger.info('ok')

        else:
            self.logger.error("Failed to update the client's auth: "
                              "{}".format(
                                  response_message(api.response, self.logger)))
            return
예제 #8
0
    def create_disk(self,
                    pool=None,
                    image=None,
                    size=None,
                    count=1,
                    max_data_area_mb=None,
                    parent=None):

        rc = 0

        if not parent:
            parent = self

        local_gw = this_host()

        disk_key = "{}.{}".format(pool, image)

        if not self._valid_pool(pool):
            return

        self.logger.debug("Creating/mapping disk {}/{}".format(pool, image))

        # make call to local api server's disk endpoint
        disk_api = '{}://127.0.0.1:{}/api/disk/{}'.format(
            self.http_mode, settings.config.api_port, disk_key)

        api_vars = {
            'pool': pool,
            'size': size.upper(),
            'owner': local_gw,
            'count': count,
            'max_data_area_mb': max_data_area_mb,
            'mode': 'create'
        }

        self.logger.debug("Issuing disk create request")

        api = APIRequest(disk_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            # rbd create and map successful across all gateways so request
            # it's details and add to the UI
            self.logger.debug("- LUN(s) ready on all gateways")
            self.logger.info("ok")

            self.logger.debug("Updating UI for the new disk(s)")
            for n in range(1, (int(count) + 1), 1):

                if count > 1:
                    disk_key = "{}.{}{}".format(pool, image, n)
                else:
                    disk_key = "{}.{}".format(pool, image)

                disk_api = ('{}://127.0.0.1:{}/api/disk/'
                            '{}'.format(self.http_mode,
                                        settings.config.api_port, disk_key))

                api = APIRequest(disk_api)
                api.get()

                if api.response.status_code == 200:
                    try:
                        image_config = api.response.json()
                    except:
                        raise GatewayAPIError("Malformed REST API response")

                    Disk(parent, disk_key, image_config)
                    self.logger.debug("{} added to the UI".format(disk_key))
                else:
                    raise GatewayAPIError(
                        "Unable to retrieve disk details "
                        "for '{}' from the API".format(disk_key))

            ceph_pools = self.parent.ceph.local_ceph.pools
            ceph_pools.refresh()

        else:
            self.logger.error("Failed : {}".format(
                response_message(api.response, self.logger)))
            rc = 8

        return rc
예제 #9
0
    def ui_command_disk(self, action, disk_name):
        """
        use the 'disk' sub-command to add or remove a disk from a specific
        host group. Removing disks should be done with care, as the remove
        operation will be executed across all hosts defined to the host group.

        e.g.
        disk add|remove rbd.disk_1
        """

        if action not in HostGroup.valid_actions:
            self.logger.error("Invalid request - must be "
                              "disk add|remove <disk_image>")
            return

        # simple sanity checks
        # 1. does the disk exist in the configuration
        ui_root = self.get_ui_root()
        if disk_name not in [disk.name for disk in ui_root.disks.children]:
            self.logger.error("Disk '{}' is not defined within the "
                              "configuration".format(disk_name))
            return

        # 2. For an 'add' request, the disk must not already be in the host
        # group. Whereas, for a remove request the disk must exist.
        if action == 'add':
            if disk_name in self.disks:
                self.logger.error("'{}' is already defined to this "
                                  "host-group".format(disk_name))
                return
        else:
            if disk_name not in self.disks:
                self.logger.error("'{}' is not a member of this "
                                  "group".format(disk_name))
                return

        # Basic checks passed, hand-off to the API
        group_api = ('{}://{}:{}/api/hostgroup/'
                     '{}'.format(self.http_mode, "localhost",
                                 settings.config.api_port, self.name))

        api_vars = {"action": action, "disks": disk_name}

        api = APIRequest(group_api, data=api_vars)
        api.put()
        self.logger.debug("- api call responded {}".format(
            api.response.status_code))
        if api.response.status_code != 200:
            self.logger.error("Failed: "
                              "{}".format(
                                  response_message(api.response, self.logger)))
            return

        # group updated, so update the host-groups UI elements
        self.logger.debug("Updating the UI")
        if action == 'add':
            HostGroupMember(self, 'disk', disk_name)
        elif action == 'remove':
            child = [
                child for child in self.children if child.name == disk_name
            ][0]
            self.delete(child)

        self.update_clients_UI(self.members)

        self.logger.info('ok')
예제 #10
0
    def ui_command_auth(self, chap=None):
        """
        Client authentication can be set to use CHAP by supplying the
        a string of the form <username>/<password>

        e.g.
        auth chap=username/password | nochap

        username ... the username is 8-64 character string. Each character
                     may either be an alphanumeric or use one of the following
                     special characters .,:,-,@.
                     Consider using the hosts 'shortname' or the initiators IQN
                     value as the username

        password ... the password must be between 12-16 chars in length
                     containing alphanumeric characters, plus the following
                     special characters @,_,-

        WARNING: Using unsupported special characters may result in truncation,
                 resulting in failed logins.


        Specifying 'nochap' will remove chap authentication for the client
        across all gateways.

        """

        self.logger.debug("CMD: ../hosts/<client_iqn> auth *")

        if not chap:
            self.logger.error("To set or reset authentication, specify either "
                              "chap=<user>/<password> or nochap")
            return

        if chap == 'nochap':
            chap = ''
        else:
            # string could have been supplied as chap=user/password or
            # simply user/password - either way all we see is user/password
            if '/' not in chap:
                self.logger.error(
                    "CHAP format is invalid - must be a <username>/<password> "
                    "format. Use 'help auth' to show the correct syntax and "
                    "supported characters")
                return

        self.logger.debug(
            "CHAP to be set to '{}' for '{}'".format(chap, self.client_iqn))

        api_vars = {"chap": chap}

        clientauth_api = ('{}://127.0.0.1:{}/api/'
                          'clientauth/{}'.format(self.http_mode,
                                                 settings.config.api_port,
                                                 self.client_iqn))

        api = APIRequest(clientauth_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            self.logger.debug("- client credentials updated")
            self.auth['chap'] = chap
            self.logger.info('ok')

        else:
            self.logger.error("Failed to update the client's auth"
                              " :{}".format(response_message(api.response,
                                                             self.logger)))
            return
예제 #11
0
    def ui_command_auth(self,
                        username=None,
                        password=None,
                        mutual_username=None,
                        mutual_password=None):
        """
        Target authentication can be set to use CHAP/CHAP_MUTUAL by supplying
        username, password, mutual_username, mutual_password

        e.g.
        auth username=<user> password=<pass> mutual_username=<m_user> mutual_password=<m_pass>

        username / mutual_username ... the username is 8-64 character string. Each character
                                       may either be an alphanumeric or use one of the following
                                       special characters .,:,-,@.
                                       Consider using the hosts 'shortname' or the initiators IQN
                                       value as the username

        password / mutual_password ... the password must be between 12-16 chars in length
                                       containing alphanumeric characters, plus the following
                                       special characters @,_,-,/
        """

        self.logger.debug("CMD: /iscsi-targets/<target_iqn> auth *")

        if not username:
            self.logger.error(
                "To set authentication, specify "
                "username=<user> password=<password> "
                "[mutual_username]=<user> [mutual_password]=<password> "
                "or nochap")
            return

        if username == 'nochap':
            username = ''
            password = ''
            mutual_username = ''
            mutual_password = ''

        self.logger.debug(
            "auth to be set to username='******', password='******', mutual_username='******', "
            "mutual_password='******'".format(username, password, mutual_username,
                                          mutual_password))
        target_iqn = self.name

        api_vars = {
            "username": username,
            "password": password,
            "mutual_username": mutual_username,
            "mutual_password": mutual_password
        }

        targetauth_api = ('{}://localhost:{}/api/'
                          'targetauth/{}'.format(self.http_mode,
                                                 settings.config.api_port,
                                                 target_iqn))
        api = APIRequest(targetauth_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            self.logger.debug("- target credentials updated")
            self.auth['username'] = username
            self.auth['password'] = password
            self.auth['mutual_username'] = mutual_username
            self.auth['mutual_password'] = mutual_password
            self.logger.info('ok')

        else:
            self.logger.error("Failed to update target auth: "
                              "{}".format(
                                  response_message(api.response, self.logger)))
            return
예제 #12
0
    def set_auth(self, chap=None, chap_mutual=None):

        self.logger.warn("chap={}, chap_mutual={}".format(chap, chap_mutual))

        self.logger.debug("CMD: ../hosts/<client_iqn> auth *")

        if not chap:
            self.logger.error(
                "To set or reset authentication, specify either "
                "chap=<user>/<password> [chap_mutual]=<user>/<password> or nochap"
            )
            return

        if chap == 'nochap':
            chap = ''
        else:
            # string could have been supplied as chap=user/password or
            # simply user/password - either way all we see is user/password
            if '/' not in chap:
                self.logger.error(
                    "CHAP format is invalid - must be a <username>/<password> "
                    "format. Use 'help auth' to show the correct syntax and "
                    "supported characters")
                return

        if not chap_mutual:
            chap_mutual = ''
        else:
            if '/' not in chap_mutual:
                self.logger.error(
                    "CHAP_MUTUAL format is invalid - must be a <username>/<password> "
                    "format. Use 'help auth' to show the correct syntax and "
                    "supported characters")
                return

        self.logger.debug(
            "auth to be set to chap='{}', chap_mutual='{}' for '{}'".format(
                chap, chap_mutual, self.client_iqn))

        target_iqn = self.parent.parent.name

        api_vars = {"chap": chap, "chap_mutual": chap_mutual}

        clientauth_api = ('{}://localhost:{}/api/'
                          'clientauth/{}/{}'.format(self.http_mode,
                                                    settings.config.api_port,
                                                    target_iqn,
                                                    self.client_iqn))

        api = APIRequest(clientauth_api, data=api_vars)
        api.put()

        if api.response.status_code == 200:
            self.logger.debug("- client credentials updated")
            if chap != '':
                self.auth['chap'] = chap
            else:
                self.auth['chap'] = "None"
            if chap_mutual != '':
                self.auth['chap_mutual'] = chap_mutual
            else:
                self.auth['chap_mutual'] = "None"

            self.logger.info('ok')

        else:
            self.logger.error("Failed to update the client's auth"
                              " :{}".format(
                                  response_message(api.response, self.logger)))
            return
예제 #13
0
    def ui_command_delete(self, gateway_name, confirm=None):
        """
        Delete a gateway from the group. This will stop and delete the target
        running on the gateway.

        If this is the last gateway the target is mapped to all objects added
        to it will be removed, and confirm=True is required.
        """

        self.logger.debug("CMD: ../gateways/ delete {} confirm {}".format(
            gateway_name, confirm))

        self.logger.info("Deleting gateway, {}".format(gateway_name))

        confirm = self.ui_eval_param(confirm, 'bool', False)

        config = self.parent.parent.parent._get_config()
        if not config:
            self.logger.error("Unable to refresh local config over API - sync "
                              "aborted, restart rbd-target-api on {0} to "
                              "sync".format(gateway_name))
            return

        target_iqn = self.parent.name

        gw_cnt = len(config['targets'][target_iqn]['portals'])
        if gw_cnt == 0:
            self.logger.error("Target is not mapped to any gateways.")
            return

        if gw_cnt == 1:
            if not confirm:
                self.logger.error("Deleting the last gateway will remove all "
                                  "objects on this target. Use confirm=true")
                return

        gw_api = '{}://{}:{}/api'.format(self.http_mode, "localhost",
                                         settings.config.api_port)
        gw_rqst = gw_api + '/gateway/{}/{}'.format(target_iqn, gateway_name)
        if confirm:
            gw_vars = {"force": 'true'}
        else:
            gw_vars = {"force": 'false'}

        api = APIRequest(gw_rqst, data=gw_vars)
        api.delete()

        msg = response_message(api.response, self.logger)
        if api.response.status_code != 200:
            if "unavailable:" + gateway_name in msg:
                self.logger.error(
                    "Could not contact {}. If the gateway is "
                    "permanently down. Use confirm=true to "
                    "force removal. WARNING: Forcing removal of "
                    "a gateway that can still be reached by an "
                    "initiator may result in data corruption.".format(
                        gateway_name))
            else:
                self.logger.error("Failed : {}".format(msg))
            return

        self.logger.debug("{}".format(msg))
        self.logger.debug("Removing gw from UI")

        self.thread_lock.acquire()
        gw_object = self.get_child(gateway_name)
        self.remove_child(gw_object)
        self.thread_lock.release()

        config = self.parent.parent.parent._get_config()
        if not config:
            self.logger.error("Could not refresh display. Restart gwcli.")
            return
        elif not config['targets'][target_iqn]['portals']:
            # no more gws so everything but the target is dropped.
            disks_object = self.parent.get_child("disks")
            disks_object.reset()

            hosts_grp_object = self.parent.get_child("host-groups")
            hosts_grp_object.reset()

            hosts_object = self.parent.get_child("hosts")
            hosts_object.reset()