Example #1
0
    def get(self):
        host_url = pecan.request.application_url.rstrip('/')
        puri = uris.PLANS_URI_STR % host_url
        pdef_uri = uris.DEPLOY_PARAMS_URI % host_url
        desc = "Solum CAMP API plans collection resource."

        handler = plan_handler.PlanHandler(pecan.request.security_context)
        plan_objs = handler.get_all()
        p_links = []
        for m in plan_objs:
            p_links.append(common_types.Link(href=uris.PLAN_URI_STR %
                                             (host_url, m.uuid),
                                             target_name=m.name))

        # if there aren't any plans, avoid returning a resource with an
        # empty plan_links array
        if len(p_links) > 0:
            res = model.Plans(uri=puri,
                              name='Solum_CAMP_plans',
                              type='plans',
                              description=desc,
                              parameter_definitions_uri=pdef_uri,
                              plan_links=p_links)
        else:
            res = model.Plans(uri=puri,
                              name='Solum_CAMP_plans',
                              type='plans',
                              description=desc,
                              parameter_definitions_uri=pdef_uri)
        return res
Example #2
0
 def delete(self, uuid):
     """Delete this plan."""
     handler = (plan_handler.PlanHandler(pecan.request.security_context))
     try:
         handler.delete(uuid)
     except (db_exc.DBReferenceError, db_exc.DBError):
         raise exception.PlanStillReferenced(name=uuid)
Example #3
0
    def patch(self, uuid):
        """Patch an existing CAMP-style plan."""

        handler = (plan_handler.PlanHandler(pecan.request.security_context))
        plan_obj = handler.get(uuid)

        # TODO([email protected]) check if there are any assemblies that
        # refer to this plan and raise an PlanStillReferenced exception if
        # there are.

        if not pecan.request.body or len(pecan.request.body) < 1:
            raise exception.BadRequest(reason='empty request body')

        # check to make sure the request has the right Content-Type
        if (pecan.request.content_type is None or
                pecan.request.content_type != 'application/json-patch+json'):
            raise exception.UnsupportedMediaType(
                name=pecan.request.content_type, method='PATCH')

        try:
            patch = jsonpatch.JsonPatch.from_string(pecan.request.body)
            patched_obj = patch.apply(plan_obj.refined_content())
            db_obj = handler.update(uuid, patched_obj)
        except KeyError:
            # a key error indicates one of the patch operations is missing a
            # component
            raise exception.BadRequest(reason=MAL_PATCH_ERR)
        except jsonpatch.JsonPatchConflict:
            raise exception.Unprocessable
        except jsonpatch.JsonPatchException as jpe:
            raise JsonPatchProcessingException(reason=six.text_type(jpe))

        return fluff_plan(db_obj.refined_content(), db_obj.uuid)
Example #4
0
 def get_one(self, uuid):
     """Return the appropriate CAMP-style plan resource."""
     handler = (plan_handler.PlanHandler(pecan.request.security_context))
     db_obj = handler.get(uuid)
     plan_dict = fluff_plan(db_obj.refined_content(), db_obj.uuid)
     return model.Plan(**plan_dict)
Example #5
0
    def post(self):
        """Create a new CAMP-style plan."""
        if not pecan.request.body or len(pecan.request.body) < 1:
            raise exception.BadRequest

        # check to make sure the request has the right Content-Type
        if (pecan.request.content_type is None
                or pecan.request.content_type != 'application/x-yaml'):
            raise exception.UnsupportedMediaType(
                name=pecan.request.content_type, method='POST')

        try:
            yaml_input_plan = yamlutils.load(pecan.request.body)
        except ValueError as excp:
            raise exception.BadRequest(reason='Plan is invalid. ' +
                                       six.text_type(excp))

        camp_version = yaml_input_plan.get('camp_version')
        if camp_version is None:
            raise exception.BadRequest(
                reason='camp_version attribute is missing from submitted Plan')
        elif camp_version != 'CAMP 1.1':
            raise exception.BadRequest(reason=UNSUP_VER_ERR % camp_version)

        # Use Solum's handler as the point of commonality. We can do this
        # because Solum stores plans in the DB in their JSON form.
        handler = (plan_handler.PlanHandler(pecan.request.security_context))
        model_plan = model.Plan(**yaml_input_plan)

        # Move any inline Service Specifications to the "services" section.
        # This avoids an issue where WSME can't properly handle multi-typed
        # attributes (e.g. 'fulfillment'). It also smoothes out the primary
        # difference between CAMP plans and Solum plans, namely that Solum
        # plans don't have inline Service Specifications.
        for art in model_plan.artifacts:
            if art.requirements != wsme.Unset:
                for req in art.requirements:
                    if (req.fulfillment != wsme.Unset and isinstance(
                            req.fulfillment, model.ServiceSpecification)):
                        s_spec = req.fulfillment

                        # if the inline service spec doesn't have an id
                        # generate one
                        if s_spec.id == wsme.Unset:
                            s_spec.id = uuidutils.generate_uuid()

                        # move the inline service spec to the 'services'
                        # section
                        if model_plan.services == wsme.Unset:
                            model_plan.services = [s_spec]
                        else:
                            model_plan.services.append(s_spec)

                        # set the fulfillment to the service spec id
                        req.fulfillment = "id:%s" % s_spec.id

        db_obj = handler.create(
            clean_plan(wjson.tojson(model.Plan, model_plan)))
        plan_dict = fluff_plan(db_obj.refined_content(), db_obj.uuid)

        pecan.response.status = 201
        pecan.response.location = plan_dict['uri']
        return plan_dict
Example #6
0
    def post(self):
        """Create a new application.

        There are a number of ways to use this method to create a new
        application. See Section 6.11 of the CAMP v1.1 specification
        for an explanation of each. Use the Content-Type of request to
        determine what the client is trying to do.
        """
        if pecan.request.content_type is None:
            raise exception.UnsupportedMediaType(
                name=pecan.request.content_type, method='POST')

        req_content_type = pecan.request.content_type

        # deploying by reference uses a JSON payload
        if req_content_type == 'application/json':
            payload = pecan.request.body
            if not payload or len(payload) < 1:
                raise exception.BadRequest(reason='empty request body')

            try:
                json_ref_doc = json.loads(payload)
            except ValueError as excp:
                raise exception.BadRequest(reason='JSON object is invalid. ' +
                                           six.text_type(excp))

            if 'plan_uri' in json_ref_doc:
                plan_uri_str = json_ref_doc['plan_uri']

                # figure out if the plan uri is relative or absolute
                plan_uri = urllib.parse.urlparse(plan_uri_str)
                uri_path = plan_uri.path
                if not plan_uri.netloc:
                    # should be something like "../plans/<uuid>" or
                    # "/camp/v1_1/plans/<uuid> (include Solum plans)
                    if (not uri_path.startswith('../plans/')
                            and not uri_path.startswith('../../../v1/plans/')
                            and not uri_path.startswith('/camp/v1_1/plans/')
                            and not uri_path.startswith('/v1/plans/')):
                        msg = 'plan_uri does not reference a plan resource'
                        raise exception.BadRequest(reason=msg)

                    plan_uuid = plan_uri.path.split('/')[-1]

                else:
                    # We have an absolute URI. Try to figure out if it refers
                    # to a plan on this Solum instance. Note the following code
                    # does not support URI aliases. A request that contains
                    # a 'plan_uri' with a network location that is different
                    # than network location used to make this request but
                    # which, nevertheless, still refers to this Solum instance
                    # will experience a false negative. This code will treat
                    # that plan as if it existed on another CAMP-compliant
                    # server.
                    if plan_uri_str.startswith(pecan.request.host_url):
                        if (not uri_path.startswith('/camp/v1_1/plans/')
                                and not uri_path.startswith('/v1/plans/')):
                            msg = 'plan_uri does not reference a plan resource'
                            raise exception.BadRequest(reason=msg)

                        plan_uuid = plan_uri.path.split('/')[-1]

                    else:
                        # The plan exists on another server.
                        # TODO(gpilz): support references to plans on other
                        # servers
                        raise exception.NotImplemented()

                # resolve the local plan by its uuid. this will raise a
                # ResourceNotFound exception if there is no plan with
                # this uuid
                phandler = plan_handler.PlanHandler(
                    pecan.request.security_context)
                plan_obj = phandler.get(plan_uuid)

            elif 'pdp_uri' in json_ref_doc:
                # TODO(gpilz): support references to PDPs
                raise exception.NotImplemented()
            else:
                # must have either 'plan_uri' or 'pdp_uri'
                msg = 'JSON object must contain either plan_uri or pdp_uri'
                raise exception.BadRequest(reason=msg)
        else:
            # TODO(gpilz): support deploying an application by value
            raise exception.NotImplemented()

        # at this point we expect to have a reference to a plan database object
        # for the plan that will be used to create the application
        ahandler = assembly_handler.AssemblyHandler(
            pecan.request.security_context)
        assem_db_obj = ahandler.create_from_plan(plan_obj)
        assem_model = model.Assembly.from_db_model(assem_db_obj,
                                                   pecan.request.host_url)

        pecan.response.status = 201
        pecan.response.location = assem_model.uri
        return wsme_json.tojson(model.Assembly, assem_model)