Merge "Remove tpmconfig Sysinv APIs"

This commit is contained in:
Zuul 2018-06-27 18:27:49 +00:00 committed by Gerrit Code Review
commit aa74264a26
8 changed files with 61 additions and 595 deletions

View File

@ -11,21 +11,38 @@
import os
from cgtsclient import exc
from cgtsclient.common import utils
from collections import OrderedDict
def _print_certificate_show(certificate):
fields = ['uuid', 'certtype', 'signature', 'start_date', 'expiry_date']
if type(certificate) is dict:
data = [(f, certificate.get(f, '')) for f in fields]
details = ('details', certificate.get('details', ''))
else:
data = [(f, getattr(certificate, f, '')) for f in fields]
details = ('details', getattr(certificate, 'details', ''))
if details[1]:
data.append(details)
utils.print_tuple_list(data)
@utils.arg('certificate_uuid', metavar='<certificate_uuid>',
help="UUID of certificate")
help="UUID of certificate or the reserved word 'tpm' "
"to show TPM certificate")
def do_certificate_show(cc, args):
"""Show Certificate details."""
if args.certificate_uuid == 'tpm':
certificates = cc.certificate.list()
for cert in certificates:
if cert.certtype == 'tpm_mode':
args.certificate_uuid = cert.uuid
break
else:
print "No TPM certificate installed"
return
certificate = cc.certificate.get(args.certificate_uuid)
if certificate:
_print_certificate_show(certificate)

View File

@ -69,7 +69,6 @@ from cgtsclient.v1 import sm_servicegroup
from cgtsclient.v1 import health
from cgtsclient.v1 import remotelogging
from cgtsclient.v1 import sdn_controller
from cgtsclient.v1 import tpmconfig
from cgtsclient.v1 import firewallrules
from cgtsclient.v1 import partition
from cgtsclient.v1 import certificate
@ -141,7 +140,6 @@ class Client(http.HTTPClient):
self.health = health.HealthManager(self)
self.remotelogging = remotelogging.RemoteLoggingManager(self)
self.sdn_controller = sdn_controller.SDNControllerManager(self)
self.tpmconfig = tpmconfig.TpmConfigManager(self)
self.firewallrules = firewallrules.FirewallRulesManager(self)
self.partition = partition.partitionManager(self)
self.license = license.LicenseManager(self)

View File

@ -55,7 +55,6 @@ from cgtsclient.v1 import license_shell
from cgtsclient.v1 import health_shell
from cgtsclient.v1 import remotelogging_shell
from cgtsclient.v1 import sdn_controller_shell
from cgtsclient.v1 import tpmconfig_shell
from cgtsclient.v1 import firewallrules_shell
from cgtsclient.v1 import partition_shell
from cgtsclient.v1 import certificate_shell
@ -108,7 +107,6 @@ COMMAND_MODULES = [
health_shell,
remotelogging_shell,
sdn_controller_shell,
tpmconfig_shell,
firewallrules_shell,
partition_shell,
license_shell,

View File

@ -1,50 +0,0 @@
#
# Copyright (c) 2017 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# -*- encoding: utf-8 -*-
#
from cgtsclient.common import base
from cgtsclient import exc
CREATION_ATTRIBUTES = ['cert_path', 'public_path', 'tpm_path']
class TpmConfig(base.Resource):
def __repr__(self):
return "<tpmconfig %s>" % self._info
class TpmConfigManager(base.Manager):
resource_class = TpmConfig
@staticmethod
def _path(id=None):
return '/v1/tpmconfig/%s' % id if id else '/v1/tpmconfig'
def list(self):
return self._list(self._path(), "tpmconfigs")
def get(self, tpmconfig_id):
try:
return self._list(self._path(tpmconfig_id))[0]
except IndexError:
return None
def create(self, **kwargs):
new = {}
for (key, value) in kwargs.items():
if key in CREATION_ATTRIBUTES:
new[key] = value
else:
raise exc.InvalidAttribute('%s' % key)
return self._create(self._path(), new)
def delete(self, tpmconfig_id):
return self._delete(self._path(tpmconfig_id))
def update(self, tpmconfig_id, patch):
return self._update(self._path(tpmconfig_id), patch)

View File

@ -1,130 +0,0 @@
#!/usr/bin/env python
#
# Copyright (c) 2017 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
import argparse
import sys
import time
from cgtsclient.common import utils
from cgtsclient import exc
def _print_tpmconfig_show(tpmconfig):
fields = ['uuid',
'tpm_path',
'created_at',
'updated_at',
'state',
]
data = [(f, getattr(tpmconfig, f, '')) for f in fields]
utils.print_tuple_list(data)
def do_tpmconfig_show(cc, args):
"""Show TPM config details."""
tpmconfigs = cc.tpmconfig.list()
if not tpmconfigs:
return
_print_tpmconfig_show(tpmconfigs[0])
@utils.arg('--cert_path',
metavar='<cert_path>',
default=None,
help="Path to certificate to upload to TPM.")
@utils.arg('--public_path',
metavar='<public_path>',
default=None,
help="Path to store public certificate.")
@utils.arg('--tpm_path',
metavar='<tpm_path>',
default=None,
help="Path to store TPM object context")
def do_tpmconfig_add(cc, args):
"""Add TPM configuration."""
field_list = ['cert_path', 'public_path', 'tpm_path']
# use field list as filter
user_specified_fields = dict((k, v) for (k, v) in vars(args).items()
if k in field_list and not (v is None))
try:
tpmconfig = cc.tpmconfig.create(**user_specified_fields)
except exc.HTTPNotFound:
raise exc.CommandError("Failed to create TPM configuration entry: "
"fields %s" % user_specified_fields)
uuid = getattr(tpmconfig, 'uuid', '')
try:
tpmconfig = cc.tpmconfig.get(uuid)
except exc.HTTPNotFound:
raise exc.CommandError("Created TPM configuration UUID not found: %s"
% uuid)
_print_tpmconfig_show(tpmconfig)
def do_tpmconfig_delete(cc, args):
"""Delete a TPM configuration."""
try:
tpmconfigs = cc.tpmconfig.list()
if not tpmconfigs:
return
tpmconfig = tpmconfigs[0]
cc.tpmconfig.delete(tpmconfig.uuid)
except exc.HTTPNotFound:
raise exc.CommandError("Failed to delete TPM configuration entry: "
"no configuration found")
print 'Deleted TPM configuration: uuid %s' % tpmconfig.uuid
@utils.arg('--cert_path',
metavar='<cert_path>',
default=None,
help="Path to certificate to upload to TPM.")
@utils.arg('--public_path',
metavar='<public_path>',
default=None,
help="Path to store public certificate.")
@utils.arg('--tpm_path',
metavar='<tpm_path>',
default=None,
help="Path to store TPM object context")
def do_tpmconfig_modify(cc, args):
"""Modify a TPM configuration."""
# find the TPM configuration first
tpmconfig = None
try:
tpmconfigs = cc.tpmconfig.list()
if tpmconfigs:
tpmconfig = tpmconfigs[0]
field_list = ['cert_path', 'public_path', 'tpm_path']
# use field list as filter
user_fields = dict((k, v) for (k, v) in vars(args).items()
if k in field_list and not (v is None))
configured_fields = tpmconfig.__dict__
configured_fields.update(user_fields)
patch = []
for (k,v) in user_fields.items():
patch.append({'op': 'replace', 'path': '/' + k, 'value': v})
try:
updated_tpmconfig = cc.tpmconfig.update(tpmconfig.uuid, patch)
except:
raise exc.CommandError("Failed to modify TPM configuration: "
"tpmconfig %s : patch %s" %
(tpmconfig.uuid, patch))
_print_tpmconfig_show(updated_tpmconfig)
return
except exc.HTTPNotFound:
pass
finally:
if not tpmconfig:
raise exc.CommandError("Failed to modify TPM configuration: "
"no configuration found")

View File

@ -75,7 +75,6 @@ from sysinv.api.controllers.v1 import storage_external
from sysinv.api.controllers.v1 import storage_tier
from sysinv.api.controllers.v1 import system
from sysinv.api.controllers.v1 import trapdest
from sysinv.api.controllers.v1 import tpmconfig
from sysinv.api.controllers.v1 import upgrade
from sysinv.api.controllers.v1 import user
@ -220,9 +219,6 @@ class V1(base.APIBase):
sdn_controller = [link.Link]
"Links to the SDN controller resource"
tpmconfig = [link.Link]
"Links to the TPM configuration resource"
firewallrules = [link.Link]
"Links to customer firewall rules"
@ -688,14 +684,6 @@ class V1(base.APIBase):
bookmark=True)
]
v1.tpmconfig = [link.Link.make_link('self',
pecan.request.host_url,
'tpmconfig', ''),
link.Link.make_link('bookmark',
pecan.request.host_url,
'tpmconfig', '',
bookmark=True)]
v1.firewallrules = [link.Link.make_link('self',
pecan.request.host_url,
'firewallrules', ''),
@ -771,7 +759,6 @@ class Controller(rest.RestController):
health = health.HealthController()
remotelogging = remotelogging.RemoteLoggingController()
sdn_controller = sdn_controller.SDNControllerController()
tpmconfig = tpmconfig.TPMConfigController()
firewallrules = firewallrules.FirewallRulesController()
license = license.LicenseController()

View File

@ -84,6 +84,9 @@ class Certificate(base.APIBase):
mode = wtypes.text
"Represents the desired mode"
details = types.MultiType({dict})
"Represents additional details of the certificate"
updated_at = wtypes.datetime.datetime
def __init__(self, **kwargs):
@ -93,6 +96,11 @@ class Certificate(base.APIBase):
continue
setattr(self, k, kwargs.get(k, wtypes.Unset))
# 'details' is not part of the object.certificate.fields
# (it is an API-only attribute)
self.fields.append('details')
setattr(self, 'details', kwargs.get('details', None))
@classmethod
def convert_with_links(cls, rpc_certificate, expand=False):
certificate = Certificate(**rpc_certificate.as_dict())
@ -101,9 +109,13 @@ class Certificate(base.APIBase):
'certtype',
'issuer',
'signature',
'details',
'start_date',
'expiry_date'])
# insert details for this certificate if they exist
certificate = _insert_certificate_details(certificate)
certificate.links = \
[link.Link.make_link('self', pecan.request.host_url,
'certificates', certificate.uuid),
@ -146,18 +158,31 @@ def _check_certificate_data(certificate):
return certificate
def _clear_existing_certificate_alarms():
# Clear all existing CERTIFICATE configuration alarms,
# for one or both controller hosts
obj = fm_api.FaultAPIs()
alarms = obj.get_faults_by_id(fm_constants.FM_ALARM_ID_CERTIFICATE_INIT)
if not alarms:
def _insert_certificate_details(certificate):
if not certificate:
return
for alarm in alarms:
obj.clear_fault(
fm_constants.FM_ALARM_ID_CERTIFICATE_INIT,
alarm.entity_instance_id)
if certificate.certtype == constants.CERT_MODE_TPM:
try:
tpmconfig = pecan.request.dbapi.tpmconfig_get_one()
except exception.NotFound:
return certificate
tpmdevices = pecan.request.dbapi.tpmdevice_get_list()
certificate.details = {}
states = {}
for device in tpmdevices:
# extract the state info per host
ihost = pecan.request.dbapi.ihost_get(device['host_id'])
if ihost:
states[ihost.hostname] = device.state
if tpmdevices:
certificate.details['state'] = states
if tpmconfig.updated_at:
certificate.details['updated_at'] = \
tpmconfig.updated_at.isoformat()
return certificate
LOCK_NAME = 'CertificateController'
@ -251,7 +276,14 @@ class CertificateController(rest.RestController):
fileitem = pecan.request.POST['file']
passphrase = pecan.request.POST.get('passphrase')
mode = pecan.request.POST.get('mode')
certificate_file = pecan.request.POST.get('certificate_file')
# Ensure that the certificate_file is a valid file path
if os.path.isabs(certificate_file):
if not os.path.isfile(certificate_file):
msg = "'certificate_file' is not a valid file path"
LOG.info(msg)
return dict(success="", error=msg)
LOG.info("certificate %s mode=%s" % (log_start, mode))

View File

@ -1,386 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013 UnitedStack Inc.
# 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
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
#
# Copyright (c) 2013-2017 Wind River Systems, Inc.
#
import jsonpatch
import os
import pecan
from pecan import rest
import wsme
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from sysinv.api.controllers.v1 import base
from sysinv.api.controllers.v1 import collection
from sysinv.api.controllers.v1 import link
from sysinv.api.controllers.v1 import types
from sysinv.api.controllers.v1 import utils
from sysinv.common import constants
from sysinv.common import exception
from sysinv.common import utils as cutils
from sysinv import objects
from sysinv.openstack.common import excutils
from sysinv.openstack.common.gettextutils import _
from sysinv.openstack.common import log
from fm_api import constants as fm_constants
from fm_api import fm_api
LOG = log.getLogger(__name__)
class TPMConfigPatchType(types.JsonPatchType):
@staticmethod
def mandatory_attrs():
return []
class TPMConfig(base.APIBase):
"""API representation of TPM Configuration.
This class enforces type checking and value constraints, and converts
between the internal object model and the API representation of
an tpmconfig.
"""
uuid = types.uuid
"Unique UUID for this tpmconfig"
cert_path = wtypes.text
"Represents the path of the SSL certificate to be stored in TPM"
public_path = wtypes.text
"Represents the path of the SSL public key"
tpm_path = wtypes.text
"Represents the path to store TPM certificate"
state = types.MultiType({dict})
"Represents the state of the TPM config"
links = [link.Link]
"A list containing a self link and associated tpmconfig links"
created_at = wtypes.datetime.datetime
updated_at = wtypes.datetime.datetime
def __init__(self, **kwargs):
self.fields = objects.tpmconfig.fields.keys()
for k in self.fields:
setattr(self, k, kwargs.get(k))
# 'cert_path' and 'public_path' are
# not part of objects.tpmconfig.fields
# (they are an API-only attribute)
for fp in ['cert_path', 'public_path']:
self.fields.append(fp)
setattr(self, fp, kwargs.get(fp, None))
# 'state' is not part of objects.tpmconfig.fields
# (it is an API-only attribute)
self.fields.append('state')
setattr(self, 'state', kwargs.get('state', None))
@classmethod
def convert_with_links(cls, rpc_tpmconfig, expand=True):
tpm = TPMConfig(**rpc_tpmconfig.as_dict())
if not expand:
tpm.unset_fields_except(['uuid',
'cert_path',
'public_path',
'tpm_path',
'state',
'created_at',
'updated_at'])
# insert state
tpm = _insert_tpmdevices_state(tpm)
tpm.links = [link.Link.make_link('self', pecan.request.host_url,
'tpmconfigs', tpm.uuid),
link.Link.make_link('bookmark', pecan.request.host_url,
'tpmconfigs', tpm.uuid,
bookmark=True)]
return tpm
class TPMConfigCollection(collection.Collection):
"""API representation of a collection of tpmconfigs."""
tpmconfigs = [TPMConfig]
"A list containing tpmconfig objects"
def __init__(self, **kwargs):
self._type = 'tpmconfigs'
@classmethod
def convert_with_links(cls, rpc_tpmconfigs, limit, url=None,
expand=False, **kwargs):
collection = TPMConfigCollection()
collection.tpmconfigs = [TPMConfig.convert_with_links(p, expand)
for p in rpc_tpmconfigs]
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
##############
# UTILS
##############
def _check_tpmconfig_data(tpmconfig):
if not utils.get_https_enabled():
raise wsme.exc.ClientSideError(
_("Cannot configure TPM without HTTPS mode being enabled"))
if not tpmconfig.get('cert_path', None):
raise wsme.exc.ClientSideError(
_("Cannot configure TPM without cert_path provided"))
if not tpmconfig.get('public_path', None):
raise wsme.exc.ClientSideError(
_("Cannot configure TPM without public_path provided"))
if not tpmconfig.get('tpm_path', None):
raise wsme.exc.ClientSideError(
_("Cannot configure TPM without tpm_path provided"))
# validate the key paths
values = [tpmconfig['cert_path'],
tpmconfig['tpm_path'],
tpmconfig['public_path']]
for i, item in enumerate(values):
# ensure valid paths
if os.path.isabs(item):
if i == 0:
# ensure key exists
if not os.path.isfile(item):
raise wsme.exc.ClientSideError(_(
"Cert path is not a valid existing file"))
else:
raise wsme.exc.ClientSideError(_(
"TPM configuration arguments must be file paths"))
return tpmconfig
def _clear_existing_tpmconfig_alarms():
# Clear all existing TPM configuration alarms,
# for one or both controller hosts
obj = fm_api.FaultAPIs()
alarms = obj.get_faults_by_id(
fm_constants.FM_ALARM_ID_TPM_INIT)
if not alarms:
return
for alarm in alarms:
obj.clear_fault(
fm_constants.FM_ALARM_ID_TPM_INIT,
alarm.entity_instance_id)
def _insert_tpmdevices_state(tpmconfig):
# update the tpmconfig state with the per host
# tpmdevice state
if not tpmconfig:
return
tpmdevices = pecan.request.dbapi.tpmdevice_get_list()
tpmconfig.state = {}
for device in tpmdevices:
# extract the state info per host
ihost = pecan.request.dbapi.ihost_get(device['host_id'])
if ihost:
tpmconfig.state[ihost.hostname] = device.state
return tpmconfig
class TPMConfigController(rest.RestController):
"""REST controller for tpmconfigs."""
def __init__(self, parent=None, **kwargs):
self._parent = parent
def _get_tpmconfigs_collection(self, uuid, marker, limit,
sort_key, sort_dir, expand=False,
resource_url=None):
limit = utils.validate_limit(limit)
sort_dir = utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.tpmconfig.get_by_uuid(pecan.request.context,
marker)
tpms = pecan.request.dbapi.tpmconfig_get_list(limit,
marker_obj,
sort_key=sort_key,
sort_dir=sort_dir)
return TPMConfigCollection.convert_with_links(tpms, limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
def _get_updates(self, patch):
"""Retrieve the updated attributes from the patch request."""
updates = {}
for p in patch:
attribute = p['path'] if p['path'][0] != '/' else p['path'][1:]
updates[attribute] = p['value']
return updates
@wsme_pecan.wsexpose(TPMConfigCollection, types.uuid, types.uuid, int,
wtypes.text, wtypes.text)
def get_all(self, uuid=None, marker=None, limit=None,
sort_key='id', sort_dir='asc'):
"""Retrieve a list of tpmconfigs. Only one per system"""
return self._get_tpmconfigs_collection(uuid, marker, limit,
sort_key, sort_dir)
@wsme_pecan.wsexpose(TPMConfig, types.uuid)
def get_one(self, tpmconfig_uuid):
"""Retrieve information about the given tpmconfig."""
rpc_tpmconfig = objects.tpmconfig.get_by_uuid(pecan.request.context,
tpmconfig_uuid)
return TPMConfig.convert_with_links(rpc_tpmconfig)
@wsme_pecan.wsexpose(TPMConfig, body=TPMConfig)
def post(self, tpmconfig):
"""Create a new tpmconfig."""
# There must not already be an existing tpm config
try:
tpm = pecan.request.dbapi.tpmconfig_get_one()
except exception.NotFound:
pass
else:
raise wsme.exc.ClientSideError(_(
"tpmconfig rejected: A TPM configuration already exists."))
_check_tpmconfig_data(tpmconfig.as_dict())
try:
new_tpmconfig = pecan.request.dbapi.tpmconfig_create(
tpmconfig.as_dict())
except exception.SysinvException as e:
LOG.exception(e)
raise wsme.exc.ClientSideError(_("Invalid data: failed to create "
"a tpm config record."))
# apply TPM configuration via agent RPCs
try:
pecan.request.rpcapi.update_tpm_config(
pecan.request.context,
tpmconfig.as_dict())
pecan.request.rpcapi.update_tpm_config_manifests(
pecan.request.context)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.exception(e)
return tpmconfig.convert_with_links(new_tpmconfig)
@wsme.validate(types.uuid, [TPMConfigPatchType])
@wsme_pecan.wsexpose(TPMConfig, types.uuid,
body=[TPMConfigPatchType])
def patch(self, tpmconfig_uuid, patch):
"""Update the current tpm configuration."""
tpmconfig = objects.tpmconfig.get_by_uuid(pecan.request.context,
tpmconfig_uuid)
tpmdevices = pecan.request.dbapi.tpmdevice_get_list()
# if any of the tpm devices are in APPLYING state
# then disallow a modification till previous config
# either applies or fails
for device in tpmdevices:
if device.state == constants.TPMCONFIG_APPLYING:
raise wsme.exc.ClientSideError(_("TPM Device %s is still "
"in APPLYING state. Wait for the configuration "
"to finish before attempting a modification." %
device.uuid))
# get attributes to be updated
updates = self._get_updates(patch)
# before we can update we have do a quick semantic check
if 'uuid' in updates:
raise wsme.exc.ClientSideError(_("uuid cannot be modified"))
_check_tpmconfig_data(updates)
# update only DB fields that have changed
# we cannot use the entire set of updates
# since some of them are API updates only
for field in objects.tpmconfig.fields:
if updates.get(field, None):
tpmconfig.field = updates[field]
tpmconfig.save()
new_tpmconfig = tpmconfig.as_dict()
# for conductor and agent updates, consider the entire
# set of incoming updates
new_tpmconfig.update(updates)
# set a modify flag within the tpmconfig, this will inform
# the conductor as well as the agents that we are looking
# to modify the TPM configuration, and not a creation
new_tpmconfig['modify'] = True
# apply TPM configuration via agent RPCs
try:
pecan.request.rpcapi.update_tpm_config(
pecan.request.context,
new_tpmconfig)
pecan.request.rpcapi.update_tpm_config_manifests(
pecan.request.context)
except Exception as e:
with excutils.save_and_reraise_exception():
LOG.exception(e)
return TPMConfig.convert_with_links(tpmconfig)
@wsme_pecan.wsexpose(None, types.uuid, status_code=204)
def delete(self, uuid):
"""Delete a tpmconfig."""
tpmconfig = objects.tpmconfig.get_by_uuid(pecan.request.context,
uuid)
# clear all existing alarms for this TPM configuration
_clear_existing_tpmconfig_alarms()
# clear all tpmdevice configurations for all hosts
tpmdevices = pecan.request.dbapi.tpmdevice_get_list()
for device in tpmdevices:
pecan.request.dbapi.tpmdevice_destroy(device.uuid)
# need to cleanup the tpm file object
tpm_file = tpmconfig.tpm_path
pecan.request.dbapi.tpmconfig_destroy(uuid)
pecan.request.rpcapi.update_tpm_config_manifests(
pecan.request.context,
delete_tpm_file=tpm_file)