Cleaned up repo/model/config files to be more like Glance;
This commit is contained in:
parent
86d1ab30b7
commit
36f7f33086
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
|
.venv
|
||||||
|
*.sqlite
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
|
@ -21,20 +21,20 @@ import falcon
|
|||||||
|
|
||||||
from barbican.api.resources import VersionResource
|
from barbican.api.resources import VersionResource
|
||||||
from barbican.api.resources import TenantsResource, TenantResource
|
from barbican.api.resources import TenantsResource, TenantResource
|
||||||
from barbican.api.resources import SecretsResource, SecretResource
|
# TBD: from barbican.api.resources import SecretsResource, SecretResource
|
||||||
|
|
||||||
# Resources
|
# Resources
|
||||||
VERSIONS = VersionResource()
|
VERSIONS = VersionResource()
|
||||||
TENANTS = TenantsResource()
|
TENANTS = TenantsResource()
|
||||||
TENANT = TenantResource()
|
TENANT = TenantResource()
|
||||||
SECRETS = SecretsResource()
|
# TBD: SECRETS = SecretsResource()
|
||||||
SECRET = SecretResource()
|
# TBD: SECRET = SecretResource()
|
||||||
|
|
||||||
# Routing
|
# Routing
|
||||||
application = falcon.API()
|
application = falcon.API()
|
||||||
api = application
|
api = application
|
||||||
api.add_route('/', VERSIONS)
|
api.add_route('/', VERSIONS)
|
||||||
api.add_route('/tenant', TENANTS)
|
api.add_route('/tenants', TENANTS)
|
||||||
api.add_route('/{tenant_id}', TENANT)
|
api.add_route('/tenants/{tenant_id}', TENANT)
|
||||||
api.add_route('/{tenant_id}/secrets', SECRETS)
|
# TBD: api.add_route('/{tenant_id}/secrets', SECRETS)
|
||||||
api.add_route('/{tenant_id}/secrets/{secret_id}', SECRET)
|
# TBD: api.add_route('/{tenant_id}/secrets/{secret_id}', SECRET)
|
||||||
|
@ -4,8 +4,11 @@ import logging
|
|||||||
|
|
||||||
from barbican.version import __version__
|
from barbican.version import __version__
|
||||||
from barbican.api import ApiResource, load_body, abort
|
from barbican.api import ApiResource, load_body, abort
|
||||||
from barbican.model.tenant import Tenant, Secret
|
from barbican.model.models import Tenant
|
||||||
import barbican.model.repositories
|
from barbican.model.repositories import TenantRepo
|
||||||
|
from barbican.common import config
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
def _tenant_not_found():
|
def _tenant_not_found():
|
||||||
abort(falcon.HTTP_404, 'Unable to locate tenant.')
|
abort(falcon.HTTP_404, 'Unable to locate tenant.')
|
||||||
@ -17,15 +20,10 @@ def _tenant_already_exists():
|
|||||||
|
|
||||||
def _secret_not_found():
|
def _secret_not_found():
|
||||||
abort(falcon.HTTP_400, 'Unable to locate secret profile.')
|
abort(falcon.HTTP_400, 'Unable to locate secret profile.')
|
||||||
|
|
||||||
|
def json_handler(obj):
|
||||||
def format_tenant(tenant):
|
"""Convert objects into json-friendly equivalents."""
|
||||||
if not isinstance(tenant, dict):
|
return obj.isoformat() if hasattr(obj, 'isoformat') else obj
|
||||||
tenant = tenant.__dict__
|
|
||||||
|
|
||||||
return {'id': tenant['id'],
|
|
||||||
'tenant_id': tenant['tenant_id']}
|
|
||||||
|
|
||||||
|
|
||||||
class VersionResource(ApiResource):
|
class VersionResource(ApiResource):
|
||||||
|
|
||||||
@ -37,149 +35,50 @@ class VersionResource(ApiResource):
|
|||||||
|
|
||||||
class TenantsResource(ApiResource):
|
class TenantsResource(ApiResource):
|
||||||
|
|
||||||
def __init__(self, db_session):
|
def __init__(self, tenant_repo=None):
|
||||||
self.repo = TenantRepo()
|
LOG.debug('Creating TenantsResource')
|
||||||
|
self.repo = tenant_repo or TenantRepo()
|
||||||
|
|
||||||
def on_post(self, req, resp):
|
def on_post(self, req, resp):
|
||||||
body = load_body(req)
|
body = load_body(req)
|
||||||
|
print 'Start on_post...%s' % body
|
||||||
username = body['username']
|
username = body['username']
|
||||||
logging.debug('Username is {0}'.format(username))
|
# LOG.debug('Username is {0}'.format(username))
|
||||||
|
print 'Username is %s' % username
|
||||||
|
|
||||||
tenant = self.repo.find_by_name(username, False)
|
tenant = self.repo.find_by_name(name=username, suppress_exception=True)
|
||||||
|
|
||||||
if tenant:
|
if tenant:
|
||||||
abort(falcon.HTTP_400, 'Tenant with username {0} '
|
abort(falcon.HTTP_400, 'Tenant with username {0} '
|
||||||
'already exists'.format(username))
|
'already exists'.format(username))
|
||||||
|
|
||||||
new_tenant = Tenant(username)
|
new_tenant = Tenant()
|
||||||
self.db.add(new_tenant)
|
new_tenant.username = username
|
||||||
self.db.commit()
|
self.repo.create_from(new_tenant)
|
||||||
|
|
||||||
|
print '...post create from'
|
||||||
|
|
||||||
resp.status = falcon.HTTP_201
|
resp.status = falcon.HTTP_201
|
||||||
resp.set_header('Location', '/v1/{0}'.format(new_tenant.id))
|
resp.set_header('Location', '/{0}'.format(new_tenant.id))
|
||||||
|
# TBD: Generate URL...
|
||||||
|
url = 'http://localhost:8080:/tenants/%s' % new_tenant.id
|
||||||
|
resp.body = json.dumps({'ref': url})
|
||||||
|
|
||||||
|
|
||||||
class TenantResource(ApiResource):
|
class TenantResource(ApiResource):
|
||||||
|
|
||||||
def __init__(self, db_session):
|
def __init__(self, tenant_repo=None):
|
||||||
self.db = db_session
|
self.repo = tenant_repo or TenantRepo()
|
||||||
|
|
||||||
def on_get(self, req, resp, tenant_id):
|
def on_get(self, req, resp, tenant_id):
|
||||||
tenant = find_tenant(self.db, id=tenant_id,
|
tenant = self.repo.get(entity_id=tenant_id)
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
resp.status = falcon.HTTP_200
|
resp.status = falcon.HTTP_200
|
||||||
resp.body = json.dumps(tenant.format())
|
resp.body = json.dumps(tenant.to_dict_fields(), default=json_handler)
|
||||||
|
|
||||||
def on_delete(self, req, resp, tenant_id):
|
def on_delete(self, req, resp, tenant_id):
|
||||||
tenant = find_tenant(self.db, id=tenant_id,
|
tenant = self.repo.get(entity_id=tenant_id)
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
self.db.delete(tenant)
|
self.repo.delete_entity(tenant)
|
||||||
self.db.commit()
|
|
||||||
|
|
||||||
resp.status = falcon.HTTP_200
|
resp.status = falcon.HTTP_200
|
||||||
|
|
||||||
|
|
||||||
class SecretsResource(ApiResource):
|
|
||||||
|
|
||||||
def __init__(self, db_session):
|
|
||||||
self.db = db_session
|
|
||||||
|
|
||||||
def on_get(self, req, resp, tenant_id):
|
|
||||||
tenant = find_tenant(self.db, id=tenant_id,
|
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
resp.status = falcon.HTTP_200
|
|
||||||
|
|
||||||
#jsonify a list of formatted secrets
|
|
||||||
resp.body = json.dumps([s.format() for s in tenant.secrets])
|
|
||||||
|
|
||||||
def on_post(self, req, resp, tenant_id):
|
|
||||||
tenant = find_tenant(self.db, id=tenant_id,
|
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
body = load_body(req)
|
|
||||||
secret_name = body['name']
|
|
||||||
|
|
||||||
# Check if the tenant already has a secret with this name
|
|
||||||
for secret in tenant.secrets:
|
|
||||||
if secret.name == secret_name:
|
|
||||||
abort(falcon.HTTP_400,
|
|
||||||
'Secret with name {0} already exists.'.format(
|
|
||||||
secret.name, secret.id))
|
|
||||||
|
|
||||||
# Create the new secret
|
|
||||||
new_secret = Secret(tenant.id, secret_name)
|
|
||||||
tenant.secrets.append(new_secret)
|
|
||||||
|
|
||||||
self.db.add(new_secret)
|
|
||||||
self.db.commit()
|
|
||||||
|
|
||||||
resp.status = falcon.HTTP_201
|
|
||||||
resp.set_header('Location',
|
|
||||||
'/v1/{0}/secrets/{1}'
|
|
||||||
.format(tenant_id, new_secret.id))
|
|
||||||
|
|
||||||
|
|
||||||
class SecretResource(ApiResource):
|
|
||||||
|
|
||||||
def __init__(self, db_session):
|
|
||||||
self.db = db_session
|
|
||||||
|
|
||||||
def on_get(self, req, resp, tenant_id, secret_id):
|
|
||||||
#verify the tenant exists
|
|
||||||
tenant = find_tenant(self.db, tenant_id=tenant_id,
|
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
#verify the secret exists
|
|
||||||
secret = find_secret(self.db, id=secret_id,
|
|
||||||
when_not_found=_secret_not_found)
|
|
||||||
|
|
||||||
#verify the secret belongs to the tenant
|
|
||||||
if not secret in tenant.secrets:
|
|
||||||
_secret_not_found()
|
|
||||||
|
|
||||||
resp.status = falcon.HTTP_200
|
|
||||||
resp.body = json.dumps(secret.format())
|
|
||||||
|
|
||||||
def on_put(self, req, resp, tenant_id, secret_id):
|
|
||||||
#verify the tenant exists
|
|
||||||
tenant = find_tenant(self.db, tenant_id=tenant_id,
|
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
#verify the secret exists
|
|
||||||
secret = find_secret(self.db, id=secret_id,
|
|
||||||
when_not_found=_secret_not_found)
|
|
||||||
|
|
||||||
#verify the secret belongs to the tenant
|
|
||||||
if not secret in tenant.secrets:
|
|
||||||
_secret_not_found()
|
|
||||||
|
|
||||||
#load the message
|
|
||||||
body = load_body(req)
|
|
||||||
|
|
||||||
#if attributes are present in message, update the secret
|
|
||||||
if 'name' in body.keys():
|
|
||||||
secret.name = body['name']
|
|
||||||
|
|
||||||
self.db.commit()
|
|
||||||
resp.status = falcon.HTTP_200
|
|
||||||
|
|
||||||
def on_delete(self, req, resp, tenant_id, secret_id):
|
|
||||||
#verify the tenant exists
|
|
||||||
tenant = find_tenant(self.db, tenant_id=tenant_id,
|
|
||||||
when_not_found=_tenant_not_found)
|
|
||||||
|
|
||||||
#verify the secret exists
|
|
||||||
secret = find_secret(self.db, id=secret_id,
|
|
||||||
when_not_found=_secret_not_found)
|
|
||||||
|
|
||||||
#verify the secret belongs to the tenant
|
|
||||||
if not secret in tenant.secrets:
|
|
||||||
_secret_not_found()
|
|
||||||
|
|
||||||
self.db.delete(secret)
|
|
||||||
self.db.commit()
|
|
||||||
|
|
||||||
resp.status = falcon.HTTP_200
|
|
0
barbican/common/__init__.py
Normal file
0
barbican/common/__init__.py
Normal file
17
barbican/common/config.py
Normal file
17
barbican/common/config.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import os
|
||||||
|
from oslo.config import cfg
|
||||||
|
from barbican.openstack.common import log
|
||||||
|
|
||||||
|
# Ensure the local python config path is on the list to pull config info from
|
||||||
|
CONF_FILES = cfg.find_config_files(prog='barbican-api')
|
||||||
|
print ">>>>>>> "
|
||||||
|
print CONF_FILES
|
||||||
|
#CONF_FILES = cfg.find_config_files(project='barbican', prog='barbican-api')
|
||||||
|
CONF_FILES.append('./etc/barbican-api.conf')
|
||||||
|
CONF_FILES.append('../etc/barbican-api.conf')
|
||||||
|
CONF_FILES = [cfile for cfile in CONF_FILES if os.path.isfile(cfile)]
|
||||||
|
|
||||||
|
# Set configuration files
|
||||||
|
CONF = cfg.CONF
|
||||||
|
CONF(prog='barbican-api', default_config_files=CONF_FILES)
|
||||||
|
#CONF(project='barbican', prog='barbican-api', default_config_files=CONF_FILES)
|
273
barbican/common/exception.py
Normal file
273
barbican/common/exception.py
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2010 United States Government as represented by the
|
||||||
|
# Administrator of the National Aeronautics and Space Administration.
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Barbican exception subclasses"""
|
||||||
|
|
||||||
|
import urlparse
|
||||||
|
from barbican.openstack.common.gettextutils import _
|
||||||
|
|
||||||
|
_FATAL_EXCEPTION_FORMAT_ERRORS = False
|
||||||
|
|
||||||
|
|
||||||
|
class RedirectException(Exception):
|
||||||
|
def __init__(self, url):
|
||||||
|
self.url = urlparse.urlparse(url)
|
||||||
|
|
||||||
|
|
||||||
|
class BarbicanException(Exception):
|
||||||
|
"""
|
||||||
|
Base Barbican Exception
|
||||||
|
|
||||||
|
To correctly use this class, inherit from it and define
|
||||||
|
a 'message' property. That message will get printf'd
|
||||||
|
with the keyword arguments provided to the constructor.
|
||||||
|
"""
|
||||||
|
message = _("An unknown exception occurred")
|
||||||
|
|
||||||
|
def __init__(self, message=None, *args, **kwargs):
|
||||||
|
if not message:
|
||||||
|
message = self.message
|
||||||
|
try:
|
||||||
|
message = message % kwargs
|
||||||
|
except Exception as e:
|
||||||
|
if _FATAL_EXCEPTION_FORMAT_ERRORS:
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
# at least get the core message out if something happened
|
||||||
|
pass
|
||||||
|
|
||||||
|
super(BarbicanException, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class MissingArgumentError(BarbicanException):
|
||||||
|
message = _("Missing required argument.")
|
||||||
|
|
||||||
|
|
||||||
|
class MissingCredentialError(BarbicanException):
|
||||||
|
message = _("Missing required credential: %(required)s")
|
||||||
|
|
||||||
|
|
||||||
|
class BadAuthStrategy(BarbicanException):
|
||||||
|
message = _("Incorrect auth strategy, expected \"%(expected)s\" but "
|
||||||
|
"received \"%(received)s\"")
|
||||||
|
|
||||||
|
|
||||||
|
class NotFound(BarbicanException):
|
||||||
|
message = _("An object with the specified identifier was not found.")
|
||||||
|
|
||||||
|
|
||||||
|
class UnknownScheme(BarbicanException):
|
||||||
|
message = _("Unknown scheme '%(scheme)s' found in URI")
|
||||||
|
|
||||||
|
|
||||||
|
class BadStoreUri(BarbicanException):
|
||||||
|
message = _("The Store URI was malformed.")
|
||||||
|
|
||||||
|
|
||||||
|
class Duplicate(BarbicanException):
|
||||||
|
message = _("An object with the same identifier already exists.")
|
||||||
|
|
||||||
|
|
||||||
|
class StorageFull(BarbicanException):
|
||||||
|
message = _("There is not enough disk space on the image storage media.")
|
||||||
|
|
||||||
|
|
||||||
|
class StorageWriteDenied(BarbicanException):
|
||||||
|
message = _("Permission to write image storage media denied.")
|
||||||
|
|
||||||
|
|
||||||
|
class AuthBadRequest(BarbicanException):
|
||||||
|
message = _("Connect error/bad request to Auth service at URL %(url)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class AuthUrlNotFound(BarbicanException):
|
||||||
|
message = _("Auth service at URL %(url)s not found.")
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizationFailure(BarbicanException):
|
||||||
|
message = _("Authorization failed.")
|
||||||
|
|
||||||
|
|
||||||
|
class NotAuthenticated(BarbicanException):
|
||||||
|
message = _("You are not authenticated.")
|
||||||
|
|
||||||
|
|
||||||
|
class Forbidden(BarbicanException):
|
||||||
|
message = _("You are not authorized to complete this action.")
|
||||||
|
|
||||||
|
|
||||||
|
class ForbiddenPublicImage(Forbidden):
|
||||||
|
message = _("You are not authorized to complete this action.")
|
||||||
|
|
||||||
|
|
||||||
|
class ProtectedImageDelete(Forbidden):
|
||||||
|
message = _("Image %(image_id)s is protected and cannot be deleted.")
|
||||||
|
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): here for backwards-compatability, need to deprecate.
|
||||||
|
class NotAuthorized(Forbidden):
|
||||||
|
message = _("You are not authorized to complete this action.")
|
||||||
|
|
||||||
|
|
||||||
|
class Invalid(BarbicanException):
|
||||||
|
message = _("Data supplied was not valid.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidSortKey(Invalid):
|
||||||
|
message = _("Sort key supplied was not valid.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidFilterRangeValue(Invalid):
|
||||||
|
message = _("Unable to filter using the specified range.")
|
||||||
|
|
||||||
|
|
||||||
|
class ReadonlyProperty(Forbidden):
|
||||||
|
message = _("Attribute '%(property)s' is read-only.")
|
||||||
|
|
||||||
|
|
||||||
|
class ReservedProperty(Forbidden):
|
||||||
|
message = _("Attribute '%(property)s' is reserved.")
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorizationRedirect(BarbicanException):
|
||||||
|
message = _("Redirecting to %(uri)s for authorization.")
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseMigrationError(BarbicanException):
|
||||||
|
message = _("There was an error migrating the database.")
|
||||||
|
|
||||||
|
|
||||||
|
class ClientConnectionError(BarbicanException):
|
||||||
|
message = _("There was an error connecting to a server")
|
||||||
|
|
||||||
|
|
||||||
|
class ClientConfigurationError(BarbicanException):
|
||||||
|
message = _("There was an error configuring the client.")
|
||||||
|
|
||||||
|
|
||||||
|
class MultipleChoices(BarbicanException):
|
||||||
|
message = _("The request returned a 302 Multiple Choices. This generally "
|
||||||
|
"means that you have not included a version indicator in a "
|
||||||
|
"request URI.\n\nThe body of response returned:\n%(body)s")
|
||||||
|
|
||||||
|
|
||||||
|
class LimitExceeded(BarbicanException):
|
||||||
|
message = _("The request returned a 413 Request Entity Too Large. This "
|
||||||
|
"generally means that rate limiting or a quota threshold was "
|
||||||
|
"breached.\n\nThe response body:\n%(body)s")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
|
||||||
|
else None)
|
||||||
|
super(LimitExceeded, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceUnavailable(BarbicanException):
|
||||||
|
message = _("The request returned 503 Service Unavilable. This "
|
||||||
|
"generally occurs on service overload or other transient "
|
||||||
|
"outage.")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.retry_after = (int(kwargs['retry']) if kwargs.get('retry')
|
||||||
|
else None)
|
||||||
|
super(ServiceUnavailable, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ServerError(BarbicanException):
|
||||||
|
message = _("The request returned 500 Internal Server Error.")
|
||||||
|
|
||||||
|
|
||||||
|
class UnexpectedStatus(BarbicanException):
|
||||||
|
message = _("The request returned an unexpected status: %(status)s."
|
||||||
|
"\n\nThe response body:\n%(body)s")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidContentType(BarbicanException):
|
||||||
|
message = _("Invalid content type %(content_type)s")
|
||||||
|
|
||||||
|
|
||||||
|
class BadRegistryConnectionConfiguration(BarbicanException):
|
||||||
|
message = _("Registry was not configured correctly on API server. "
|
||||||
|
"Reason: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class BadStoreConfiguration(BarbicanException):
|
||||||
|
message = _("Store %(store_name)s could not be configured correctly. "
|
||||||
|
"Reason: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class BadDriverConfiguration(BarbicanException):
|
||||||
|
message = _("Driver %(driver_name)s could not be configured correctly. "
|
||||||
|
"Reason: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class StoreDeleteNotSupported(BarbicanException):
|
||||||
|
message = _("Deleting images from this store is not supported.")
|
||||||
|
|
||||||
|
|
||||||
|
class StoreAddDisabled(BarbicanException):
|
||||||
|
message = _("Configuration for store failed. Adding images to this "
|
||||||
|
"store is disabled.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidNotifierStrategy(BarbicanException):
|
||||||
|
message = _("'%(strategy)s' is not an available notifier strategy.")
|
||||||
|
|
||||||
|
|
||||||
|
class MaxRedirectsExceeded(BarbicanException):
|
||||||
|
message = _("Maximum redirects (%(redirects)s) was exceeded.")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidRedirect(BarbicanException):
|
||||||
|
message = _("Received invalid HTTP redirect.")
|
||||||
|
|
||||||
|
|
||||||
|
class NoServiceEndpoint(BarbicanException):
|
||||||
|
message = _("Response from Keystone does not contain a Barbican endpoint.")
|
||||||
|
|
||||||
|
|
||||||
|
class RegionAmbiguity(BarbicanException):
|
||||||
|
message = _("Multiple 'image' service matches for region %(region)s. This "
|
||||||
|
"generally means that a region is required and you have not "
|
||||||
|
"supplied one.")
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerCreationFailure(BarbicanException):
|
||||||
|
message = _("Server worker creation failed: %(reason)s.")
|
||||||
|
|
||||||
|
|
||||||
|
class SchemaLoadError(BarbicanException):
|
||||||
|
message = _("Unable to load schema: %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidObject(BarbicanException):
|
||||||
|
message = _("Provided object does not match schema "
|
||||||
|
"'%(schema)s': %(reason)s")
|
||||||
|
|
||||||
|
|
||||||
|
class UnsupportedHeaderFeature(BarbicanException):
|
||||||
|
message = _("Provided header feature is unsupported: %(feature)s")
|
||||||
|
|
||||||
|
|
||||||
|
class InUseByStore(BarbicanException):
|
||||||
|
message = _("The image cannot be deleted because it is in use through "
|
||||||
|
"the backend store outside of Barbican.")
|
||||||
|
|
||||||
|
|
||||||
|
class ImageSizeLimitExceeded(BarbicanException):
|
||||||
|
message = _("The provided image is too large.")
|
@ -1,22 +0,0 @@
|
|||||||
import os
|
|
||||||
from oslo.config import cfg
|
|
||||||
from barbican.openstack.common import log
|
|
||||||
|
|
||||||
CONFIG_FILE_ENV_VAR = 'CONFIG_FILE'
|
|
||||||
|
|
||||||
if CONFIG_FILE_ENV_VAR in os.environ:
|
|
||||||
_DEFAULT_CONFIG_ARGS = ['--config-file', os.environ['CONFIG_FILE']]
|
|
||||||
else:
|
|
||||||
_DEFAULT_CONFIG_ARGS = ['--config-file', '/etc/barbican/barbican.cfg']
|
|
||||||
|
|
||||||
_config_opts = cfg.ConfigOpts()
|
|
||||||
|
|
||||||
# Configure project logging
|
|
||||||
log.setup('barbican')
|
|
||||||
|
|
||||||
def get_config():
|
|
||||||
return _config_opts
|
|
||||||
|
|
||||||
|
|
||||||
def init_config(cfg_args=_DEFAULT_CONFIG_ARGS):
|
|
||||||
_config_opts(args=cfg_args)
|
|
154
barbican/model/models.py
Normal file
154
barbican/model/models.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
# Copyright (c) 2013 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Defines database models for Barbican
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
from sqlalchemy import Column, Integer, String, BigInteger
|
||||||
|
from sqlalchemy.ext.compiler import compiles
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy import ForeignKey, DateTime, Boolean, Text
|
||||||
|
from sqlalchemy.orm import relationship, backref, object_mapper
|
||||||
|
from sqlalchemy import Index, UniqueConstraint
|
||||||
|
|
||||||
|
from barbican.openstack.common import timeutils
|
||||||
|
from barbican.openstack.common import uuidutils
|
||||||
|
|
||||||
|
BASE = declarative_base()
|
||||||
|
|
||||||
|
|
||||||
|
@compiles(BigInteger, 'sqlite')
|
||||||
|
def compile_big_int_sqlite(type_, compiler, **kw):
|
||||||
|
return 'INTEGER'
|
||||||
|
|
||||||
|
|
||||||
|
class ModelBase(object):
|
||||||
|
"""Base class for Nova and Barbican Models"""
|
||||||
|
__table_args__ = {'mysql_engine': 'InnoDB'}
|
||||||
|
__table_initialized__ = False
|
||||||
|
__protected_attributes__ = set([
|
||||||
|
"created_at", "updated_at", "deleted_at", "deleted"])
|
||||||
|
|
||||||
|
id = Column(String(36), primary_key=True, default=uuidutils.generate_uuid)
|
||||||
|
|
||||||
|
created_at = Column(DateTime, default=timeutils.utcnow,
|
||||||
|
nullable=False)
|
||||||
|
updated_at = Column(DateTime, default=timeutils.utcnow,
|
||||||
|
nullable=False, onupdate=timeutils.utcnow)
|
||||||
|
deleted_at = Column(DateTime)
|
||||||
|
deleted = Column(Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
|
def save(self, session=None):
|
||||||
|
"""Save this object"""
|
||||||
|
# import api here to prevent circular dependency problem
|
||||||
|
import barbican.model.repositories
|
||||||
|
session = session or barbican.model.repositories.get_session()
|
||||||
|
session.add(self)
|
||||||
|
session.flush()
|
||||||
|
|
||||||
|
def delete(self, session=None):
|
||||||
|
"""Delete this object"""
|
||||||
|
self.deleted = True
|
||||||
|
self.deleted_at = timeutils.utcnow()
|
||||||
|
self.save(session=session)
|
||||||
|
|
||||||
|
def update(self, values):
|
||||||
|
"""dict.update() behaviour."""
|
||||||
|
for k, v in values.iteritems():
|
||||||
|
self[k] = v
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
setattr(self, key, value)
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
return getattr(self, key)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
self._i = iter(object_mapper(self).columns)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
n = self._i.next().name
|
||||||
|
return n, getattr(self, n)
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return self.__dict__.keys()
|
||||||
|
|
||||||
|
def values(self):
|
||||||
|
return self.__dict__.values()
|
||||||
|
|
||||||
|
def items(self):
|
||||||
|
return self.__dict__.items()
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
return self.__dict__.copy()
|
||||||
|
|
||||||
|
def to_dict_fields(self):
|
||||||
|
"""Returns a dictionary of just the db fields of this entity."""
|
||||||
|
dict_fields = {'id':self.id,
|
||||||
|
'created':self.created_at,
|
||||||
|
'updated':self.updated_at}
|
||||||
|
if self.deleted_at:
|
||||||
|
dict_fields['deleted'] = self.deleted_at
|
||||||
|
if self.deleted:
|
||||||
|
dict_fields['is_deleted'] = True
|
||||||
|
dict_fields.update(self._do_extra_dict_fields())
|
||||||
|
return dict_fields
|
||||||
|
|
||||||
|
def _do_extra_dict_fields(self):
|
||||||
|
"""Sub-class hook method: return dict of fields."""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class Tenant(BASE, ModelBase):
|
||||||
|
"""
|
||||||
|
Represents a Tenant in the datastore
|
||||||
|
|
||||||
|
Tenants are users that wish to store secret information within
|
||||||
|
Cloudkeep's Barbican.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = 'tenants'
|
||||||
|
|
||||||
|
username = Column(String(255))
|
||||||
|
|
||||||
|
def _do_extra_dict_fields(self):
|
||||||
|
"""Sub-class hook method: return dict of fields."""
|
||||||
|
return {'username':self.username}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Keep this tuple synchronized with the models in the file
|
||||||
|
MODELS = [Tenant]
|
||||||
|
|
||||||
|
|
||||||
|
def register_models(engine):
|
||||||
|
"""
|
||||||
|
Creates database tables for all models with the given engine
|
||||||
|
"""
|
||||||
|
# TBD: Remove this:
|
||||||
|
print "models: %s" % `MODELS`
|
||||||
|
for model in MODELS:
|
||||||
|
model.metadata.create_all(engine)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister_models(engine):
|
||||||
|
"""
|
||||||
|
Drops database tables for all models with the given engine
|
||||||
|
"""
|
||||||
|
for model in MODELS:
|
||||||
|
model.metadata.drop_all(engine)
|
@ -23,15 +23,18 @@ import logging
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
import sqlalchemy.orm as sa_orm
|
import sqlalchemy.orm as sa_orm
|
||||||
import sqlalchemy.sql as sa_sql
|
import sqlalchemy.sql as sa_sql
|
||||||
|
|
||||||
from barbican.common import exception
|
from barbican.common import exception
|
||||||
|
from barbican.common import config
|
||||||
# TBD: from barbican.db.sqlalchemy import migration
|
# TBD: from barbican.db.sqlalchemy import migration
|
||||||
from barbican.model import models
|
from barbican.model import models
|
||||||
import barbican.openstack.common.log as os_logging
|
import barbican.openstack.common.log as os_logging
|
||||||
from barbican.openstack.common import timeutils
|
from barbican.openstack.common import timeutils
|
||||||
|
from barbican.openstack.common.gettextutils import _
|
||||||
|
|
||||||
|
|
||||||
_ENGINE = None
|
_ENGINE = None
|
||||||
@ -50,7 +53,8 @@ db_opts = [
|
|||||||
cfg.IntOpt('sql_idle_timeout', default=3600),
|
cfg.IntOpt('sql_idle_timeout', default=3600),
|
||||||
cfg.IntOpt('sql_max_retries', default=60),
|
cfg.IntOpt('sql_max_retries', default=60),
|
||||||
cfg.IntOpt('sql_retry_interval', default=1),
|
cfg.IntOpt('sql_retry_interval', default=1),
|
||||||
cfg.BoolOpt('db_auto_create', default=False),
|
cfg.BoolOpt('db_auto_create', default=True),
|
||||||
|
cfg.StrOpt('sql_connection', default=None),
|
||||||
]
|
]
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
@ -68,6 +72,8 @@ def setup_db_env():
|
|||||||
_MAX_RETRIES = CONF.sql_max_retries
|
_MAX_RETRIES = CONF.sql_max_retries
|
||||||
_RETRY_INTERVAL = CONF.sql_retry_interval
|
_RETRY_INTERVAL = CONF.sql_retry_interval
|
||||||
_CONNECTION = CONF.sql_connection
|
_CONNECTION = CONF.sql_connection
|
||||||
|
# TBD: Remove:
|
||||||
|
print "Conn = %s" % _CONNECTION
|
||||||
sa_logger = logging.getLogger('sqlalchemy.engine')
|
sa_logger = logging.getLogger('sqlalchemy.engine')
|
||||||
if CONF.debug:
|
if CONF.debug:
|
||||||
sa_logger.setLevel(logging.DEBUG)
|
sa_logger.setLevel(logging.DEBUG)
|
||||||
@ -111,6 +117,8 @@ def get_engine():
|
|||||||
'convert_unicode': True}
|
'convert_unicode': True}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# TBD: Remove
|
||||||
|
print "Conn: %s; Args: %s" % (_CONNECTION, engine_args)
|
||||||
_ENGINE = sqlalchemy.create_engine(_CONNECTION, **engine_args)
|
_ENGINE = sqlalchemy.create_engine(_CONNECTION, **engine_args)
|
||||||
|
|
||||||
# TBD: if 'mysql' in connection_dict.drivername:
|
# TBD: if 'mysql' in connection_dict.drivername:
|
||||||
@ -131,7 +139,6 @@ def get_engine():
|
|||||||
if CONF.db_auto_create:
|
if CONF.db_auto_create:
|
||||||
LOG.info(_('auto-creating barbican registry DB'))
|
LOG.info(_('auto-creating barbican registry DB'))
|
||||||
models.register_models(_ENGINE)
|
models.register_models(_ENGINE)
|
||||||
LOG.warn(_('(Not doing this yet!) auto-creating barbican registry DB'))
|
|
||||||
# TBD: try:
|
# TBD: try:
|
||||||
# TBD: migration.version_control()
|
# TBD: migration.version_control()
|
||||||
# TBD: except exception.DatabaseMigrationError:
|
# TBD: except exception.DatabaseMigrationError:
|
||||||
@ -204,18 +211,23 @@ class BaseRepo(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
print "BaseRepo"
|
||||||
|
LOG.debug("BaseRepo init...")
|
||||||
|
configure_db()
|
||||||
|
|
||||||
def get_session(self, session=None):
|
def get_session(self, session=None):
|
||||||
|
print "Getting session..."
|
||||||
return session or get_session()
|
return session or get_session()
|
||||||
|
|
||||||
def find_by_name(self, name, suppress_exception, session=None):
|
def find_by_name(self, name, suppress_exception=False, session=None):
|
||||||
session = self.get_session(session)
|
session = self.get_session(session)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
print "Starting find by name steps..."
|
||||||
query = self._do_build_query_by_name(name, session)
|
query = self._do_build_query_by_name(name, session)
|
||||||
|
print "...query = %s" % `query`
|
||||||
entity = query.one()
|
entity = query.one()
|
||||||
|
print "...post query.one()"
|
||||||
|
|
||||||
except sa_orm.exc.NoResultFound:
|
except sa_orm.exc.NoResultFound:
|
||||||
entity = None
|
entity = None
|
||||||
@ -225,7 +237,7 @@ class BaseRepo(object):
|
|||||||
|
|
||||||
return entity
|
return entity
|
||||||
|
|
||||||
def get(self, entity_id, force_show_deleted=False, session=None):
|
def get(self, entity_id, force_show_deleted=False, suppress_exception=False, session=None):
|
||||||
"""Get an entity or raise if it does not exist."""
|
"""Get an entity or raise if it does not exist."""
|
||||||
session = self.get_session(session)
|
session = self.get_session(session)
|
||||||
|
|
||||||
@ -239,8 +251,10 @@ class BaseRepo(object):
|
|||||||
entity = query.one()
|
entity = query.one()
|
||||||
|
|
||||||
except sa_orm.exc.NoResultFound:
|
except sa_orm.exc.NoResultFound:
|
||||||
raise exception.NotFound("No %s found with ID %s"
|
entity = None
|
||||||
% (self._do_entity_name(), entity_id))
|
if not suppress_exception:
|
||||||
|
raise exception.NotFound("No %s found with ID %s"
|
||||||
|
% (self._do_entity_name(), entity_id))
|
||||||
|
|
||||||
return entity
|
return entity
|
||||||
|
|
||||||
@ -285,6 +299,13 @@ class BaseRepo(object):
|
|||||||
"""
|
"""
|
||||||
return self._update(values, entity_id, purge_props)
|
return self._update(values, entity_id, purge_props)
|
||||||
|
|
||||||
|
def delete_entity(self, entity):
|
||||||
|
"""Remove the entity"""
|
||||||
|
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
entity.delete(session=session)
|
||||||
|
|
||||||
def _do_entity_name(self):
|
def _do_entity_name(self):
|
||||||
"""Sub-class hook: return entity name, such as for debugging."""
|
"""Sub-class hook: return entity name, such as for debugging."""
|
||||||
return "Entity"
|
return "Entity"
|
||||||
@ -384,7 +405,7 @@ class TenantRepo(BaseRepo):
|
|||||||
def _do_build_query_by_name(self, name, session):
|
def _do_build_query_by_name(self, name, session):
|
||||||
"""Sub-class hook: find entity by name."""
|
"""Sub-class hook: find entity by name."""
|
||||||
return session.query(models.Tenant)\
|
return session.query(models.Tenant)\
|
||||||
.filter_by(name=name)
|
.filter_by(username=name)
|
||||||
|
|
||||||
def _do_build_get_query(self, entity_id, session):
|
def _do_build_get_query(self, entity_id, session):
|
||||||
"""Sub-class hook: build a retrieve query."""
|
"""Sub-class hook: build a retrieve query."""
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# Copyright 2011 Justin Santa Barbara
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Represents a secret to store in Cloudkeep's Barbican."""
|
|
||||||
|
|
||||||
#import barbican.model.Tenant
|
|
||||||
from base import Base
|
|
||||||
from sqlalchemy import Table, Column, String
|
|
||||||
from sqlalchemy import Integer, ForeignKey, Boolean
|
|
||||||
from sqlalchemy.orm import relationship, backref
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
|
||||||
|
|
||||||
#
|
|
||||||
# class Secret(Base):
|
|
||||||
# """
|
|
||||||
# A secret is any information that needs to be stored and protected within
|
|
||||||
# Cloudkeep's Barbican.
|
|
||||||
# """
|
|
||||||
#
|
|
||||||
# secret_id = Column(String)
|
|
||||||
# name = Column(String)
|
|
||||||
# tenant_id = Column(Integer, ForeignKey('tenant.id'))
|
|
||||||
# tenant = relationship(Tenant, primaryjoin=tenant_id == Tenant.id)
|
|
||||||
#
|
|
||||||
# def __init__(self, secret_id):
|
|
||||||
# self.secret_id = secret_id
|
|
@ -1,86 +0,0 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
||||||
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# Copyright 2011 Justin Santa Barbara
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
"""Generic Node base class for all workers that run on hosts."""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
from base import Base
|
|
||||||
from sqlalchemy import Table, Column, String
|
|
||||||
from sqlalchemy import Integer, ForeignKey, Boolean
|
|
||||||
from sqlalchemy.orm import relationship, relation, backref
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
|
||||||
|
|
||||||
|
|
||||||
class Tenant(Base):
|
|
||||||
"""
|
|
||||||
Tenants are users that wish to store secret information within
|
|
||||||
Cloudkeep's Barbican.
|
|
||||||
"""
|
|
||||||
|
|
||||||
logging.debug('In Tenant table setup')
|
|
||||||
|
|
||||||
__tablename__ = "tenants"
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
username = Column(String)
|
|
||||||
# secrets = relationship('Secret', backref='tenant', lazy='dynamic')
|
|
||||||
# secrets = relationship('Secret', secondary=_secrets)
|
|
||||||
# secrets = relationship("Secret",
|
|
||||||
# order_by="desc(Secret.name)",
|
|
||||||
# primaryjoin="Secret.tenant_id==Tenant.id")
|
|
||||||
|
|
||||||
def __init__(self, username):
|
|
||||||
self.username = username
|
|
||||||
|
|
||||||
def __init__(self, username, secrets=[]):
|
|
||||||
self.username = username
|
|
||||||
self.secrets = secrets
|
|
||||||
|
|
||||||
def format(self):
|
|
||||||
return {'id': self.id,
|
|
||||||
'username': self.username}
|
|
||||||
|
|
||||||
|
|
||||||
class Secret(Base):
|
|
||||||
"""
|
|
||||||
A secret is any information that needs to be stored and protected within
|
|
||||||
Cloudkeep's Barbican.
|
|
||||||
"""
|
|
||||||
|
|
||||||
__tablename__ = "secrets"
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
name = Column(String)
|
|
||||||
tenant_id = Column(Integer, ForeignKey('tenants.id'))
|
|
||||||
tenant = relationship("Tenant", backref=backref('secrets', order_by=id))
|
|
||||||
# tenant = relationship(Tenant, primaryjoin=tenant_id == Tenant.id)
|
|
||||||
|
|
||||||
# creates a bidirectional relationship
|
|
||||||
# from Secret to Tenant it's Many-to-One
|
|
||||||
# from Tenant to Secret it's One-to-Many
|
|
||||||
# tenant = relation(Tenant, backref=backref('secret', order_by=id))
|
|
||||||
|
|
||||||
def __init__(self, tenant_id, name):
|
|
||||||
self.tenant_id = tenant_id
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
def format(self):
|
|
||||||
return {'id': self.id,
|
|
||||||
'name': self.username,
|
|
||||||
'tenant_id': self.tenant_id}
|
|
@ -1,33 +0,0 @@
|
|||||||
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
|
|
||||||
|
|
||||||
from barbican.model.tenant import Tenant, Secret
|
|
||||||
|
|
||||||
|
|
||||||
def _empty_condition():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def find_tenant(db_session, id=None, username=None,
|
|
||||||
when_not_found=_empty_condition,
|
|
||||||
when_multiple_found=_empty_condition):
|
|
||||||
try:
|
|
||||||
if id:
|
|
||||||
return db_session.query(Tenant).filter_by(id=id).one()
|
|
||||||
elif username:
|
|
||||||
return db_session.query(Tenant).filter_by(username=username).one()
|
|
||||||
except NoResultFound:
|
|
||||||
when_not_found()
|
|
||||||
except MultipleResultsFound:
|
|
||||||
when_multiple_found()
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def find_secret(db_session, id, when_not_found=_empty_condition,
|
|
||||||
when_multiple_found=_empty_condition):
|
|
||||||
try:
|
|
||||||
return db_session.query(Secret).filter_by(id=id).one()
|
|
||||||
except NoResultFound:
|
|
||||||
when_not_found()
|
|
||||||
except MultipleResultsFound:
|
|
||||||
when_multiple_found()
|
|
@ -1,8 +1,6 @@
|
|||||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
# Copyright 2010 United States Government as represented by the
|
# Copyright (c) 2012 Intel Corporation.
|
||||||
# Administrator of the National Aeronautics and Space Administration.
|
|
||||||
# Copyright 2011 Justin Santa Barbara
|
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -17,19 +15,25 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
"""Base class for all model objects."""
|
"""
|
||||||
|
UUID related utilities and helper functions.
|
||||||
|
"""
|
||||||
|
|
||||||
from sqlalchemy import Column
|
import uuid
|
||||||
from sqlalchemy import Integer, ForeignKey
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
|
||||||
|
|
||||||
|
|
||||||
class Persisted(object):
|
def generate_uuid():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
|
||||||
|
|
||||||
@declared_attr
|
def is_uuid_like(val):
|
||||||
def __tablename__(self):
|
"""Returns validation of a value as a UUID.
|
||||||
return self.__name__.lower()
|
|
||||||
|
|
||||||
Base = declarative_base(cls=Persisted)
|
For our purposes, a UUID is a canonical form string:
|
||||||
|
aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return str(uuid.UUID(val)) == val
|
||||||
|
except (TypeError, ValueError, AttributeError):
|
||||||
|
return False
|
@ -1,6 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from barbican.api.resources import *
|
from barbican.api.resources import *
|
||||||
from barbican.model.tenant import Tenant
|
from barbican.model.models import Tenant
|
||||||
|
from barbican.common import config
|
||||||
|
|
||||||
from mock import MagicMock
|
from mock import MagicMock
|
||||||
|
|
||||||
@ -38,29 +39,34 @@ class WhenTestingVersionResource(unittest.TestCase):
|
|||||||
class WhenCreatingTenantsUsingTenantsResource(unittest.TestCase):
|
class WhenCreatingTenantsUsingTenantsResource(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
db_filter = MagicMock()
|
self.username = '1234'
|
||||||
db_filter.one.return_value = Tenant('tenant_id')
|
|
||||||
|
self.tenant_repo = MagicMock()
|
||||||
db_query = MagicMock()
|
self.tenant_repo.find_by_name.return_value = None
|
||||||
db_query.filter_by.return_value = db_filter
|
self.tenant_repo.create_from.return_value = None
|
||||||
|
|
||||||
self.db_session = MagicMock()
|
|
||||||
self.db_session.query.return_value = db_query
|
|
||||||
|
|
||||||
self.stream = MagicMock()
|
self.stream = MagicMock()
|
||||||
self.stream.read.return_value = u'{ "username" : "1234" }'
|
self.stream.read.return_value = u'{ "username" : "%s" }' % self.username
|
||||||
|
|
||||||
self.req = MagicMock()
|
self.req = MagicMock()
|
||||||
self.req.stream = self.stream
|
self.req.stream = self.stream
|
||||||
|
|
||||||
self.resp = MagicMock()
|
self.resp = MagicMock()
|
||||||
self.resource = TenantsResource(self.db_session)
|
self.resource = TenantsResource(self.tenant_repo)
|
||||||
|
|
||||||
|
def test_should_add_new_tenant(self):
|
||||||
|
self.resource.on_post(self.req, self.resp)
|
||||||
|
|
||||||
|
self.tenant_repo.find_by_name.assert_called_once_with(name=self.username, suppress_exception=True)
|
||||||
|
# TBD: Make this work: self.tenant_repo.create_from.assert_called_once_with(unittest.mock.ANY)
|
||||||
|
|
||||||
def test_should_throw_exception_for_tenants_that_exist(self):
|
def test_should_throw_exception_for_tenants_that_exist(self):
|
||||||
|
self.tenant_repo.find_by_name.return_value = Tenant()
|
||||||
|
|
||||||
with self.assertRaises(falcon.HTTPError):
|
with self.assertRaises(falcon.HTTPError):
|
||||||
self.resource.on_post(self.req, self.resp)
|
self.resource.on_post(self.req, self.resp)
|
||||||
|
|
||||||
self.db_session.query.assert_called_once_with(Tenant)
|
self.tenant_repo.find_by_name.assert_called_once_with(name=self.username, suppress_exception=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -2,7 +2,7 @@ import os
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
from barbican.config import init_config, get_config
|
from barbican.common import config
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Configuration test configuration options
|
# Configuration test configuration options
|
||||||
@ -17,6 +17,8 @@ CFG_TEST_OPTIONS = [
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
def suite():
|
def suite():
|
||||||
@ -32,16 +34,10 @@ class WhenConfiguring(unittest.TestCase):
|
|||||||
|
|
||||||
LOG.debug("In test 'test_loading'")
|
LOG.debug("In test 'test_loading'")
|
||||||
|
|
||||||
try:
|
CONF.register_group(test_group)
|
||||||
init_config(['--config-file', './etc/barbican/barbican.conf'])
|
CONF.register_opts(CFG_TEST_OPTIONS, group=test_group)
|
||||||
except:
|
|
||||||
init_config(['--config-file', '../etc/barbican/barbican.conf'])
|
|
||||||
|
|
||||||
conf = get_config()
|
self.assertTrue(CONF.test.should_pass)
|
||||||
conf.register_group(test_group)
|
|
||||||
conf.register_opts(CFG_TEST_OPTIONS, group=test_group)
|
|
||||||
|
|
||||||
self.assertTrue(conf.test.should_pass)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
90
etc/barbican-api.conf
Normal file
90
etc/barbican-api.conf
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
# Show more verbose log output (sets INFO log level output)
|
||||||
|
verbose = True
|
||||||
|
|
||||||
|
# Show debugging output in logs (sets DEBUG log level output)
|
||||||
|
debug = True
|
||||||
|
|
||||||
|
# Address to bind the API server
|
||||||
|
bind_host = 0.0.0.0
|
||||||
|
|
||||||
|
# Port the bind the API server to
|
||||||
|
bind_port = 9292
|
||||||
|
|
||||||
|
# Log to this file. Make sure you do not set the same log
|
||||||
|
# file for both the API and registry servers!
|
||||||
|
log_file = /var/log/barbican/api.log
|
||||||
|
|
||||||
|
# Backlog requests when creating socket
|
||||||
|
backlog = 4096
|
||||||
|
|
||||||
|
# TCP_KEEPIDLE value in seconds when creating socket.
|
||||||
|
# Not supported on OS X.
|
||||||
|
#tcp_keepidle = 600
|
||||||
|
|
||||||
|
# SQLAlchemy connection string for the reference implementation
|
||||||
|
# registry server. Any valid SQLAlchemy connection string is fine.
|
||||||
|
# See: http://www.sqlalchemy.org/docs/05/reference/sqlalchemy/connections.html#sqlalchemy.create_engine
|
||||||
|
sql_connection = sqlite:///barbican.sqlite
|
||||||
|
|
||||||
|
# Period in seconds after which SQLAlchemy should reestablish its connection
|
||||||
|
# to the database.
|
||||||
|
#
|
||||||
|
# MySQL uses a default `wait_timeout` of 8 hours, after which it will drop
|
||||||
|
# idle connections. This can result in 'MySQL Gone Away' exceptions. If you
|
||||||
|
# notice this, you can lower this value to ensure that SQLAlchemy reconnects
|
||||||
|
# before MySQL can drop the connection.
|
||||||
|
sql_idle_timeout = 3600
|
||||||
|
|
||||||
|
# Number of Barbican API worker processes to start.
|
||||||
|
# On machines with more than one CPU increasing this value
|
||||||
|
# may improve performance (especially if using SSL with
|
||||||
|
# compression turned on). It is typically recommended to set
|
||||||
|
# this value to the number of CPUs present on your machine.
|
||||||
|
workers = 1
|
||||||
|
|
||||||
|
# Role used to identify an authenticated user as administrator
|
||||||
|
#admin_role = admin
|
||||||
|
|
||||||
|
# Allow unauthenticated users to access the API with read-only
|
||||||
|
# privileges. This only applies when using ContextMiddleware.
|
||||||
|
#allow_anonymous_access = False
|
||||||
|
|
||||||
|
# Allow access to version 1 of barbican api
|
||||||
|
#enable_v1_api = True
|
||||||
|
|
||||||
|
# Allow access to version 2 of barbican api
|
||||||
|
#enable_v2_api = True
|
||||||
|
|
||||||
|
# ================= SSL Options ===============================
|
||||||
|
|
||||||
|
# Certificate file to use when starting API server securely
|
||||||
|
#cert_file = /path/to/certfile
|
||||||
|
|
||||||
|
# Private key file to use when starting API server securely
|
||||||
|
#key_file = /path/to/keyfile
|
||||||
|
|
||||||
|
# CA certificate file to use to verify connecting clients
|
||||||
|
#ca_file = /path/to/cafile
|
||||||
|
|
||||||
|
# ================= Security Options ==========================
|
||||||
|
|
||||||
|
# AES key for encrypting store 'location' metadata, including
|
||||||
|
# -- if used -- Swift or S3 credentials
|
||||||
|
# Should be set to a random string of length 16, 24 or 32 bytes
|
||||||
|
#metadata_encryption_key = <16, 24 or 32 char registry metadata key>
|
||||||
|
|
||||||
|
# ============ Delayed Delete Options =============================
|
||||||
|
|
||||||
|
# Turn on/off delayed delete
|
||||||
|
delayed_delete = False
|
||||||
|
|
||||||
|
# Delayed delete time in seconds
|
||||||
|
scrub_time = 43200
|
||||||
|
|
||||||
|
# Directory that the scrubber will use to remind itself of what to delete
|
||||||
|
# Make sure this is also set in glance-scrubber.conf
|
||||||
|
scrubber_datadir = /var/lib/barbican/scrubber
|
||||||
|
|
||||||
|
[test]
|
||||||
|
should_pass = true
|
@ -1,8 +0,0 @@
|
|||||||
# Default Cloudkeep Barbican Config File
|
|
||||||
|
|
||||||
[logging]
|
|
||||||
use_stderr=true
|
|
||||||
|
|
||||||
[test]
|
|
||||||
should_pass = true
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
config = {
|
|
||||||
'sqlalchemy': {
|
|
||||||
'url': 'sqlite:////tmp/barbican.db',
|
|
||||||
'echo': True,
|
|
||||||
'echo_pool': False,
|
|
||||||
'pool_recycle': 3600,
|
|
||||||
'encoding': 'utf-8'
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
|
||||||
# The list of modules to copy from openstack-common
|
# The list of modules to copy from openstack-common
|
||||||
modules=gettextutils,jsonutils,log,local,notifier,timeutils
|
modules=gettextutils,jsonutils,log,local,notifier,timeutils,uuidutils
|
||||||
|
|
||||||
# The base module to hold the copy of openstack.common
|
# The base module to hold the copy of openstack.common
|
||||||
base=barbican
|
base=barbican
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[uwsgi]
|
[uwsgi]
|
||||||
|
|
||||||
socket = 172.16.83.131:8080
|
socket = :8080
|
||||||
protocol = http
|
protocol = http
|
||||||
|
|
||||||
processes = 1
|
processes = 1
|
||||||
@ -11,7 +11,7 @@ vaccum = true
|
|||||||
no-default-app = true
|
no-default-app = true
|
||||||
memory-report = true
|
memory-report = true
|
||||||
|
|
||||||
env = CONFIG_FILE=/etc/barbican/barbican.cfg
|
#env = CONFIG_FILE=/etc/barbican/barbican.cfg
|
||||||
|
|
||||||
pythonpath = ./
|
pythonpath = ./
|
||||||
module = barbican.api.app:application
|
module = barbican.api.app:application
|
||||||
|
Loading…
Reference in New Issue
Block a user