Esempio n. 1
0
    def set_boot_device(self, session, boot_device, persistent=False):
        """Set node boot device

        :param session: The session to use for making this request.
        :param boot_device: Boot device to assign to the node.
        :param persistent: If the boot device change is maintained after node
            reboot
        :return: The updated :class:`~openstack.baremetal.v1.node.Node`
        """
        session = self._get_session(session)
        version = self._get_microversion_for(session, 'commit')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'management', 'boot_device')

        body = {'boot_device': boot_device, 'persistent': persistent}

        response = session.put(
            request.url,
            json=body,
            headers=request.headers,
            microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        msg = ("Failed to set boot device for node {node}".format(
            node=self.id))
        exceptions.raise_from_response(response, error_message=msg)
Esempio n. 2
0
    def call_vendor_passthru(self, session, verb, method, body=None):
        """Call a vendor passthru method.

        :param session: The session to use for making this request.
        :param verb: The HTTP verb, one of GET, SET, POST, DELETE.
        :param method: The method to call using vendor_passthru.
        :param body: The JSON body in the HTTP call.
        :returns: The HTTP response.
        """
        session = self._get_session(session)
        version = self._get_microversion_for(session, 'commit')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url,
                                    'vendor_passthru?method={}'.format(method))

        call = getattr(session, verb.lower())
        response = call(request.url,
                        json=body,
                        headers=request.headers,
                        microversion=version,
                        retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        msg = ("Failed to call vendor_passthru for node {node}, verb {verb}"
               " and method {method}".format(node=self.id,
                                             verb=verb,
                                             method=method))
        exceptions.raise_from_response(response, error_message=msg)

        return response
Esempio n. 3
0
    def _metadata(self, method, key=None, clear=False, delete=False,
                  **metadata):
        for k, v in metadata.items():
            if not isinstance(v, six.string_types):
                raise ValueError("The value for %s (%s) must be "
                                 "a text string" % (k, v))

        # If we're in a ServerDetail, we need to pop the "detail" portion
        # of the URL off and then everything else will work the same.
        pos = self.base_path.find("detail")
        if pos != -1:
            base = self.base_path[:pos]
        else:
            base = self.base_path

        if key is not None:
            url = utils.urljoin(base, self.id, "metadata", key)
        else:
            url = utils.urljoin(base, self.id, "metadata")

        kwargs = {}
        if metadata or clear:
            # 'meta' is the key for singular modifications.
            # 'metadata' is the key for mass modifications.
            key = "meta" if key is not None else "metadata"
            kwargs["json"] = {key: metadata}

        headers = {"Accept": ""} if delete else {}

        response = method(url, headers=headers, **kwargs)

        # ensure Nova API has not returned us an error
        exceptions.raise_from_response(response)
        # DELETE doesn't return a JSON body while everything else does.
        return response.json() if not delete else None
Esempio n. 4
0
    def list(cls, session, ignore_missing=True, base_path=None, **params):
        session = cls._get_session(session)

        # concat attrs to build a new base_path while excluding unnecessary params
        excludes = set(
            ["paginated", "stream_name", "prepend_key", "query_cursor"])

        base_path = f'cursors?stream-name={params["stream_name"]}&'
        for key, value in params.items():
            if key not in excludes:
                base_path = base_path + key.replace("_",
                                                    "-") + "=" + value + "&"
        base_path = base_path[:-1]

        microversion = cls._get_microversion_for_list(session)

        data = session.get(base_path,
                           headers={"Accept": "application/json"},
                           params=None,
                           microversion=microversion)
        exceptions.raise_from_response(data)
        result = data.json()

        if result is not None:
            return result

        if ignore_missing:
            return None
        raise exceptions.ResourceNotFound("No %s found for %s" %
                                          (cls.__name__, params['name_or_id']))
Esempio n. 5
0
    def set_power_state(self, session, target):
        """Run an action modifying this node's power state.

        This call is asynchronous, it will return success as soon as the Bare
        Metal service acknowledges the request.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param target: Target power state, e.g. "rebooting", "power on".
            See the Bare Metal service documentation for available actions.
        """
        session = self._get_session(session)

        if target.startswith("soft "):
            version = '1.27'
        else:
            version = None

        version = utils.pick_microversion(session, version)

        # TODO(dtantsur): server timeout support
        body = {'target': target}

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'states', 'power')
        response = session.put(
            request.url, json=body,
            headers=request.headers, microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        msg = ("Failed to set power state for bare metal node {node} "
               "to {target}".format(node=self.id, target=target))
        exceptions.raise_from_response(response, error_message=msg)
Esempio n. 6
0
    def get_data(self, session, processed=True):
        """Get introspection data.

        Note that the introspection data format is not stable and can vary
        from environment to environment.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param processed: Whether to fetch the final processed data (the
            default) or the raw unprocessed data as received from the ramdisk.
        :type processed: bool
        :returns: introspection data from the most recent successful run.
        :rtype: dict
        """
        session = self._get_session(session)

        version = (self._get_microversion_for(session, 'fetch')
                   if processed else '1.17')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'data')
        if not processed:
            request.url = utils.urljoin(request.url, 'unprocessed')
        response = session.get(request.url,
                               headers=request.headers,
                               microversion=version)
        msg = ("Failed to fetch introspection data for node {id}".format(
            id=self.id))
        exceptions.raise_from_response(response, error_message=msg)
        return response.json()
Esempio n. 7
0
    def _translate_response(self, response, has_body=None, error_message=None):
        """Given a KSA response, inflate this instance with its data

        DELETE operations don't return a body, so only try to work
        with a body when has_body is True.

        This method updates attributes that correspond to headers
        and body on this instance and clears the dirty set.
        """
        if has_body is None:
            has_body = self.has_body
        exceptions.raise_from_response(response, error_message=error_message)
        if has_body:
            try:
                body = response.json()
                if self.resources_key and self.resources_key in body:
                    body = body[self.resources_key][0]
                body_attrs = self._consume_body_attrs(body)
                self._body.attributes.update(body_attrs)
                self._body.clean()

            except ValueError:
                # Server returned not parse-able response (202, 204, etc)
                # Do simply nothing
                pass

        headers = self._consume_header_attrs(response.headers)
        self._header.attributes.update(headers)
        self._header.clean()
        self._update_location()
        dict.update(self, self.to_dict())
Esempio n. 8
0
    def _upload_image_put(
            self, name, filename, meta, validate_checksum, **image_kwargs):
        image_data = open(filename, 'rb')

        properties = image_kwargs.pop('properties', {})

        image_kwargs.update(self._make_v2_image_params(meta, properties))
        image_kwargs['name'] = name

        image = self._create(_image.Image, **image_kwargs)

        image.data = image_data

        try:
            response = image.upload(self)
            exceptions.raise_from_response(response)
            # image_kwargs are flat here
            md5 = image_kwargs.get(self._IMAGE_MD5_KEY)
            sha256 = image_kwargs.get(self._IMAGE_SHA256_KEY)
            if validate_checksum and (md5 or sha256):
                # Verify that the hash computed remotely matches the local
                # value
                data = image.fetch(self)
                checksum = data.get('checksum')
                if checksum:
                    valid = (checksum == md5 or checksum == sha256)
                    if not valid:
                        raise Exception('Image checksum verification failed')
        except Exception:
            self.log.debug(
                "Deleting failed upload of image %s", name)
            self.delete_image(image.id)
            raise

        return image
Esempio n. 9
0
    def restore(self, session, backup=None, restore_time=None):
        """Restore instance from the backup of PIR.
        """
        url = utils.urljoin(self.base_path, 'recovery')

        body = {
            'source': None,
            'target': {'instance_id': self.id}
        }

        if backup:
            body['source'] = {'type': 'backup', 'backup_id': backup.id}
        elif restore_time:
            body['source'] = {
                'type': 'timestamp',
                'restore_time': restore_time
            }

        body['source']['instance_id'] = self.id

        response = session.post(url, json=body)
        exceptions.raise_from_response(response)
        job_id = response.json().get('job_id')

        return job_id
Esempio n. 10
0
    def remove_trait(self, session, trait, ignore_missing=True):
        """Remove a trait from a node.

        :param session: The session to use for making this request.
        :param trait: The trait to remove from the node.
        :param bool ignore_missing: When set to ``False``
            :class:`~openstack.exceptions.ResourceNotFound` will be
            raised when the trait does not exist.
            Otherwise, ``False`` is returned.
        :returns: The updated :class:`~openstack.baremetal.v1.node.Node`
        """
        session = self._get_session(session)
        version = utils.pick_microversion(session, '1.37')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'traits', trait)

        response = session.delete(
            request.url, headers=request.headers, microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        if ignore_missing or response.status_code == 400:
            session.log.debug(
                'Trait %(trait)s was already removed from node %(node)s',
                {'trait': trait, 'node': self.id})
            return False

        msg = ("Failed to remove trait {trait} from bare metal node {node}"
               .format(node=self.id, trait=trait))
        exceptions.raise_from_response(response, error_message=msg)

        self.traits = list(set(self.traits) - {trait})

        return True
Esempio n. 11
0
    def attach_vif(self, session, vif_id):
        """Attach a VIF to the node.

        The exact form of the VIF ID depends on the network interface used by
        the node. In the most common case it is a Network service port
        (NOT a Bare Metal port) ID. A VIF can only be attached to one node
        at a time.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param string vif_id: Backend-specific VIF ID.
        :return: ``None``
        :raises: :exc:`~openstack.exceptions.NotSupported` if the server
            does not support the VIF API.
        """
        session = self._get_session(session)
        version = self._assert_microversion_for(
            session, 'commit', _common.VIF_VERSION,
            error_message=("Cannot use VIF attachment API"))

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'vifs')
        body = {'id': vif_id}
        response = session.post(
            request.url, json=body,
            headers=request.headers, microversion=version,
            # NOTE(dtantsur): do not retry CONFLICT, it's a valid status code
            # in this API when the VIF is already attached to another node.
            retriable_status_codes=[503])

        msg = ("Failed to attach VIF {vif} to bare metal node {node}"
               .format(node=self.id, vif=vif_id))
        exceptions.raise_from_response(response, error_message=msg)
Esempio n. 12
0
    def set_traits(self, session, traits):
        """Set traits for a node.

        Removes any existing traits and adds the traits passed in to this
        method.

        :param session: The session to use for making this request.
        :param traits: list of traits to add to the node.
        :returns: The updated :class:`~openstack.baremetal.v1.node.Node`
        """
        session = self._get_session(session)
        version = utils.pick_microversion(session, '1.37')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'traits')

        body = {'traits': traits}

        response = session.put(
            request.url,
            json=body,
            headers=request.headers,
            microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        msg = ("Failed to set traits for node {node}".format(node=self.id))
        exceptions.raise_from_response(response, error_message=msg)

        self.traits = traits
Esempio n. 13
0
    def list_vifs(self, session):
        """List IDs of VIFs attached to the node.

        The exact form of the VIF ID depends on the network interface used by
        the node. In the most common case it is a Network service port
        (NOT a Bare Metal port) ID.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :return: List of VIF IDs as strings.
        :raises: :exc:`~openstack.exceptions.NotSupported` if the server
            does not support the VIF API.
        """
        session = self._get_session(session)
        version = self._assert_microversion_for(
            session,
            'fetch',
            _common.VIF_VERSION,
            error_message=("Cannot use VIF attachment API"))

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'vifs')
        response = session.get(request.url,
                               headers=request.headers,
                               microversion=version)

        msg = ("Failed to list VIFs attached to bare metal node {node}".format(
            node=self.id))
        exceptions.raise_from_response(response, error_message=msg)
        return [vif['id'] for vif in response.json()['vifs']]
Esempio n. 14
0
    def _upload_image_put(self, name, filename, meta, wait, timeout,
                          **image_kwargs):
        image_data = open(filename, 'rb')

        properties = image_kwargs.pop('properties', {})

        image_kwargs.update(self._make_v2_image_params(meta, properties))
        image_kwargs['name'] = name

        data = self.post('/images', json=image_kwargs)
        image = self._connection._get_and_munchify(key=None, data=data)

        try:
            response = self.put(
                '/images/{id}/file'.format(id=image.id),
                headers={'Content-Type': 'application/octet-stream'},
                data=image_data)
            exceptions.raise_from_response(response)
        except Exception:
            self._connection.log.debug("Deleting failed upload of image %s",
                                       name)
            try:
                response = self.delete('/images/{id}'.format(id=image.id))
                exceptions.raise_from_response(response)
            except exc.OpenStackCloudHTTPError:
                # We're just trying to clean up - if it doesn't work - shrug
                self._connection.log.warning(
                    "Failed deleting image after we failed uploading it.",
                    exc_info=True)
            raise

        return self._connection._normalize_image(image)
Esempio n. 15
0
def get_token_from_token(conn, token_id):
    response = conn.identity.post(
        '/auth/tokens',
        json={
            "auth": {
                "identity": {
                    "methods": ["token"],
                    "token": {
                        "id": token_id
                    }
                },
                "scope": {
                    "project": {
                        "name": conn.auth['project_name']
                    }
                }
            }
        },
        headers={
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'User-Agent': 'openstacksdk/0.48.1 keystoneauth1/4.2.1 '
            'python-requests/2.23.0 CPython/3.8.5',
            'X-Auth-Token': token_id
        })
    exceptions.raise_from_response(response)
    return (response.headers['X-Subject-Token'], response.json())
Esempio n. 16
0
 def _delete_tracker(self, session, tracker):
     """Delete Tracker
     """
     url = self.base_path + '?tracker_name={}'.format(tracker)
     response = session.delete(url)
     exceptions.raise_from_response(response)
     return None
Esempio n. 17
0
    def delete_topic(self, instance, topics, ignore_missing=True):
        """Delete topic on DMS instance

        :param instance: The instance id or an object instance of
            :class:`~otcextensions.sdk.dms.v1.instance.Instance`
        :param list topics: List of topic IDs
        :param bool ignore_missing: When set to ``False``
            :class:`~otcextensions.sdk.exceptions.ResourceNotFound` will be
            raised when the instance does not exist.
        :returns: `None`
        """
        instance_obj = self._get_resource(_instance.Instance, instance)

        topics_list = []
        if isinstance(topics, str):
            topics_list.append(topics)
        elif isinstance(topics, list):
            for i in topics:
                if isinstance(i, str):
                    topics_list.append(i)
                elif isinstance(i, _topic.Topic):
                    topics_list.append(i.id)

        response = self.post('/instances/%s/topics/delete' % (instance_obj.id),
                             json={'topics': topics_list})
        exceptions.raise_from_response(response)
Esempio n. 18
0
    def _translate_response(self, response, has_body=None, error_message=None):
        """Given a KSA response, inflate this instance with its data

        DELETE operations don't return a body, so only try to work
        with a body when has_body is True.

        This method updates attributes that correspond to headers
        and body on this instance and clears the dirty set.
        """
        if has_body is None:
            has_body = self.has_body
        exceptions.raise_from_response(response, error_message=error_message)
        if has_body:
            try:
                body = response.json()
                cluster = body['clusters'][0]['cluster']
                self.ca = cluster['certificate-authority-data']
                user = body['users'][0]['user']
                self.client_certificate = user['client-certificate-data']
                self.client_key = user['client-key-data']
                context = body['contexts'][0]
                self.context = {
                    'name': context['name'],
                    'cluster': cluster['server'],
                    'user': context['context']['user']
                }
            except ValueError:
                # Server returned not parse-able response (202, 204, etc)
                # Do simply nothing
                pass

        headers = self._consume_header_attrs(response.headers)
        self._header.attributes.update(headers)
        self._header.clean()
        self._update_location()
Esempio n. 19
0
 def precache_images(self, session, images):
     """Requests image pre-caching"""
     body = {'cache': images}
     url = utils.urljoin(self.base_path, self.id, 'images')
     response = session.post(
         url, json=body, microversion=self._max_microversion)
     exceptions.raise_from_response(response)
Esempio n. 20
0
    def get_logs(self, session, log_type, start_date, end_date,
                 offset, limit, level):
        """Get instance logs

        :param session: The session to use for making this request.
            :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param str log_type: The type of logs to query:
            'errorlog' or 'slowlog'.
        :param str start_date: Start date of the of the log query. Format:
            %Y-%m-%dT%H:%M:%S%z where z is the tzinfo in HHMM format.
        :param str end_date: End date of the of the log query. Format:
            %Y-%m-%dT%H:%M:%S%z where z is the tzinfo in HHMM format.
        :param int offset: .
        :param int limit: Specifies the number of records on a page. Its value
            range is from 1 to 100.
        :param str level: Specifies the log level.

        """
        url_params = log_type + '?' + '&'.join([
            'start_date=' + start_date,
            'end_date=' + end_date,
            'offset=' + str(offset),
            'limit=' + str(limit),
            'level=' + level
        ])
        url = utils.urljoin(self.base_path, self.id, url_params)
        response = session.get(url)
        exceptions.raise_from_response(response)
        return response.json()
Esempio n. 21
0
    def fetch(self, session, requires_id=True,
              base_path=None, error_message=None, **params):
        """Get a remote resource based on this instance.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param boolean requires_id: A boolean indicating whether resource ID
                                    should be part of the requested URI.
        :param str base_path: Base part of the URI for fetching resources, if
                              different from
                              :data:`~openstack.resource.Resource.base_path`.
        :param str error_message: An Error message to be returned if
                                  requested object does not exist.
        :param dict params: Additional parameters that can be consumed.
        :return: This :class:`Resource` instance.
        :raises: :exc:`~openstack.exceptions.MethodNotSupported` if
                 :data:`Resource.allow_fetch` is not set to ``True``.
        :raises: :exc:`~openstack.exceptions.ResourceNotFound` if
                 the resource was not found.
        """
        if not self.allow_fetch:
            raise exceptions.MethodNotSupported(self, "fetch")

        params = {
            'instance_id': self.instance_id,
            'backup_id': self.id
        }
        query_params = self._query_mapping._transpose(params, self)
        url = utils.urljoin(self.base_path) % params

        session = self._get_session(session)
        microversion = self._get_microversion_for(session, 'fetch')
        response = session.get(
            url, microversion=microversion,
            headers={"Accept": "application/json"},
            params=query_params.copy())

        exceptions.raise_from_response(response)

        try:
            body = response.json()
            if self.resources_key in body:
                body = body[self.resources_key]

            if not len(body) == 1:
                raise exceptions.SDKException('Not a single result returned')

            body = body[0]
            body_attrs = self._consume_body_attrs(body)
            self._body.attributes.update(body_attrs)
            self._body.clean()

        except ValueError:
            # Server returned not parse-able response (202, 204, etc)
            # Do simply nothing
            pass

        dict.update(self, self.to_dict())

        return self
Esempio n. 22
0
def revoke_token(conn, token):
    response = conn.identity.delete('/auth/tokens',
                                    headers={
                                        'X-Auth-Token': token,
                                        'X-Subject-Token': token
                                    })
    exceptions.raise_from_response(response)
Esempio n. 23
0
    def list(cls, session, ignore_missing=True, base_path=None, **params):
        session = cls._get_session(session)

        excludes = set(["paginated", "stream_name", "query_checkpoint", "id"])
        print(params)
        if len(params) > 2:
            base_path = f'checkpoints?stream_name={params["stream_name"]}&'
            for key, value in params.items():
                if key not in excludes:
                    base_path = base_path + key + "=" + value + "&"
            base_path = base_path[:-1]
        else:
            base_path = f'checkpoints?stream_name={params["stream_name"]}'

        microversion = cls._get_microversion_for_list(session)

        data = session.get(base_path,
                           headers={"Accept": "application/json"},
                           params=None,
                           microversion=microversion)
        exceptions.raise_from_response(data)
        result = data.json()
        print(result)
        if result is not None:
            return result

        if ignore_missing:
            return None
        raise exceptions.ResourceNotFound("No %s found for %s" %
                                          (cls.__name__, params['name_or_id']))
Esempio n. 24
0
def get_token_from_password(conn):
    response = conn.identity.post(
        '/auth/tokens',
        json={
            "auth": {
                "identity": {
                    "methods": ["password"],
                    "password": {
                        "user": {
                            "name": conn.auth['username'],
                            "domain": {
                                "name": conn.auth['user_domain_name']
                            },
                            "password": conn.auth['password']
                        }
                    }
                },
                "scope": {
                    "project": {
                        "name": conn.auth['project_name']
                    }
                }
            }
        })
    exceptions.raise_from_response(response)
    return (response.headers['X-Subject-Token'], response.json())
Esempio n. 25
0
    def _upload_image_put(self, name, filename, meta, validate_checksum,
                          **image_kwargs):
        image_data = open(filename, 'rb')

        properties = image_kwargs.pop('properties', {})

        image_kwargs.update(self._make_v2_image_params(meta, properties))
        image_kwargs['name'] = name

        image = self._create(_image.Image, **image_kwargs)

        image.data = image_data

        try:
            response = image.upload(self)
            exceptions.raise_from_response(response)
            # image_kwargs are flat here
            md5 = image_kwargs.get(self._IMAGE_MD5_KEY)
            sha256 = image_kwargs.get(self._IMAGE_SHA256_KEY)
            if validate_checksum and (md5 or sha256):
                # Verify that the hash computed remotely matches the local
                # value
                data = image.fetch(self)
                checksum = data.get('checksum')
                if checksum:
                    valid = (checksum == md5 or checksum == sha256)
                    if not valid:
                        raise Exception('Image checksum verification failed')
        except Exception:
            self.log.debug("Deleting failed upload of image %s", name)
            self.delete_image(image.id)
            raise

        return image
Esempio n. 26
0
 def _delete_response(self, response, error_message=None):
     exceptions.raise_from_response(response, error_message=error_message)
     location = response.headers['Location']
     action_id = location.split('/')[-1]
     action = _action.Action.existing(id=action_id,
                                      connection=self._connection)
     return action
Esempio n. 27
0
    def list(cls, session, paginated=True, base_path=None,
             allow_unknown_params=False, **params):

        if not cls.allow_list:
            raise exceptions.MethodNotSupported(cls, "list")
        session = cls._get_session(session)
        microversion = cls._get_microversion_for_list(session)

        if base_path is None:
            base_path = cls.base_path
        params = cls._query_mapping._validate(
            params, base_path=base_path,
            allow_unknown_params=allow_unknown_params)

        query_params = cls._query_mapping._transpose(params, cls)
        uri = base_path % params

        limit = query_params.get('limit')

        # Track the total number of resources yielded so we can paginate
        # swift objects
        total_yielded = query_params.get('offset', 0)
        while uri:
            # Copy query_params due to weird mock unittest interactions
            response = session.get(
                uri,
                headers={"Accept": "application/json"},
                params=query_params.copy(),
                microversion=microversion)
            exceptions.raise_from_response(response)
            data = response.json()

            # Discard any existing pagination keys
            query_params.pop('offset', None)
            query_params.pop('limit', None)

            if cls.resources_key:
                resources = data[cls.resources_key]
            else:
                resources = data

            if not isinstance(resources, list):
                resources = [resources]

            marker = None
            for raw_resource in resources:
                value = cls.existing(
                    microversion=microversion,
                    connection=session._get_connection(),
                    **raw_resource)
                marker = total_yielded + 1
                yield value
                total_yielded += 1

            if resources and paginated:
                uri, next_params = cls._get_next_link(
                    uri, response, data, marker, limit, total_yielded)
                query_params.update(next_params)
            else:
                return
Esempio n. 28
0
    def list_vifs(self, session):
        """List IDs of VIFs attached to the node.

        The exact form of the VIF ID depends on the network interface used by
        the node. In the most common case it is a Network service port
        (NOT a Bare Metal port) ID.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :return: List of VIF IDs as strings.
        :raises: :exc:`~openstack.exceptions.NotSupported` if the server
            does not support the VIF API.
        """
        session = self._get_session(session)
        version = self._assert_microversion_for(
            session, 'fetch', _common.VIF_VERSION,
            error_message=("Cannot use VIF attachment API"))

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'vifs')
        response = session.get(
            request.url, headers=request.headers, microversion=version)

        msg = ("Failed to list VIFs attached to bare metal node {node}"
               .format(node=self.id))
        exceptions.raise_from_response(response, error_message=msg)
        return [vif['id'] for vif in response.json()['vifs']]
Esempio n. 29
0
    def delete_security_group_rule(self, rule_id):
        """Delete a security group rule

        :param string rule_id: The unique ID of the security group rule.

        :returns: True if delete succeeded, False otherwise.

        :raises: OpenStackCloudException on operation error.
        :raises: OpenStackCloudUnavailableFeature if security groups are
                 not supported on this cloud.
        """
        # Security groups not supported
        if not self._has_secgroups():
            raise exc.OpenStackCloudUnavailableFeature(
                "Unavailable feature: security groups")

        if self._use_neutron_secgroups():
            self.network.delete_security_group_rule(rule_id,
                                                    ignore_missing=False)
            return True

        else:
            try:
                exceptions.raise_from_response(
                    self.compute.delete(
                        '/os-security-group-rules/{id}'.format(id=rule_id)))
            except exc.OpenStackCloudResourceNotFound:
                return False

            return True
Esempio n. 30
0
 def _set_peering(self, session, set_status):
     """Accept/Reject Peering Request"""
     url = utils.urljoin(self.base_path, self.id, set_status)
     response = session.put(url)
     exceptions.raise_from_response(response)
     self._translate_response(response)
     return self
    def list(cls,
             session,
             paginated=True,
             base_path=None,
             allow_unknown_params=False,
             **params):

        if not cls.allow_list:
            raise exceptions.MethodNotSupported(cls, "list")
        session = cls._get_session(session)
        microversion = cls._get_microversion_for_list(session)

        if base_path is None:
            base_path = cls.base_path

        response = session.get(base_path,
                               headers={"Accept": "application/json"},
                               microversion=microversion)
        exceptions.raise_from_response(response)
        data = response.json()

        resources = data['availability_zones']

        if not isinstance(resources, list):
            resources = [resources]

        for raw_resource in resources[0]:
            value = cls.existing(microversion=microversion,
                                 connection=session._get_connection(),
                                 **raw_resource)
            yield value
Esempio n. 32
0
    def set_power_state(self, session, target):
        """Run an action modifying this node's power state.

        This call is asynchronous, it will return success as soon as the Bare
        Metal service acknowledges the request.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param target: Target power state, e.g. "rebooting", "power on".
            See the Bare Metal service documentation for available actions.
        """
        session = self._get_session(session)

        if target.startswith("soft "):
            version = '1.27'
        else:
            version = None

        version = utils.pick_microversion(session, version)

        # TODO(dtantsur): server timeout support
        body = {'target': target}

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'states', 'power')
        response = session.put(
            request.url,
            json=body,
            headers=request.headers,
            microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        msg = ("Failed to set power state for bare metal node {node} "
               "to {target}".format(node=self.id, target=target))
        exceptions.raise_from_response(response, error_message=msg)
Esempio n. 33
0
    def _upload_image_put(
        self, name, filename, data, meta,
        validate_checksum, use_import=False,
        stores=None,
        all_stores=None,
        all_stores_must_succeed=None,
        **image_kwargs,
    ):
        if filename and not data:
            image_data = open(filename, 'rb')
        else:
            image_data = data

        properties = image_kwargs.pop('properties', {})

        image_kwargs.update(self._make_v2_image_params(meta, properties))
        image_kwargs['name'] = name

        image = self._create(_image.Image, **image_kwargs)

        image.data = image_data
        supports_import = (
            image.image_import_methods
            and 'glance-direct' in image.image_import_methods
        )
        if stores or all_stores or all_stores_must_succeed:
            use_import = True
        if use_import and not supports_import:
            raise exceptions.SDKException(
                "Importing image was requested but the cloud does not"
                " support the image import method.")

        try:
            if not use_import:
                response = image.upload(self)
                exceptions.raise_from_response(response)
            if use_import:
                image.stage(self)
                image.import_image(self)

            # image_kwargs are flat here
            md5 = image_kwargs.get(self._IMAGE_MD5_KEY)
            sha256 = image_kwargs.get(self._IMAGE_SHA256_KEY)
            if validate_checksum and (md5 or sha256):
                # Verify that the hash computed remotely matches the local
                # value
                data = image.fetch(self)
                checksum = data.get('checksum')
                if checksum:
                    valid = (checksum == md5 or checksum == sha256)
                    if not valid:
                        raise Exception('Image checksum verification failed')
        except Exception:
            self.log.debug(
                "Deleting failed upload of image %s", name)
            self.delete_image(image.id)
            raise

        return image
Esempio n. 34
0
 def _delete_response(self, response, error_message=None):
     exceptions.raise_from_response(response, error_message=error_message)
     location = response.headers['Location']
     action_id = location.split('/')[-1]
     action = _action.Action.existing(
         id=action_id,
         connection=self._connection)
     return action
Esempio n. 35
0
    def _download(self, session, error_message=None, stream=False):
        request = self._prepare_request()
        request.headers['Accept'] = 'bytes'

        response = session.get(
            request.url, headers=request.headers, stream=stream)
        exceptions.raise_from_response(response, error_message=error_message)
        return response
Esempio n. 36
0
    def _action(self, session, action, body):
        """Preform actions given the message body.

        """
        url = utils.urljoin(self.base_path, self.id, 'tasks', action)
        response = session.post(url, json=body)
        exceptions.raise_from_response(response)
        return response
Esempio n. 37
0
 def delete_metadata(self, session, keys):
     request = self._prepare_request()
     headers = {key: '' for key in keys}
     response = session.post(
         request.url,
         headers=self._calculate_headers(headers))
     exceptions.raise_from_response(
         response, error_message="Error deleting metadata keys")
     return self
Esempio n. 38
0
    def _action(self, session, action, body):
        """Preform actions given the message body.

        """
        url = utils.urljoin(self.base_path, self.id, 'tasks', action)
        response = session.post(
            url,
            json=body)
        exceptions.raise_from_response(response)
        return response
Esempio n. 39
0
 def _do_maintenance_action(self, session, verb, body=None):
     session = self._get_session(session)
     version = self._get_microversion_for(session, 'commit')
     request = self._prepare_request(requires_id=True)
     request.url = utils.urljoin(request.url, 'maintenance')
     response = getattr(session, verb)(
         request.url, json=body,
         headers=request.headers, microversion=version)
     msg = ("Failed to change maintenance mode for node {node}"
            .format(node=self.id))
     exceptions.raise_from_response(response, error_message=msg)
Esempio n. 40
0
    def delete_metadata(self, session, keys):
        if not keys:
            return
        # If we have an empty object, update it from the remote side so that
        # we have a copy of the original metadata. Deleting metadata requires
        # POSTing and overwriting all of the metadata. If we already have
        # metadata locally, assume this is an existing object.
        if not self.metadata:
            self.head(session)

        metadata = copy.deepcopy(self.metadata)

        # Include any original system metadata so it doesn't get erased on POST
        for key in self._system_metadata:
            value = getattr(self, key)
            if value:
                metadata[key] = value

        # Remove the requested metadata keys
        # TODO(mordred) Why don't we just look at self._header_mapping()
        # instead of having system_metadata?
        deleted = False
        attr_keys_to_delete = set()
        for key in keys:
            if key == 'delete_after':
                del(metadata['delete_at'])
            else:
                if key in metadata:
                    del(metadata[key])
                    # Delete the attribute from the local copy of the object.
                    # Metadata that doesn't have Component attributes is
                    # handled by self.metadata being reset when we run
                    # self.head
                    if hasattr(self, key):
                        attr_keys_to_delete.add(key)
                    deleted = True

        # Nothing to delete, skip the POST
        if not deleted:
            return self

        request = self._prepare_request()
        response = session.post(
            request.url, headers=self._calculate_headers(metadata))
        exceptions.raise_from_response(
            response, error_message="Error deleting metadata keys")

        # Only delete from local object if the remote delete was successful
        for key in attr_keys_to_delete:
            delattr(self, key)

        # Just update ourselves from remote again.
        return self.head(session)
Esempio n. 41
0
def _json_response(response, result_key=None, error_message=None):
    """Temporary method to use to bridge from ShadeAdapter to SDK calls."""
    exceptions.raise_from_response(response, error_message=error_message)

    if not response.content:
        # This doesn't have any content
        return response

    # Some REST calls do not return json content. Don't decode it.
    if 'application/json' not in response.headers.get('Content-Type'):
        return response

    try:
        result_json = response.json()
    except JSONDecodeError:
        return response
    return result_json
Esempio n. 42
0
    def abort(self, session):
        """Abort introspection.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        """
        if self.is_finished:
            return

        session = self._get_session(session)

        version = self._get_microversion_for(session, 'delete')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'abort')
        response = session.post(
            request.url, headers=request.headers, microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)
        msg = ("Failed to abort introspection for node {id}"
               .format(id=self.id))
        exceptions.raise_from_response(response, error_message=msg)
Esempio n. 43
0
    def validate(self, session, required=('boot', 'deploy', 'power')):
        """Validate required information on a node.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param required: List of interfaces that are required to pass
            validation. The default value is the list of minimum required
            interfaces for provisioning.

        :return: dict mapping interface names to :class:`ValidationResult`
            objects.
        :raises: :exc:`~openstack.exceptions.ValidationException` if validation
            fails for a required interface.
        """
        session = self._get_session(session)
        version = self._get_microversion_for(session, 'fetch')

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'validate')
        response = session.get(request.url, headers=request.headers,
                               microversion=version)

        msg = ("Failed to validate node {node}".format(node=self.id))
        exceptions.raise_from_response(response, error_message=msg)
        result = response.json()

        if required:
            failed = [
                '%s (%s)' % (key, value.get('reason', 'no reason'))
                for key, value in result.items()
                if key in required and not value.get('result')
            ]

            if failed:
                raise exceptions.ValidationException(
                    'Validation failed for required interfaces of node {node}:'
                    ' {failures}'.format(node=self.id,
                                         failures=', '.join(failed)))

        return {key: ValidationResult(value.get('result'), value.get('reason'))
                for key, value in result.items()}
Esempio n. 44
0
    def get_data(self, session):
        """Get introspection data.

        Note that the introspection data format is not stable and can vary
        from environment to environment.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :returns: introspection data from the most recent successful run.
        :rtype: dict
        """
        session = self._get_session(session)

        version = self._get_microversion_for(session, 'fetch')
        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'data')
        response = session.get(
            request.url, headers=request.headers, microversion=version)
        msg = ("Failed to fetch introspection data for node {id}"
               .format(id=self.id))
        exceptions.raise_from_response(response, error_message=msg)
        return response.json()
Esempio n. 45
0
    def delete_security_group(self, name_or_id):
        """Delete a security group

        :param string name_or_id: The name or unique ID of the security group.

        :returns: True if delete succeeded, False otherwise.

        :raises: OpenStackCloudException on operation error.
        :raises: OpenStackCloudUnavailableFeature if security groups are
                 not supported on this cloud.
        """
        # Security groups not supported
        if not self._has_secgroups():
            raise exc.OpenStackCloudUnavailableFeature(
                "Unavailable feature: security groups"
            )

        # TODO(mordred): Let's come back and stop doing a GET before we do
        #                the delete.
        secgroup = self.get_security_group(name_or_id)
        if secgroup is None:
            self.log.debug('Security group %s not found for deleting',
                           name_or_id)
            return False

        if self._use_neutron_secgroups():
            exceptions.raise_from_response(
                self.network.delete(
                    '/security-groups/{sg_id}.json'.format(
                        sg_id=secgroup['id'])),
                error_message="Error deleting security group {0}".format(
                    name_or_id)
            )
            return True

        else:
            proxy._json_response(self.compute.delete(
                '/os-security-groups/{id}'.format(id=secgroup['id'])))
            return True
Esempio n. 46
0
    def detach_vif(self, session, vif_id, ignore_missing=True):
        """Detach a VIF from the node.

        The exact form of the VIF ID depends on the network interface used by
        the node. In the most common case it is a Network service port
        (NOT a Bare Metal port) ID.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param string vif_id: Backend-specific VIF ID.
        :param bool ignore_missing: When set to ``False``
                    :class:`~openstack.exceptions.ResourceNotFound` will be
                    raised when the VIF does not exist. Otherwise, ``False``
                    is returned.
        :return: ``True`` if the VIF was detached, otherwise ``False``.
        :raises: :exc:`~openstack.exceptions.NotSupported` if the server
            does not support the VIF API.
        """
        session = self._get_session(session)
        version = self._assert_microversion_for(
            session, 'commit', _common.VIF_VERSION,
            error_message=("Cannot use VIF attachment API"))

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'vifs', vif_id)
        response = session.delete(
            request.url, headers=request.headers, microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        if ignore_missing and response.status_code == 400:
            session.log.debug(
                'VIF %(vif)s was already removed from node %(node)s',
                {'vif': vif_id, 'node': self.id})
            return False

        msg = ("Failed to detach VIF {vif} from bare metal node {node}"
               .format(node=self.id, vif=vif_id))
        exceptions.raise_from_response(response, error_message=msg)
        return True
Esempio n. 47
0
    def attach_vif(self, session, vif_id, retry_on_conflict=True):
        """Attach a VIF to the node.

        The exact form of the VIF ID depends on the network interface used by
        the node. In the most common case it is a Network service port
        (NOT a Bare Metal port) ID. A VIF can only be attached to one node
        at a time.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param string vif_id: Backend-specific VIF ID.
        :param retry_on_conflict: Whether to retry HTTP CONFLICT errors.
            This can happen when either the VIF is already used on a node or
            the node is locked. Since the latter happens more often, the
            default value is True.
        :return: ``None``
        :raises: :exc:`~openstack.exceptions.NotSupported` if the server
            does not support the VIF API.
        """
        session = self._get_session(session)
        version = self._assert_microversion_for(
            session, 'commit', _common.VIF_VERSION,
            error_message=("Cannot use VIF attachment API"))

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'vifs')
        body = {'id': vif_id}
        retriable_status_codes = _common.RETRIABLE_STATUS_CODES
        if not retry_on_conflict:
            retriable_status_codes = set(retriable_status_codes) - {409}
        response = session.post(
            request.url, json=body,
            headers=request.headers, microversion=version,
            retriable_status_codes=retriable_status_codes)

        msg = ("Failed to attach VIF {vif} to bare metal node {node}"
               .format(node=self.id, vif=vif_id))
        exceptions.raise_from_response(response, error_message=msg)
Esempio n. 48
0
    def delete_security_group_rule(self, rule_id):
        """Delete a security group rule

        :param string rule_id: The unique ID of the security group rule.

        :returns: True if delete succeeded, False otherwise.

        :raises: OpenStackCloudException on operation error.
        :raises: OpenStackCloudUnavailableFeature if security groups are
                 not supported on this cloud.
        """
        # Security groups not supported
        if not self._has_secgroups():
            raise exc.OpenStackCloudUnavailableFeature(
                "Unavailable feature: security groups"
            )

        if self._use_neutron_secgroups():
            try:
                exceptions.raise_from_response(
                    self.network.delete(
                        '/security-group-rules/{sg_id}.json'.format(
                            sg_id=rule_id)),
                    error_message="Error deleting security group rule "
                                  "{0}".format(rule_id))
            except exc.OpenStackCloudResourceNotFound:
                return False
            return True

        else:
            try:
                exceptions.raise_from_response(
                    self.compute.delete(
                        '/os-security-group-rules/{id}'.format(id=rule_id)))
            except exc.OpenStackCloudResourceNotFound:
                return False

            return True
Esempio n. 49
0
    def _translate_response(self, response, has_body=None, error_message=None):
        """Given a KSA response, inflate this instance with its data

        DELETE operations don't return a body, so only try to work
        with a body when has_body is True.

        This method updates attributes that correspond to headers
        and body on this instance and clears the dirty set.
        """
        if has_body is None:
            has_body = self.has_body
        exceptions.raise_from_response(response, error_message=error_message)
        if has_body:
            body = response.json()
            if self.resource_key and self.resource_key in body:
                body = body[self.resource_key]

            body = self._consume_body_attrs(body)
            self._body.attributes.update(body)
            self._body.clean()

        headers = self._consume_header_attrs(response.headers)
        self._header.attributes.update(headers)
        self._header.clean()
Esempio n. 50
0
 def _do_raise(self, *args, **kwargs):
     return exceptions.raise_from_response(*args, **kwargs)
Esempio n. 51
0
    def list(cls, session, paginated=False, **params):
        """This method is a generator which yields resource objects.

        This resource object list generator handles pagination and takes query
        params for response filtering.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param bool paginated: ``True`` if a GET to this resource returns
                               a paginated series of responses, or ``False``
                               if a GET returns only one page of data.
                               **When paginated is False only one
                               page of data will be returned regardless
                               of the API's support of pagination.**
        :param dict params: These keyword arguments are passed through the
            :meth:`~openstack.resource.QueryParamter._transpose` method
            to find if any of them match expected query parameters to be
            sent in the *params* argument to
            :meth:`~keystoneauth1.adapter.Adapter.get`. They are additionally
            checked against the
            :data:`~openstack.resource.Resource.base_path` format string
            to see if any path fragments need to be filled in by the contents
            of this argument.

        :return: A generator of :class:`Resource` objects.
        :raises: :exc:`~openstack.exceptions.MethodNotSupported` if
                 :data:`Resource.allow_list` is not set to ``True``.
        :raises: :exc:`~openstack.exceptions.InvalidResourceQuery` if query
                 contains invalid params.
        """
        if not cls.allow_list:
            raise exceptions.MethodNotSupported(cls, "list")
        session = cls._get_session(session)

        cls._query_mapping._validate(params, base_path=cls.base_path)
        query_params = cls._query_mapping._transpose(params)
        uri = cls.base_path % params

        limit = query_params.get('limit')

        # Track the total number of resources yielded so we can paginate
        # swift objects
        total_yielded = 0
        while uri:
            # Copy query_params due to weird mock unittest interactions
            response = session.get(
                uri,
                headers={"Accept": "application/json"},
                params=query_params.copy())
            exceptions.raise_from_response(response)
            data = response.json()

            # Discard any existing pagination keys
            query_params.pop('marker', None)
            query_params.pop('limit', None)

            if cls.resources_key:
                resources = data[cls.resources_key]
            else:
                resources = data

            if not isinstance(resources, list):
                resources = [resources]

            marker = None
            for raw_resource in resources:
                # Do not allow keys called "self" through. Glance chose
                # to name a key "self", so we need to pop it out because
                # we can't send it through cls.existing and into the
                # Resource initializer. "self" is already the first
                # argument and is practically a reserved word.
                raw_resource.pop("self", None)

                value = cls.existing(**raw_resource)
                marker = value.id
                yield value
                total_yielded += 1

            if resources and paginated:
                uri, next_params = cls._get_next_link(
                    uri, response, data, marker, limit, total_yielded)
                query_params.update(next_params)
            else:
                return
Esempio n. 52
0
    def set_provision_state(self, session, target, config_drive=None,
                            clean_steps=None, rescue_password=None,
                            wait=False, timeout=None):
        """Run an action modifying this node's provision state.

        This call is asynchronous, it will return success as soon as the Bare
        Metal service acknowledges the request.

        :param session: The session to use for making this request.
        :type session: :class:`~keystoneauth1.adapter.Adapter`
        :param target: Provisioning action, e.g. ``active``, ``provide``.
            See the Bare Metal service documentation for available actions.
        :param config_drive: Config drive to pass to the node, only valid
            for ``active` and ``rebuild`` targets. You can use functions from
            :mod:`openstack.baremetal.configdrive` to build it.
        :param clean_steps: Clean steps to execute, only valid for ``clean``
            target.
        :param rescue_password: Password for the rescue operation, only valid
            for ``rescue`` target.
        :param wait: Whether to wait for the target state to be reached.
        :param timeout: Timeout (in seconds) to wait for the target state to be
            reached. If ``None``, wait without timeout.

        :return: This :class:`Node` instance.
        :raises: ValueError if ``config_drive``, ``clean_steps`` or
            ``rescue_password`` are provided with an invalid ``target``.
        """
        session = self._get_session(session)

        version = None
        if target in _common.PROVISIONING_VERSIONS:
            version = '1.%d' % _common.PROVISIONING_VERSIONS[target]

        if config_drive:
            # Some config drive actions require a higher version.
            if isinstance(config_drive, dict):
                version = '1.56'
            elif target == 'rebuild':
                version = '1.35'

        version = utils.pick_microversion(session, version)

        body = {'target': target}
        if config_drive:
            if target not in ('active', 'rebuild'):
                raise ValueError('Config drive can only be provided with '
                                 '"active" and "rebuild" targets')
            # Not a typo - ironic accepts "configdrive" (without underscore)
            body['configdrive'] = config_drive

        if clean_steps is not None:
            if target != 'clean':
                raise ValueError('Clean steps can only be provided with '
                                 '"clean" target')
            body['clean_steps'] = clean_steps

        if rescue_password is not None:
            if target != 'rescue':
                raise ValueError('Rescue password can only be provided with '
                                 '"rescue" target')
            body['rescue_password'] = rescue_password

        if wait:
            try:
                expected_state = _common.EXPECTED_STATES[target]
            except KeyError:
                raise ValueError('For target %s the expected state is not '
                                 'known, cannot wait for it' % target)

        request = self._prepare_request(requires_id=True)
        request.url = utils.urljoin(request.url, 'states', 'provision')
        response = session.put(
            request.url, json=body,
            headers=request.headers, microversion=version,
            retriable_status_codes=_common.RETRIABLE_STATUS_CODES)

        msg = ("Failed to set provision state for bare metal node {node} "
               "to {target}".format(node=self.id, target=target))
        exceptions.raise_from_response(response, error_message=msg)

        if wait:
            return self.wait_for_provision_state(session,
                                                 expected_state,
                                                 timeout=timeout)
        else:
            return self.fetch(session)