Exemple #1
0
def validate_json(json_string, schema):
    """
    invokes the validate function of jsonschema
    """
    schema_dict = json.loads(schema)
    schema_title = schema_dict['title']
    try:
        validate(json_string, schema_dict)
    except ValidationError as err:
        title = 'JSON validation failed: {}'.format(err.message)
        description = 'Failed validator: {} : {}'.format(
            err.validator, err.validator_value)
        LOG.error(title)
        LOG.error(description)
        raise InvalidFormatError(
            title=title,
            description=description,
        )
    except SchemaError as err:
        title = 'SchemaError: Unable to validate JSON: {}'.format(err)
        description = 'Invalid Schema: {}'.format(schema_title)
        LOG.error(title)
        LOG.error(description)
        raise AppError(title=title, description=description)
    except FormatError as err:
        title = 'FormatError: Unable to validate JSON: {}'.format(err)
        description = 'Invalid Format: {}'.format(schema_title)
        LOG.error(title)
        LOG.error(description)
        raise AppError(title=title, description=description)
    def get_configdocs_status(self):
        """
        Returns a list of the configdocs, committed or in buffer, and their
        current committed and buffer statuses
        """
        configdocs_status = []

        # If there is no committed revision, then it's 0.
        # new revision is ok because we just checked for buffer emptiness
        old_revision_id = self._get_committed_rev_id() or 0
        new_revision_id = self._get_buffer_rev_id() or old_revision_id

        try:
            diff = self.deckhand.get_diff(old_revision_id=old_revision_id,
                                          new_revision_id=new_revision_id)

        except DeckhandResponseError as drex:
            raise AppError(
                title='Unable to retrieve revisions',
                description=(
                    'Deckhand has responded unexpectedly: {}:{}'.format(
                        drex.status_code, drex.response_message)),
                status=falcon.HTTP_500,
                retry=False,
            )

        for collection_id in diff:
            collection = {"collection_name": collection_id}
            if diff[collection_id] in [
                    "unmodified", "modified", "created", "deleted"
            ]:
                collection['buffer_status'] = diff[collection_id]
                if diff[collection_id] == "created":
                    collection['committed_status'] = 'not present'
                else:
                    collection['committed_status'] = 'present'

            else:
                raise AppError(
                    title='Invalid collection status',
                    description=(
                        'Collection_id, {} has an invalid collection status. '
                        'unmodified, modified, created, and deleted are the'
                        ' only valid collection statuses.', collection_id),
                    status=falcon.HTTP_500,
                    retry=False,
                )
            configdocs_status.append(collection)

        return configdocs_status
Exemple #3
0
    def _get_versions_name_id(self, ordered_versions):

        # Get version id
        old_version_id = self.get_revision_id(ordered_versions[0])
        new_version_id = self.get_revision_id(ordered_versions[1])

        # Get revision name
        old_version_name = ordered_versions[0]
        new_version_name = ordered_versions[1]

        # Check that revision id of LAST_SITE_ACTION and SUCCESSFUL_SITE_ACTION
        # is not None
        for name, rev_id in [(old_version_name, old_version_id),
                             (new_version_name, new_version_id)]:
            if (name in [LAST_SITE_ACTION, SUCCESSFUL_SITE_ACTION]
                    and rev_id is None):
                raise AppError(
                    title='Version does not exist',
                    description='{} version does not exist'.format(name),
                    status=falcon.HTTP_404,
                    retry=False)

        # Set to 0 if there is no committed version
        if old_version_name == COMMITTED and old_version_id is None:
            old_version_id = 0
            new_version_id = self.get_revision_id(BUFFER) or 0

        # Set new_version_id if None
        if new_version_id is None:
            new_version_id = (self.get_revision_id(BUFFER) or old_version_id
                              or 0)

        return (old_version_name, new_version_name, old_version_id,
                new_version_id)
    def is_collection_in_buffer(self, collection_id):
        """
        Returns if the collection is represented in the buffer
        """
        if self.is_buffer_empty():
            return False

        # If there is no committed revision, then it's 0.
        # new revision is ok because we just checked for buffer emptiness
        old_revision_id = self._get_committed_rev_id() or 0

        try:
            diff = self.deckhand.get_diff(
                old_revision_id=old_revision_id,
                new_revision_id=self._get_buffer_rev_id())
            # the collection is in the buffer if it's not unmodified
            return diff.get(collection_id, 'unmodified') != 'unmodified'

        except DeckhandResponseError as drex:
            raise AppError(
                title='Unable to retrieve revisions',
                description=(
                    'Deckhand has responded unexpectedly: {}:{}'.format(
                        drex.status_code, drex.response_message)),
                status=falcon.HTTP_500,
                retry=False,
            )
Exemple #5
0
def _get_service_type(endpoint):
    """
    Because these values should not be used until after initialization,
    they cannot be directly associated with the enum. Thie method takes
    the enum value and retrieves the values when accessed the first time.
    :param Endpoints endpoint: The endpoint to look up
    :returns: The service type value for the named endpoint
    :rtype: str
    :raises AppError: if not provided a valid Endpoints enumeration value
    """
    if isinstance(endpoint, Endpoints):
        endpoint_values = {
            Endpoints.SHIPYARD: CONF.shipyard.service_type,
            Endpoints.DRYDOCK: CONF.drydock.service_type,
            Endpoints.ARMADA: CONF.armada.service_type,
            Endpoints.DECKHAND: CONF.deckhand.service_type,
            Endpoints.PROMENADE: CONF.promenade.service_type
        }
        return endpoint_values.get(endpoint)
    raise AppError(
        title='Endpoint is not known',
        description=(
            'Shipyard is trying to reach an unknown endpoint: {}'.format(
                endpoint.name)),
        status=falcon.HTTP_500,
        retry=False)
    def _get_ordered_versions(self, versions=None):
        """returns a list of ordered versions"""

        # Default ordering
        def_order = [SUCCESSFUL_SITE_ACTION,
                     LAST_SITE_ACTION,
                     COMMITTED,
                     BUFFER]

        # Defaults to COMMITTED and BUFFER
        if versions is None:
            versions = [COMMITTED, BUFFER]

        elif not len(versions) == 2:
            raise AppError(
                title='Incorrect number of versions for comparison',
                description=(
                    'User must pass in 2 valid versions for comparison'),
                status=falcon.HTTP_400,
                retry=False)

        elif versions[0] == versions[1]:
            raise AppError(
                title='Versions must be different for comparison',
                description=(
                    'Versions must be unique in order to perform comparison'),
                status=falcon.HTTP_400,
                retry=False)

        for version in versions:
            if version not in def_order:
                raise AppError(
                    title='Invalid version detected',
                    description=(
                        '{} is not a valid version, which include: '
                        '{}'.format(version, ', '.join(def_order))),
                    status=falcon.HTTP_400,
                    retry=False)

        # Higher index in the def_order list will mean that it is a newer
        # version. We will swap the order and sort the version if need be.
        if def_order.index(versions[0]) > def_order.index(versions[1]):
            ordered_versions = list(reversed(versions))
        else:
            ordered_versions = versions

        return ordered_versions
Exemple #7
0
 def secure_handler(slf, req, resp, *args, **kwargs):
     ctx = req.context
     policy_eng = ctx.policy_engine
     LOG.info("Policy Engine: %s", policy_eng.__class__.__name__)
     # perform auth
     LOG.info("Enforcing policy %s on request %s",
              self.action, ctx.request_id)
     # policy engine must be configured
     if policy_eng is None:
         LOG.error(
             "Error-Policy engine required-action: %s", self.action)
         raise AppError(
             title="Auth is not being handled by any policy engine",
             status=falcon.HTTP_500,
             retry=False
         )
     authorized = False
     try:
         if policy_eng.authorize(self.action, ctx):
             # authorized
             LOG.info("Request is authorized")
             authorized = True
     except:
         # couldn't service the auth request
         LOG.error(
             "Error - Expectation Failed - action: %s", self.action)
         raise ApiError(
             title="Expectation Failed",
             status=falcon.HTTP_417,
             retry=False
         )
     if authorized:
         return f(slf, req, resp, *args, **kwargs)
     else:
         LOG.error("Auth check failed. Authenticated:%s",
                   ctx.authenticated)
         # raise the appropriate response exeception
         if ctx.authenticated:
             LOG.error("Error: Forbidden access - action: %s",
                       self.action)
             raise ApiError(
                 title="Forbidden",
                 status=falcon.HTTP_403,
                 description="Credentials do not permit access",
                 retry=False
             )
         else:
             LOG.error("Error - Unauthenticated access")
             raise ApiError(
                 title="Unauthenticated",
                 status=falcon.HTTP_401,
                 description="Credentials are not established",
                 retry=False
             )
Exemple #8
0
 def tag_buffer(self, tag):
     """Convenience method to tag the buffer version."""
     buffer_rev_id = self.get_revision_id(BUFFER)
     if buffer_rev_id is None:
         raise AppError(
             title='Unable to tag buffer as {}'.format(tag),
             description=('Buffer revision id could not be determined from'
                          'Deckhand'),
             status=falcon.HTTP_500,
             retry=False)
     self.tag_revision(buffer_rev_id, tag)
 def _get_revision_dict(self):
     """
     Returns a dictionary with values representing the revisions in
     Deckhand that Shipyard cares about - committed, buffer,
     and latest, as well as a count of revisions
     Committed and buffer are revisions associated with the
     shipyard tags. If either of those are not present in deckhand,
     returns None for the value.
     Latest holds the revision information for the newest revision.
     """
     # return the cached instance version of the revision dict.
     if self.revision_dict is not None:
         return self.revision_dict
     # or generate one for the cache
     committed_revision = None
     buffer_revision = None
     latest_revision = None
     revision_count = 0
     try:
         revisions = self.deckhand.get_revision_list()
         revision_count = len(revisions)
         if revisions:
             latest_revision = revisions[-1]
             for revision in reversed(revisions):
                 tags = revision.get('tags', [])
                 if COMMITTED in tags or ROLLBACK_COMMIT in tags:
                     committed_revision = revision
                     break
                 else:
                     # there are buffer revisions, only grab it on
                     # the first pass through
                     # if the first revision is committed, or if there
                     # are no revsisions, buffer revsision stays None
                     if buffer_revision is None:
                         buffer_revision = revision
     except NoRevisionsExistError:
         # the values of None/None/None/0 are fine
         pass
     except DeckhandResponseError as drex:
         raise AppError(
             title='Unable to retrieve revisions',
             description=(
                 'Deckhand has responded unexpectedly: {}:{}'.format(
                     drex.status_code, drex.response_message)),
             status=falcon.HTTP_500,
             retry=False)
     self.revision_dict = {
         COMMITTED: committed_revision,
         BUFFER: buffer_revision,
         LATEST: latest_revision,
         REVISION_COUNT: revision_count
     }
     return self.revision_dict
def check_auth(ctx, rule):
    """Checks the authorization to the requested rule

    :param ctx: the request context for the action being performed
    :param rule: the name of the policy rule to validate the user in the
        context against

    Returns if authorized, otherwise raises an ApiError.
    """
    try:
        policy_eng = ctx.policy_engine
        LOG.info("Policy Engine: %s", policy_eng.__class__.__name__)
        # perform auth
        LOG.info("Enforcing policy %s on request %s", rule, ctx.request_id)
        # policy engine must be configured
        if policy_eng is None:
            LOG.error(
                "Error-Policy engine required-action: %s", rule)
            raise AppError(
                title="Auth is not being handled by any policy engine",
                status=falcon.HTTP_500,
                retry=False
            )
        if policy_eng.authorize(rule, ctx):
            # authorized - log and return
            LOG.info("Request to %s is authorized", rule)
            return
    except Exception as ex:
        # couldn't service the auth request
        LOG.exception("Error - Expectation Failed - action: %s", rule)
        raise ApiError(
            title="Expectation Failed",
            status=falcon.HTTP_417,
            retry=False
        )
    # raise the appropriate response exeception
    if ctx.authenticated:
        # authenticated but not authorized
        LOG.error("Error: Forbidden access - action: %s", rule)
        raise ApiError(
            title="Forbidden",
            status=falcon.HTTP_403,
            description="Credentials do not permit access",
            retry=False
        )
    else:
        LOG.error("Error - Unauthenticated access")
        raise ApiError(
            title="Unauthenticated",
            status=falcon.HTTP_401,
            description="Credentials are not established",
            retry=False
        )
Exemple #11
0
def _get_ks_session():
    # Establishes a keystone session
    try:
        auth = loading.load_auth_from_conf_options(CONF, "keystone_authtoken")
        return session.Session(auth=auth)
    except exc.AuthorizationFailure as aferr:
        LOG.error('Could not authorize against keystone: %s', str(aferr))
        raise AppError(
            title='Could not authorize Shipyard against Keystone',
            description=(
                'Keystone has rejected the authorization request by Shipyard'),
            status=falcon.HTTP_500,
            retry=False)
Exemple #12
0
 def get_validations_for_buffer(self):
     """
     Convenience method to do validations for buffer version.
     """
     buffer_rev_id = self._get_buffer_rev_id()
     if buffer_rev_id:
         return self.get_validations_for_revision(buffer_rev_id)
     raise AppError(
         title='Unable to start validation of buffer',
         description=('Buffer revision id could not be determined from'
                      'Deckhand'),
         status=falcon.HTTP_500,
         retry=False)
Exemple #13
0
def _get_ks_session():
    # Establishes a keystone session
    keystone_auth = {}
    for attr in ('auth_url', 'password', 'project_domain_name', 'project_name',
                 'username', 'user_domain_name'):
        keystone_auth[attr] = CONF.get('keystone_authtoken').get(attr)
    try:
        auth = v3.Password(**keystone_auth)
        return session.Session(auth=auth)
    except AuthorizationFailure as aferr:
        LOG.error('Could not authorize against keystone: %s', str(aferr))
        raise AppError(
            title='Could not authorize Shipyard against Keystone',
            description=(
                'Keystone has reqjected the authorization request by Shipyard'
            ),
            status=falcon.HTTP_500,
            retry=False)
Exemple #14
0
    def check_intermediate_commit(self):

        # Initialize variable
        list_of_committed_rev = []

        try:
            # Get the list of all revisions present in Deckhand
            all_revisions = self.deckhand.get_revision_list()

        except NoRevisionsExistError:
            # the values of None/None/None/0 are fine
            pass

        except DeckhandResponseError as drex:
            raise AppError(
                title='Unable to retrieve revisions',
                description=(
                    'Deckhand has responded unexpectedly: {}:{}'.format(
                        drex.status_code, drex.response_message)),
                status=falcon.HTTP_500,
                retry=False)

        if all_revisions:
            # Get the list of 'committed' revisions
            for revision in all_revisions:
                if 'committed' in revision['tags']:
                    list_of_committed_rev.append(revision)

            # This is really applicable for scenarios where multiple
            # configdocs commits and site actions were performed. Hence
            # we should expect at least 2 'committed' revisions to be
            # present in deckhand.
            #
            # We will check the second last most recent committed revision
            # to see if a site-action has been executed on it
            if len(list_of_committed_rev) > 1:
                revision_tags = list_of_committed_rev[-2]['tags']
                if ('site-action-success' not in revision_tags
                        and 'site-action-failure' not in revision_tags):
                    return True

        return False
Exemple #15
0
def get_endpoint(endpoint):
    """
    Wraps calls to keystone for lookup of an endpoint by service type
    :param Endpoints endpoint: The endpoint to look up
    :returns: The url string of the endpoint
    :rtype: str
    :raises AppError: if the endpoint cannot be resolved
    """
    service_type = _get_service_type(endpoint)
    try:
        return _get_ks_session().get_endpoint(interface='internal',
                                              service_type=service_type)
    except exc.EndpointNotFound:
        LOG.error('Could not find an internal interface for %s', endpoint.name)
        raise AppError(
            title='Can not access service endpoint',
            description=(
                'Keystone catalog has no internal endpoint for service type: '
                '{}'.format(service_type)),
            status=falcon.HTTP_500,
            retry=False)
def get_nodes_provision_status(drydock):
    # Calls Drydock client to fetch node provision status
    try:
        nodes = drydock.get_nodes()

        nodes_status = []
        for node in nodes:
            nodes_status.append({
                'hostname': node.get('hostname'),
                'status': node.get('status_name')
            })
    except dderrors.ClientError as ddex:
        raise AppError(
            title='Unable to retrieve nodes status',
            description=('Drydock has responded unexpectedly: '
                         '{}'.format(ddex.response_message)),
            status=falcon.HTTP_500,
            retry=False,
        )

    machine_status = {'nodes_provision_status': nodes_status}

    return machine_status
def get_machines_powerstate(drydock):
    # Calls Drydock client to fetch nodes power state
    try:
        machines = drydock.get_nodes()

        machines_ps = []
        for machine in machines:
            machines_ps.append({
                'hostname': machine.get('hostname'),
                'power_state': machine.get('power_state')
            })
    except dderrors.ClientError as ddex:
        raise AppError(
            title='Unable to retrieve nodes power-state',
            description=('Drydock has responded unexpectedly: {}'.format(
                ddex.response_message)),
            status=falcon.HTTP_500,
            retry=False,
        )

    machines_powerstate = {'machines_powerstate': machines_ps}

    return machines_powerstate
Exemple #18
0
    def get_configdocs_status(self, versions=None):
        """
        :param versions: A list of 2 versions. Defaults to buffer and
                         commmitted if None.

        Returns a list of the configdocs based on their versions and
        statuses
        """
        configdocs_status = []

        # Get ordered versions
        ordered_versions = self._get_ordered_versions(versions)

        # Get version name and id
        old_version_name, new_version_name, old_version_id, new_version_id = (
            self._get_versions_name_id(ordered_versions))

        try:
            diff = self.deckhand.get_diff(old_revision_id=old_version_id,
                                          new_revision_id=new_version_id)

        except DeckhandResponseError as drex:
            raise AppError(
                title='Unable to retrieve revisions',
                description=(
                    'Deckhand has responded unexpectedly: {}:{}'.format(
                        drex.status_code, drex.response_message)),
                status=falcon.HTTP_500,
                retry=False,
            )

        for collection_id in diff:
            collection = {"collection_name": collection_id}

            if diff[collection_id] in [
                    "unmodified", "modified", "created", "deleted"
            ]:

                collection['base_version'] = old_version_name
                collection['base_revision'] = old_version_id
                collection['new_version'] = new_version_name
                collection['new_revision'] = new_version_id
                collection['new_status'] = diff[collection_id]

                if diff[collection_id] == "created":
                    collection['base_status'] = 'not present'
                else:
                    collection['base_status'] = 'present'

            else:
                raise AppError(
                    title='Invalid collection status',
                    description=(
                        'Collection_id, {} has an invalid collection status. '
                        'unmodified, modified, created, and deleted are the'
                        ' only valid collection statuses.', collection_id),
                    status=falcon.HTTP_500,
                    retry=False)

            configdocs_status.append(collection)

        return configdocs_status
Exemple #19
0
    def _get_revision_dict(self):
        """
        Returns a dictionary with values representing the revisions in
        Deckhand that Shipyard cares about - committed, buffer, latest,
        last_site_action and successful_site_action, as well as a count
        of revisions.
        Committed and buffer are revisions associated with the
        shipyard tags. If either of those are not present in deckhand,
        returns None for the value.
        Latest holds the revision information for the newest revision.
        Last site action holds the revision information for the most
        recent site action
        Successful site action holds the revision information for the
        most recent successfully executed site action.
        """
        # return the cached instance version of the revision dict.
        if self.revision_dict is not None:
            return self.revision_dict
        # or generate one for the cache
        committed_revision = None
        buffer_revision = None
        last_site_action = None
        latest_revision = None
        revision_count = 0
        successful_site_action = None
        try:
            revisions = self.deckhand.get_revision_list()
            revision_count = len(revisions)
            if revisions:
                # Retrieve latest revision
                latest_revision = revisions[-1]

                # Get required revision
                for revision in reversed(revisions):
                    tags = revision.get('tags', [])

                    if (committed_revision is None and
                        (COMMITTED in tags or ROLLBACK_COMMIT in tags)):
                        committed_revision = revision
                    else:
                        # there are buffer revisions, only grab it on
                        # the first pass through
                        # if the first revision is committed, or if there
                        # are no revsisions, buffer revsision stays None
                        if (committed_revision is None
                                and buffer_revision is None):
                            buffer_revision = revision

                    # Get the revision of the last successful site action
                    if (successful_site_action is None
                            and SITE_ACTION_SUCCESS in tags):
                        successful_site_action = revision

                    # Get the revision of the last site action
                    if (last_site_action is None
                            and (SITE_ACTION_SUCCESS in tags
                                 or SITE_ACTION_FAILURE in tags)):
                        last_site_action = revision

        except NoRevisionsExistError:
            # the values of None/None/None/0 are fine
            pass

        except DeckhandResponseError as drex:
            raise AppError(
                title='Unable to retrieve revisions',
                description=(
                    'Deckhand has responded unexpectedly: {}:{}'.format(
                        drex.status_code, drex.response_message)),
                status=falcon.HTTP_500,
                retry=False)
        self.revision_dict = {
            BUFFER: buffer_revision,
            COMMITTED: committed_revision,
            LAST_SITE_ACTION: last_site_action,
            LATEST: latest_revision,
            REVISION_COUNT: revision_count,
            SUCCESSFUL_SITE_ACTION: successful_site_action
        }
        return self.revision_dict