Container Infrastructure Management Service for OpenStack
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
7.5 KiB

# Copyright 2015 NEC Corporation. All rights reserved.
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_utils import timeutils
import pecan
import wsme
from wsme import types as wtypes
from magnum.api.controllers import base
from magnum.api.controllers import link
from magnum.api.controllers.v1 import types
from magnum.api import expose
from magnum.api import utils as api_utils
from magnum.common import exception
from magnum.common import policy
from magnum import objects
class ClusterID(wtypes.Base):
"""API representation of a cluster ID
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of a cluster
uuid = types.uuid
"""Unique UUID for this cluster"""
def __init__(self, uuid):
self.uuid = uuid
class Certificate(base.APIBase):
"""API representation of a certificate.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of a
_cluster_uuid = None
"""uuid or logical name of cluster"""
_cluster = None
def _get_cluster_uuid(self):
return self._cluster_uuid
def _set_cluster_uuid(self, value):
if value and self._cluster_uuid != value:
self._cluster = api_utils.get_resource('Cluster', value)
self._cluster_uuid = self._cluster.uuid
except exception.ClusterNotFound as e:
# Change error code because 404 (NotFound) is inappropriate
# response for a POST request to create a Cluster
e.code = 400 # BadRequest
elif value == wtypes.Unset:
self._cluster_uuid = wtypes.Unset
bay_uuid = wsme.wsproperty(wtypes.text, _get_cluster_uuid,
"""The bay UUID or id"""
cluster_uuid = wsme.wsproperty(wtypes.text, _get_cluster_uuid,
"""The cluster UUID or id"""
links = wsme.wsattr([link.Link], readonly=True)
"""A list containing a self link and associated certificate links"""
csr = wtypes.StringType(min_length=1)
""""The Certificate Signing Request"""
pem = wtypes.StringType()
""""The Signed Certificate"""
ca_cert_type = wtypes.StringType()
""""The CA Certificate type the CSR will be signed by"""
def __init__(self, **kwargs):
super(Certificate, self).__init__()
self.fields = []
for field in objects.Certificate.fields:
# Skip fields we do not expose.
if not hasattr(self, field):
setattr(self, field, kwargs.get(field, wtypes.Unset))
# set the attribute for bay_uuid for backwards compatibility
setattr(self, 'bay_uuid', kwargs.get('bay_uuid', self._cluster_uuid))
def get_cluster(self):
if not self._cluster:
self._cluster = api_utils.get_resource('Cluster',
return self._cluster
def _convert_with_links(certificate, url, expand=True):
if not expand:
certificate.unset_fields_except(['bay_uuid', 'cluster_uuid',
'csr', 'pem', 'ca_cert_type'])
certificate.links = [link.Link.make_link('self', url,
link.Link.make_link('bookmark', url,
return certificate
def convert_with_links(cls, rpc_cert, expand=True):
cert = Certificate(**rpc_cert.as_dict())
return cls._convert_with_links(cert,
pecan.request.host_url, expand)
def sample(cls, expand=True):
sample = cls(bay_uuid='7ae81bb3-dec3-4289-8d6c-da80bd8001ae',
return cls._convert_with_links(sample, 'http://localhost:9511', expand)
class CertificateController(base.Controller):
"""REST controller for Certificate."""
def __init__(self):
super(CertificateController, self).__init__()
_custom_actions = {
'detail': ['GET'],
@expose.expose(Certificate, types.uuid_or_name, wtypes.text)
def get_one(self, cluster_ident, ca_cert_type=None):
"""Retrieve CA information about the given cluster.
:param cluster_ident: UUID of a cluster or
logical name of the cluster.
context = pecan.request.context
cluster = api_utils.get_resource('Cluster', cluster_ident)
policy.enforce(context, 'certificate:get', cluster.as_dict(),
certificate = pecan.request.rpcapi.get_ca_certificate(cluster,
return Certificate.convert_with_links(certificate)
@expose.expose(Certificate, body=Certificate, status_code=201)
def post(self, certificate):
"""Sign a new certificate by the CA.
:param certificate: a certificate within the request body.
context = pecan.request.context
cluster = certificate.get_cluster()
policy.enforce(context, 'certificate:create', cluster.as_dict(),
certificate_dict = certificate.as_dict()
certificate_dict['project_id'] = context.project_id
certificate_dict['user_id'] = context.user_id
cert_obj = objects.Certificate(context, **certificate_dict)
new_cert = pecan.request.rpcapi.sign_certificate(cluster,
return Certificate.convert_with_links(new_cert)
@expose.expose(ClusterID, types.uuid_or_name, status_code=202)
def patch(self, cluster_ident):
context = pecan.request.context
cluster = api_utils.get_resource('Cluster', cluster_ident)
policy.enforce(context, 'certificate:rotate_ca', cluster.as_dict(),
if cluster.cluster_template.tls_disabled:
raise exception.NotSupported("Rotating the CA certificate on a "
"non-TLS cluster is not supported")
return ClusterID(cluster.uuid)