merge from upstream
This commit is contained in:
commit
e74e34b7d5
@ -25,6 +25,8 @@ LOG = log.getLogger(__name__)
|
||||
_DRIVER_OPTIONS = [
|
||||
cfg.StrOpt('transport', default='falcon',
|
||||
help='Transport driver to use'),
|
||||
cfg.StrOpt('manager', default='default',
|
||||
help='Manager driver to use'),
|
||||
cfg.StrOpt('storage', default='mockdb',
|
||||
help='Storage driver to use'),
|
||||
]
|
||||
@ -72,7 +74,7 @@ class Bootstrap(object):
|
||||
storage_type = 'cdn.storage'
|
||||
storage_name = self.driver_conf.storage
|
||||
|
||||
args = [self.conf, self.provider]
|
||||
args = [self.conf]
|
||||
|
||||
try:
|
||||
mgr = driver.DriverManager(namespace=storage_type,
|
||||
@ -83,6 +85,25 @@ class Bootstrap(object):
|
||||
except RuntimeError as exc:
|
||||
LOG.exception(exc)
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def manager(self):
|
||||
LOG.debug((u'Loading manager driver'))
|
||||
|
||||
# create the driver manager to load the appropriate drivers
|
||||
manager_type = 'cdn.manager'
|
||||
manager_name = self.driver_conf.manager
|
||||
|
||||
args = [self.conf, self.storage, self.provider]
|
||||
|
||||
try:
|
||||
mgr = driver.DriverManager(namespace=manager_type,
|
||||
name=manager_name,
|
||||
invoke_on_load=True,
|
||||
invoke_args=args)
|
||||
return mgr.driver
|
||||
except RuntimeError as exc:
|
||||
LOG.exception(exc)
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def transport(self):
|
||||
LOG.debug("loading transport")
|
||||
@ -91,7 +112,7 @@ class Bootstrap(object):
|
||||
transport_type = 'cdn.transport'
|
||||
transport_name = self.driver_conf.transport
|
||||
|
||||
args = [self.conf, self.storage]
|
||||
args = [self.conf, self.manager]
|
||||
|
||||
LOG.debug((u'Loading transport driver: %s'), transport_name)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2013 Red Hat, Inc.
|
||||
# Copyright (c) 2014 Rackspace, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -15,16 +15,22 @@
|
||||
|
||||
import functools
|
||||
|
||||
import msgpack
|
||||
|
||||
def cached_getattr(meth):
|
||||
"""Caches attributes returned by __getattr__
|
||||
import cdn.openstack.common.log as logging
|
||||
|
||||
It can be used to cache results from
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def memoized_getattr(meth):
|
||||
"""Memoizes attributes returned by __getattr__
|
||||
|
||||
It can be used to remember the results from
|
||||
__getattr__ and reduce the debt of calling
|
||||
it again when the same attribute is accessed.
|
||||
|
||||
This decorator caches attributes by setting
|
||||
them in the object itself.
|
||||
This decorator memoizes attributes by setting
|
||||
them on the object itself.
|
||||
|
||||
The wrapper returned by this decorator won't alter
|
||||
the returned value.
|
||||
@ -40,6 +46,91 @@ def cached_getattr(meth):
|
||||
return wrapper
|
||||
|
||||
|
||||
def caches(keygen, ttl, cond=None):
|
||||
"""Flags a getter method as being cached using oslo.cache.
|
||||
|
||||
It is assumed that the containing class defines an attribute
|
||||
named `_cache` that is an instance of an oslo.cache backend.
|
||||
|
||||
The getter should raise an exception if the value can't be
|
||||
loaded, which will skip the caching step. Otherwise, the
|
||||
getter must return a value that can be encoded with
|
||||
msgpack.
|
||||
|
||||
Note that you can also flag a remover method such that it
|
||||
will purge an associated item from the cache, e.g.:
|
||||
|
||||
def project_cache_key(user, project=None):
|
||||
return user + ':' + str(project)
|
||||
|
||||
class Project(object):
|
||||
def __init__(self, db, cache):
|
||||
self._db = db
|
||||
self._cache = cache
|
||||
|
||||
@decorators.caches(project_cache_key, 60)
|
||||
def get_project(self, user, project=None):
|
||||
return self._db.get_project(user, project)
|
||||
|
||||
@get_project.purges
|
||||
def del_project(self, user, project=None):
|
||||
self._db.delete_project(user, project)
|
||||
|
||||
:param keygen: A static key generator function. This function
|
||||
must accept the same arguments as the getter, sans `self`.
|
||||
:param ttl: TTL for the cache entry, in seconds.
|
||||
:param cond: Conditional for whether or not to cache the
|
||||
value. Must be a function that takes a single value, and
|
||||
returns True or False.
|
||||
"""
|
||||
|
||||
def purges_prop(remover):
|
||||
|
||||
@functools.wraps(remover)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
# First, purge from cache
|
||||
key = keygen(*args, **kwargs)
|
||||
del self._cache[key]
|
||||
|
||||
# Remove/delete from origin
|
||||
remover(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
def prop(getter):
|
||||
|
||||
@functools.wraps(getter)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
key = keygen(*args, **kwargs)
|
||||
packed_value = self._cache.get(key)
|
||||
|
||||
if packed_value is None:
|
||||
value = getter(self, *args, **kwargs)
|
||||
|
||||
# Cache new value if desired
|
||||
if cond is None or cond(value):
|
||||
# NOTE(kgriffs): Setting use_bin_type is essential
|
||||
# for being able to distinguish between Unicode
|
||||
# and binary strings when decoding; otherwise,
|
||||
# both types are normalized to the MessagePack
|
||||
# str format family.
|
||||
packed_value = msgpack.packb(value, use_bin_type=True)
|
||||
|
||||
if not self._cache.set(key, packed_value, ttl):
|
||||
LOG.warn('Failed to cache key: ' + key)
|
||||
else:
|
||||
# NOTE(kgriffs): unpackb does not default to UTF-8,
|
||||
# so we have to explicitly ask for it.
|
||||
value = msgpack.unpackb(packed_value, encoding='utf-8')
|
||||
|
||||
return value
|
||||
|
||||
wrapper.purges = purges_prop
|
||||
return wrapper
|
||||
|
||||
return prop
|
||||
|
||||
|
||||
def lazy_property(write=False, delete=True):
|
||||
"""Creates a lazy property.
|
||||
|
||||
|
22
cdn/manager/base/__init__.py
Normal file
22
cdn/manager/base/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
from cdn.manager.base import driver
|
||||
from cdn.manager.base import services
|
||||
|
||||
|
||||
Driver = driver.ManagerDriverBase
|
||||
|
||||
ServicesController = services.ServicesControllerBase
|
30
cdn/manager/base/controller.py
Normal file
30
cdn/manager/base/controller.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ManagerControllerBase(object):
|
||||
|
||||
"""Top-level class for controllers.
|
||||
|
||||
:param driver: Instance of the driver
|
||||
instantiating this controller.
|
||||
"""
|
||||
|
||||
def __init__(self, driver):
|
||||
self._driver = driver
|
38
cdn/manager/base/driver.py
Normal file
38
cdn/manager/base/driver.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ManagerDriverBase(object):
|
||||
def __init__(self, conf, storage, providers):
|
||||
self._conf = conf
|
||||
self._storage = storage
|
||||
self._providers = providers
|
||||
|
||||
@property
|
||||
def storage(self):
|
||||
return self._storage
|
||||
|
||||
@property
|
||||
def providers(self):
|
||||
return self._providers
|
||||
|
||||
@abc.abstractproperty
|
||||
def services_controller(self):
|
||||
"""Returns the driver's services controller."""
|
||||
raise NotImplementedError
|
25
cdn/manager/base/providers.py
Normal file
25
cdn/manager/base/providers.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
|
||||
class ProviderWrapper(object):
|
||||
def create(self, ext, service_name, service_json):
|
||||
return ext.obj.service_controller.create(service_name, service_json)
|
||||
|
||||
def update(self, ext, service_name, service_json):
|
||||
return ext.obj.service_controller.update(service_name, service_json)
|
||||
|
||||
def delete(self, ext, service_name):
|
||||
return ext.obj.service_controller.delete(service_name)
|
49
cdn/manager/base/services.py
Normal file
49
cdn/manager/base/services.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from cdn.manager.base import controller
|
||||
from cdn.manager.base import providers
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ServicesControllerBase(controller.ManagerControllerBase):
|
||||
|
||||
def __init__(self, manager):
|
||||
super(ServicesControllerBase, self).__init__(manager)
|
||||
|
||||
self.provider_wrapper = providers.ProviderWrapper()
|
||||
|
||||
@abc.abstractmethod
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def get(self, project_id, service_name):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, project_id, service_name, service_json):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, project_id, service_name, service_json):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, project_id, service_name):
|
||||
raise NotImplementedError
|
22
cdn/manager/default/__init__.py
Normal file
22
cdn/manager/default/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
"""Default Manager Driver"""
|
||||
|
||||
from cdn.manager.default import driver
|
||||
|
||||
|
||||
# Hoist classes into package namespace
|
||||
Driver = driver.DefaultManagerDriver
|
19
cdn/manager/default/controllers.py
Normal file
19
cdn/manager/default/controllers.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
from cdn.manager.default import services
|
||||
|
||||
|
||||
Services = services.DefaultServicesController
|
30
cdn/manager/default/driver.py
Normal file
30
cdn/manager/default/driver.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
"""Default manager driver implementation."""
|
||||
|
||||
from cdn.common import decorators
|
||||
from cdn.manager import base
|
||||
from cdn.manager.default import controllers
|
||||
|
||||
|
||||
class DefaultManagerDriver(base.Driver):
|
||||
|
||||
def __init__(self, conf, storage, providers):
|
||||
super(DefaultManagerDriver, self).__init__(conf, storage, providers)
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
def services_controller(self):
|
||||
return controllers.Services(self)
|
59
cdn/manager/default/services.py
Normal file
59
cdn/manager/default/services.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
from cdn.manager import base
|
||||
|
||||
|
||||
class DefaultServicesController(base.ServicesController):
|
||||
def __init__(self, manager):
|
||||
super(DefaultServicesController, self).__init__(manager)
|
||||
|
||||
self.storage = self._driver.storage.service_controller
|
||||
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
return self.storage.list(project_id, marker, limit)
|
||||
|
||||
def get(self, project_id, service_name):
|
||||
return self.storage.get(project_id, service_name)
|
||||
|
||||
def create(self, project_id, service_name, service_json):
|
||||
self.storage.create(
|
||||
project_id,
|
||||
service_name,
|
||||
service_json)
|
||||
|
||||
return self._driver.providers.map(
|
||||
self.provider_wrapper.create,
|
||||
service_name,
|
||||
service_json)
|
||||
|
||||
def update(self, project_id, service_name, service_json):
|
||||
self.storage.update(
|
||||
project_id,
|
||||
service_name,
|
||||
service_json
|
||||
)
|
||||
|
||||
return self._driver.providers.map(
|
||||
self.provider_wrapper.update,
|
||||
service_name,
|
||||
service_json)
|
||||
|
||||
def delete(self, project_id, service_name):
|
||||
self.storage.delete(project_id, service_name)
|
||||
|
||||
return self._driver.providers.map(
|
||||
self.provider_wrapper.delete,
|
||||
service_name)
|
21
cdn/provider/base/__init__.py
Normal file
21
cdn/provider/base/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
from cdn.provider.base import driver
|
||||
from cdn.provider.base import services
|
||||
|
||||
|
||||
Driver = driver.ProviderDriverBase
|
||||
ServiceBase = services.ServicesControllerBase
|
30
cdn/provider/base/controller.py
Normal file
30
cdn/provider/base/controller.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ProviderControllerBase(object):
|
||||
|
||||
"""Top-level class for controllers.
|
||||
|
||||
:param driver: Instance of the driver
|
||||
instantiating this controller.
|
||||
"""
|
||||
|
||||
def __init__(self, driver):
|
||||
self._driver = driver
|
49
cdn/provider/base/driver.py
Normal file
49
cdn/provider/base/driver.py
Normal file
@ -0,0 +1,49 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ProviderDriverBase(object):
|
||||
"""Interface definition for storage drivers.
|
||||
|
||||
Data plane storage drivers are responsible for implementing the
|
||||
core functionality of the system.
|
||||
|
||||
Connection information and driver-specific options are
|
||||
loaded from the config file.
|
||||
|
||||
:param conf: Configuration containing options for this driver.
|
||||
:type conf: `oslo.config.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf):
|
||||
self._conf = conf
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_alive(self):
|
||||
"""Check whether the storage is ready."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractproperty
|
||||
def provider_name(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractproperty
|
||||
def service_controller(self):
|
||||
"""Returns the driver's hostname controller."""
|
||||
raise NotImplementedError
|
57
cdn/provider/base/responder.py
Normal file
57
cdn/provider/base/responder.py
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
class Responder(object):
|
||||
def __init__(self, provider_type):
|
||||
self.provider = provider_type
|
||||
|
||||
def failed(self, msg):
|
||||
ex_type, ex, tb = sys.exc_info()
|
||||
|
||||
print("error: {0} {1} {2} {3}".format(self.provider, msg, ex_type, ex))
|
||||
traceback.print_tb(tb)
|
||||
|
||||
return {
|
||||
self.provider: {
|
||||
"error": msg
|
||||
}
|
||||
}
|
||||
|
||||
def created(self, domain):
|
||||
return {
|
||||
self.provider: {
|
||||
"domain": domain
|
||||
}
|
||||
}
|
||||
|
||||
def updated(self, domain):
|
||||
return {
|
||||
self.provider: {
|
||||
"domain": domain
|
||||
}
|
||||
}
|
||||
|
||||
def deleted(self, domain):
|
||||
return {
|
||||
self.provider: {
|
||||
"domain": domain
|
||||
}
|
||||
}
|
41
cdn/provider/base/services.py
Normal file
41
cdn/provider/base/services.py
Normal file
@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from cdn.provider.base import controller
|
||||
from cdn.provider.base import responder
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ServicesControllerBase(controller.ProviderControllerBase):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(ServicesControllerBase, self).__init__(driver)
|
||||
|
||||
self.responder = responder.Responder(driver.provider_name)
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, service_name, service_json):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, service_name, service_json):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, service_name):
|
||||
raise NotImplementedError
|
@ -3,4 +3,4 @@
|
||||
from cdn.provider.fastly import driver
|
||||
|
||||
# Hoist classes into package namespace
|
||||
CDNProvider = driver.CDNProvider
|
||||
Driver = driver.CDNProvider
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
"""Fastly CDN Provider implementation."""
|
||||
|
||||
from cdn.common import decorators
|
||||
from cdn.openstack.common import log as logging
|
||||
from cdn.provider import base
|
||||
from cdn.provider.fastly import controllers
|
||||
@ -38,15 +37,19 @@ class CDNProvider(base.Driver):
|
||||
def __init__(self, conf):
|
||||
super(CDNProvider, self).__init__(conf)
|
||||
|
||||
self.conf.register_opts(FASTLY_OPTIONS,
|
||||
group=FASTLY_GROUP)
|
||||
self.fastly_conf = self.conf[FASTLY_GROUP]
|
||||
self._conf.register_opts(FASTLY_OPTIONS,
|
||||
group=FASTLY_GROUP)
|
||||
self.fastly_conf = self._conf[FASTLY_GROUP]
|
||||
|
||||
self.fastly_client = fastly.connect(self.fastly_conf.apikey)
|
||||
|
||||
def is_alive(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def provider_name(self):
|
||||
return "Fastly"
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
return self.fastly_client
|
||||
|
@ -31,7 +31,7 @@ class ServiceController(base.ServiceBase):
|
||||
self.current_customer = self.client.get_current_customer()
|
||||
|
||||
def update(self, service_name, service_json):
|
||||
print("update services")
|
||||
return self.responder.updated(service_name)
|
||||
|
||||
def create(self, service_name, service_json):
|
||||
|
||||
|
@ -3,4 +3,4 @@
|
||||
from cdn.provider.mock import driver
|
||||
|
||||
# Hoist classes into package namespace
|
||||
CDNProvider = driver.CDNProvider
|
||||
Driver = driver.CDNProvider
|
||||
|
@ -15,7 +15,6 @@
|
||||
|
||||
"""CDN Provider implementation."""
|
||||
|
||||
from cdn.common import decorators
|
||||
from cdn.openstack.common import log as logging
|
||||
from cdn.provider import base
|
||||
from cdn.provider.mock import controllers
|
||||
@ -31,6 +30,10 @@ class CDNProvider(base.Driver):
|
||||
def is_alive(self):
|
||||
return True
|
||||
|
||||
@decorators.lazy_property(write=False)
|
||||
@property
|
||||
def provider_name(self):
|
||||
return "Mock"
|
||||
|
||||
@property
|
||||
def service_controller(self):
|
||||
return controllers.ServiceController()
|
||||
return controllers.ServiceController(self)
|
||||
|
@ -18,8 +18,8 @@ from cdn.provider import base
|
||||
|
||||
class ServiceController(base.ServiceBase):
|
||||
|
||||
def __init__(self):
|
||||
super(ServiceController, self).__init__()
|
||||
def __init__(self, driver):
|
||||
super(ServiceController, self).__init__(driver)
|
||||
|
||||
def update(self, service_name, service_json):
|
||||
return self.responder.updated(service_name)
|
||||
|
@ -1,7 +0,0 @@
|
||||
"""CDN Storage Drivers"""
|
||||
|
||||
from cdn.storage import base
|
||||
|
||||
# Hoist classes into package namespace
|
||||
StorageDriverBase = base.StorageDriverBase
|
||||
ServicesBase = base.ServicesBase
|
@ -1,149 +0,0 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
_LIMITS_OPTIONS = [
|
||||
cfg.IntOpt('default_services_paging', default=10,
|
||||
help='Default services pagination size')
|
||||
]
|
||||
|
||||
_LIMITS_GROUP = 'limits:storage'
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class DriverBase(object):
|
||||
|
||||
"""Base class for both data and control plane drivers
|
||||
|
||||
:param conf: Configuration containing options for this driver.
|
||||
:type conf: `oslo.config.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf, providers):
|
||||
self.conf = conf
|
||||
self.providers = providers
|
||||
|
||||
def providers(self):
|
||||
return self.providers
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class StorageDriverBase(DriverBase):
|
||||
|
||||
"""Interface definition for storage drivers.
|
||||
|
||||
Data plane storage drivers are responsible for implementing the
|
||||
core functionality of the system.
|
||||
|
||||
Connection information and driver-specific options are
|
||||
loaded from the config file.
|
||||
|
||||
:param conf: Configuration containing options for this driver.
|
||||
:type conf: `oslo.config.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf, providers):
|
||||
super(StorageDriverBase, self).__init__(conf, providers)
|
||||
|
||||
self.conf.register_opts(_LIMITS_OPTIONS, group=_LIMITS_GROUP)
|
||||
self.limits_conf = self.conf[_LIMITS_GROUP]
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_alive(self):
|
||||
"""Check whether the storage is ready."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractproperty
|
||||
def service_controller(self):
|
||||
"""Returns the driver's hostname controller."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ControllerBase(object):
|
||||
|
||||
"""Top-level class for controllers.
|
||||
|
||||
:param driver: Instance of the driver
|
||||
instantiating this controller.
|
||||
"""
|
||||
|
||||
def __init__(self, driver):
|
||||
self.driver = driver
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ServicesBase(ControllerBase):
|
||||
|
||||
"""This class is responsible for managing Services
|
||||
"""
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, driver):
|
||||
super(ServicesBase, self).__init__(driver)
|
||||
|
||||
self.wrapper = ProviderWrapper()
|
||||
|
||||
@abc.abstractmethod
|
||||
def list(self, project_id):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, project_id, service_name, service_json):
|
||||
if (self.driver.providers is not None):
|
||||
return self.driver.providers.map(
|
||||
self.wrapper.create,
|
||||
service_name,
|
||||
service_json)
|
||||
else:
|
||||
return None
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, project_id, service_name, service_json):
|
||||
if (self.driver.providers is not None):
|
||||
return self.driver.providers.map(
|
||||
self.wrapper.update,
|
||||
service_name,
|
||||
service_json)
|
||||
else:
|
||||
return None
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, project_id, service_name):
|
||||
if (self.driver.providers is not None):
|
||||
return self.driver.providers.map(
|
||||
self.wrapper.delete,
|
||||
service_name)
|
||||
else:
|
||||
return None
|
||||
|
||||
@abc.abstractmethod
|
||||
def get(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ProviderWrapper(object):
|
||||
|
||||
def create(self, ext, service_name, service_json):
|
||||
return ext.obj.service_controller.create(service_name, service_json)
|
||||
|
||||
def update(self, ext, service_name):
|
||||
return ext.obj.service_controller.update(service_name)
|
||||
|
||||
def delete(self, ext, service_name):
|
||||
return ext.obj.service_controller.delete(service_name)
|
22
cdn/storage/base/__init__.py
Normal file
22
cdn/storage/base/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
from cdn.storage.base import driver
|
||||
from cdn.storage.base import services
|
||||
|
||||
|
||||
Driver = driver.StorageDriverBase
|
||||
|
||||
ServicesController = services.ServicesControllerBase
|
30
cdn/storage/base/controller.py
Normal file
30
cdn/storage/base/controller.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class StorageControllerBase(object):
|
||||
|
||||
"""Top-level class for controllers.
|
||||
|
||||
:param driver: Instance of the driver
|
||||
instantiating this controller.
|
||||
"""
|
||||
|
||||
def __init__(self, driver):
|
||||
self._driver = driver
|
57
cdn/storage/base/driver.py
Normal file
57
cdn/storage/base/driver.py
Normal file
@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
_LIMITS_OPTIONS = [
|
||||
cfg.IntOpt('default_services_paging', default=10,
|
||||
help='Default services pagination size')
|
||||
]
|
||||
|
||||
_LIMITS_GROUP = 'limits:storage'
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class StorageDriverBase(object):
|
||||
"""Interface definition for storage drivers.
|
||||
|
||||
Data plane storage drivers are responsible for implementing the
|
||||
core functionality of the system.
|
||||
|
||||
Connection information and driver-specific options are
|
||||
loaded from the config file.
|
||||
|
||||
:param conf: Configuration containing options for this driver.
|
||||
:type conf: `oslo.config.ConfigOpts`
|
||||
"""
|
||||
|
||||
def __init__(self, conf):
|
||||
self._conf = conf
|
||||
self._conf.register_opts(_LIMITS_OPTIONS, group=_LIMITS_GROUP)
|
||||
self.limits_conf = self._conf[_LIMITS_GROUP]
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_alive(self):
|
||||
"""Check whether the storage is ready."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractproperty
|
||||
def service_controller(self):
|
||||
"""Returns the driver's hostname controller."""
|
||||
raise NotImplementedError
|
46
cdn/storage/base/services.py
Normal file
46
cdn/storage/base/services.py
Normal file
@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import abc
|
||||
import six
|
||||
|
||||
from cdn.storage.base import controller
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class ServicesControllerBase(controller.StorageControllerBase):
|
||||
|
||||
def __init__(self, driver):
|
||||
super(ServicesControllerBase, self).__init__(driver)
|
||||
|
||||
@abc.abstractmethod
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def create(self, project_id, service_name, service_json):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def update(self, project_id, service_name, service_json):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete(self, project_id, service_name):
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def get(self):
|
||||
raise NotImplementedError
|
@ -3,4 +3,4 @@
|
||||
from cdn.storage.cassandra import driver
|
||||
|
||||
# Hoist classes into package namespace
|
||||
StorageDriver = driver.StorageDriver
|
||||
Driver = driver.CassandraStorageDriver
|
||||
|
@ -19,7 +19,7 @@ from cassandra.cluster import Cluster
|
||||
|
||||
from cdn.common import decorators
|
||||
from cdn.openstack.common import log as logging
|
||||
from cdn import storage
|
||||
from cdn.storage import base
|
||||
from cdn.storage.cassandra import controllers
|
||||
|
||||
from oslo.config import cfg
|
||||
@ -42,14 +42,14 @@ def _connection(conf):
|
||||
return session
|
||||
|
||||
|
||||
class StorageDriver(storage.StorageDriverBase):
|
||||
class CassandraStorageDriver(base.Driver):
|
||||
|
||||
def __init__(self, conf, providers):
|
||||
super(StorageDriver, self).__init__(conf, providers)
|
||||
def __init__(self, conf):
|
||||
super(CassandraStorageDriver, self).__init__(conf)
|
||||
|
||||
self.conf.register_opts(CASSANDRA_OPTIONS,
|
||||
group=CASSANDRA_GROUP)
|
||||
self.cassandra_conf = self.conf[CASSANDRA_GROUP]
|
||||
self._conf.register_opts(CASSANDRA_OPTIONS,
|
||||
group=CASSANDRA_GROUP)
|
||||
self.cassandra_conf = self._conf[CASSANDRA_GROUP]
|
||||
|
||||
def is_alive(self):
|
||||
return True
|
||||
|
@ -84,13 +84,13 @@ CQL_UPDATE_RESTRICTIONS = '''
|
||||
'''
|
||||
|
||||
|
||||
class ServicesController(base.ServicesBase):
|
||||
class ServicesController(base.ServicesController):
|
||||
|
||||
@property
|
||||
def session(self):
|
||||
return self.driver.service_database
|
||||
return self._driver.service_database
|
||||
|
||||
def list(self, project_id):
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
|
||||
# get all services
|
||||
args = {
|
||||
@ -140,23 +140,13 @@ class ServicesController(base.ServicesBase):
|
||||
|
||||
self.session.execute(CQL_CREATE_SERVICE, args)
|
||||
|
||||
# create at providers
|
||||
providers = super(ServicesController, self).create(
|
||||
project_id, service_name, service)
|
||||
|
||||
return providers
|
||||
|
||||
def update(self, project_id, service_name, service_json):
|
||||
# update configuration in storage
|
||||
|
||||
# determine what changed.
|
||||
|
||||
# update those columns provided only.
|
||||
|
||||
# update at providers
|
||||
return super(ServicesController, self).update(project_id,
|
||||
service_name,
|
||||
service_json)
|
||||
pass
|
||||
|
||||
def delete(self, project_id, service_name):
|
||||
# delete local configuration from storage
|
||||
@ -165,6 +155,3 @@ class ServicesController(base.ServicesBase):
|
||||
'service_name': service_name
|
||||
}
|
||||
self.session.execute(CQL_DELETE_SERVICE, args)
|
||||
|
||||
# delete from providers
|
||||
return super(ServicesController, self).delete(project_id, service_name)
|
||||
|
@ -3,4 +3,4 @@
|
||||
from cdn.storage.mockdb import driver
|
||||
|
||||
# Hoist classes into package namespace
|
||||
StorageDriver = driver.StorageDriver
|
||||
Driver = driver.MockDBStorageDriver
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
from cdn.common import decorators
|
||||
from cdn.openstack.common import log as logging
|
||||
from cdn import storage
|
||||
from cdn.storage import base
|
||||
from cdn.storage.mockdb import controllers
|
||||
|
||||
from oslo.config import cfg
|
||||
@ -36,14 +36,14 @@ def _connection():
|
||||
return None
|
||||
|
||||
|
||||
class StorageDriver(storage.StorageDriverBase):
|
||||
class MockDBStorageDriver(base.Driver):
|
||||
|
||||
def __init__(self, conf, providers):
|
||||
super(StorageDriver, self).__init__(conf, providers)
|
||||
def __init__(self, conf):
|
||||
super(MockDBStorageDriver, self).__init__(conf)
|
||||
|
||||
self.conf.register_opts(MOCKDB_OPTIONS,
|
||||
group=MOCKDB_GROUP)
|
||||
self.mockdb_conf = self.conf[MOCKDB_GROUP]
|
||||
self._conf.register_opts(MOCKDB_OPTIONS,
|
||||
group=MOCKDB_GROUP)
|
||||
self.mockdb_conf = self._conf[MOCKDB_GROUP]
|
||||
|
||||
def is_alive(self):
|
||||
return True
|
||||
|
@ -16,14 +16,13 @@
|
||||
from cdn.storage import base
|
||||
|
||||
|
||||
class ServicesController(base.ServicesBase):
|
||||
class ServicesController(base.ServicesController):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ServicesController, self).__init__(*args, **kwargs)
|
||||
@property
|
||||
def session(self):
|
||||
return self._driver.service_database
|
||||
|
||||
self._session = self.driver.service_database
|
||||
|
||||
def list(self, project_id):
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
services = {
|
||||
"links": [
|
||||
{
|
||||
@ -88,25 +87,17 @@ class ServicesController(base.ServicesBase):
|
||||
|
||||
def get(self, project_id):
|
||||
# get the requested service from storage
|
||||
print "get service"
|
||||
return ""
|
||||
|
||||
def create(self, project_id, service_name, service_json):
|
||||
|
||||
# create at providers
|
||||
providers = super(ServicesController, self).create(
|
||||
project_id, service_name, service_json)
|
||||
|
||||
return providers
|
||||
return ""
|
||||
|
||||
def update(self, project_id, service_name, service_json):
|
||||
# update configuration in storage
|
||||
|
||||
# update at providers
|
||||
return super(ServicesController, self).update(project_id,
|
||||
service_name,
|
||||
service_json)
|
||||
return ""
|
||||
|
||||
def delete(self, project_id, service_name):
|
||||
|
||||
# delete from providers
|
||||
return super(ServicesController, self).delete(project_id, service_name)
|
||||
return ""
|
||||
|
@ -3,4 +3,4 @@
|
||||
from cdn.storage.mongodb import driver
|
||||
|
||||
# Hoist classes into package namespace
|
||||
StorageDriver = driver.StorageDriver
|
||||
Driver = driver.MongoDBStorageDriver
|
||||
|
@ -20,7 +20,7 @@ import pymongo.errors
|
||||
|
||||
from cdn.common import decorators
|
||||
from cdn.openstack.common import log as logging
|
||||
from cdn import storage
|
||||
from cdn.storage import base
|
||||
from cdn.storage.mongodb import controllers
|
||||
|
||||
from oslo.config import cfg
|
||||
@ -63,14 +63,14 @@ def _connection(conf):
|
||||
return MongoClient(conf.uri)
|
||||
|
||||
|
||||
class StorageDriver(storage.StorageDriverBase):
|
||||
class MongoDBStorageDriver(base.Driver):
|
||||
|
||||
def __init__(self, conf, providers):
|
||||
super(StorageDriver, self).__init__(conf, providers)
|
||||
def __init__(self, conf):
|
||||
super(MongoDBStorageDriver, self).__init__(conf)
|
||||
|
||||
self.conf.register_opts(MONGODB_OPTIONS,
|
||||
group=MONGODB_GROUP)
|
||||
self.mongodb_conf = self.conf[MONGODB_GROUP]
|
||||
self._conf.register_opts(MONGODB_OPTIONS,
|
||||
group=MONGODB_GROUP)
|
||||
self.mongodb_conf = self._conf[MONGODB_GROUP]
|
||||
|
||||
def is_alive(self):
|
||||
try:
|
||||
|
@ -17,95 +17,28 @@
|
||||
from cdn.storage import base
|
||||
|
||||
|
||||
class ServicesController(base.ServicesBase):
|
||||
|
||||
def list(self, project_id):
|
||||
services = {
|
||||
"links": [
|
||||
{
|
||||
"rel": "next",
|
||||
"href": "/v1.0/services?marker=www.test.com&limit=20"
|
||||
}
|
||||
],
|
||||
"services": [
|
||||
{
|
||||
"domains": [
|
||||
{
|
||||
"domain": "www.mywebsite.com"
|
||||
}
|
||||
],
|
||||
"origins": [
|
||||
{
|
||||
"origin": "mywebsite.com",
|
||||
"port": 80,
|
||||
"ssl": False
|
||||
}
|
||||
],
|
||||
"caching": [
|
||||
{"name": "default", "ttl": 3600},
|
||||
{
|
||||
"name": "home",
|
||||
"ttl": 17200,
|
||||
"rules": [
|
||||
{"name": "index",
|
||||
"request_url": "/index.htm"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "images",
|
||||
"ttl": 12800,
|
||||
"rules": [
|
||||
{"name": "images", "request_url": "*.png"}
|
||||
]
|
||||
}
|
||||
],
|
||||
"restrictions": [
|
||||
{
|
||||
"name": "website only",
|
||||
"rules": [
|
||||
{
|
||||
"name": "mywebsite.com",
|
||||
"http_host": "www.mywebsite.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"href": "/v1.0/services/mywebsite",
|
||||
"rel": "self"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
class ServicesController(base.ServicesController):
|
||||
@property
|
||||
def session(self):
|
||||
return self._driver.service_database
|
||||
|
||||
def list(self, project_id, marker=None, limit=None):
|
||||
services = {}
|
||||
return services
|
||||
|
||||
def get(self, project_id):
|
||||
def get(self, project_id, service_name):
|
||||
# get the requested service from storage
|
||||
print "get service"
|
||||
pass
|
||||
|
||||
def create(self, project_id, service_name, service_json):
|
||||
|
||||
# create the service in storage
|
||||
service = service_json
|
||||
|
||||
# create at providers
|
||||
return super(ServicesController, self).create(project_id,
|
||||
service_name,
|
||||
service)
|
||||
pass
|
||||
|
||||
def update(self, project_id, service_name, service_json):
|
||||
# update configuration in storage
|
||||
|
||||
# update at providers
|
||||
return super(ServicesController, self).update(project_id,
|
||||
service_name,
|
||||
service_json)
|
||||
pass
|
||||
|
||||
def delete(self, project_id, service_name):
|
||||
# delete local configuration from storage
|
||||
|
||||
# delete from providers
|
||||
return super(ServicesController, self).delete(project_id, service_name)
|
||||
pass
|
||||
|
@ -3,5 +3,6 @@
|
||||
|
||||
from cdn.transport import base
|
||||
|
||||
|
||||
# Hoist into package namespace
|
||||
DriverBase = base.DriverBase
|
||||
Driver = base.TransportDriverBase
|
||||
|
@ -18,16 +18,26 @@ import six
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class DriverBase(object):
|
||||
class TransportDriverBase(object):
|
||||
"""Base class for Transport Drivers to document the expected interface.
|
||||
|
||||
:param conf: configuration instance
|
||||
:type conf: oslo.config.cfg.CONF
|
||||
"""
|
||||
|
||||
def __init__(self, conf, storage):
|
||||
def __init__(self, conf, manager):
|
||||
self._conf = conf
|
||||
self._storage = storage
|
||||
self._manager = manager
|
||||
|
||||
self._app = None
|
||||
|
||||
@property
|
||||
def app(self):
|
||||
return self._app
|
||||
|
||||
@property
|
||||
def manager(self):
|
||||
return self._manager
|
||||
|
||||
@abc.abstractmethod
|
||||
def listen():
|
||||
|
@ -3,4 +3,4 @@
|
||||
from cdn.transport.falcon import driver
|
||||
|
||||
# Hoist into package namespace
|
||||
TransportDriver = driver.TransportDriver
|
||||
Driver = driver.TransportDriver
|
||||
|
@ -42,37 +42,36 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class TransportDriver(transport.DriverBase):
|
||||
class TransportDriver(transport.Driver):
|
||||
|
||||
def __init__(self, conf, storage):
|
||||
super(TransportDriver, self).__init__(conf, storage)
|
||||
def __init__(self, conf, manager):
|
||||
super(TransportDriver, self).__init__(conf, manager)
|
||||
|
||||
self._conf.register_opts(_WSGI_OPTIONS, group=_WSGI_GROUP)
|
||||
self._wsgi_conf = self._conf[_WSGI_GROUP]
|
||||
|
||||
self.app = None
|
||||
self._init_routes()
|
||||
self._setup_app()
|
||||
|
||||
def _init_routes(self):
|
||||
def _setup_app(self):
|
||||
"""Initialize hooks and URI routes to resources."""
|
||||
self.app = falcon.API()
|
||||
self._app = falcon.API()
|
||||
version_path = "/v1.0"
|
||||
project_id = "/{project_id}"
|
||||
|
||||
prefix = version_path + project_id
|
||||
|
||||
# init the controllers
|
||||
service_controller = self._storage.service_controller
|
||||
service_controller = self.manager.services_controller
|
||||
|
||||
# setup the routes
|
||||
self.app.add_route(prefix,
|
||||
v1.V1Resource())
|
||||
self._app.add_route(prefix,
|
||||
v1.V1Resource())
|
||||
|
||||
self.app.add_route(prefix + '/services',
|
||||
services.ServicesResource(service_controller))
|
||||
self._app.add_route(prefix + '/services',
|
||||
services.ServicesResource(service_controller))
|
||||
|
||||
self.app.add_route(prefix + '/services/{service_name}',
|
||||
services.ServiceResource(service_controller))
|
||||
self._app.add_route(prefix + '/services/{service_name}',
|
||||
services.ServiceResource(service_controller))
|
||||
|
||||
def listen(self):
|
||||
"""Self-host using 'bind' and 'port' from the WSGI config group."""
|
||||
|
@ -29,6 +29,9 @@ log_file = cdn.log
|
||||
# Transport driver module (e.g., falcon, pecan)
|
||||
transport = falcon
|
||||
|
||||
# Manager driver module (e.g. default)
|
||||
manager = default
|
||||
|
||||
# Storage driver module (e.g., mongodb, sqlite, cassandra)
|
||||
storage = mockdb
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
-e git+https://github.com/zebrafishlabs/fastly-python@b98a756b2a03687d76c3cfa0e024a445af85b38d#egg=fastly-python
|
||||
httplib2>=0.8
|
||||
cdn-fastly==1.0.4
|
||||
httplib2>=0.8
|
||||
|
@ -1,4 +1,5 @@
|
||||
-r common.txt
|
||||
-r storage/cassandra.txt
|
||||
-r transport/falcon.txt
|
||||
-r transport/pecan.txt
|
||||
-r provider/fastly.txt
|
@ -0,0 +1 @@
|
||||
pecan==0.6.1
|
19
setup.cfg
19
setup.cfg
@ -26,17 +26,23 @@ packages =
|
||||
console_scripts =
|
||||
cdn-server = cdn.cmd.server:run
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
|
||||
cdn.transport =
|
||||
falcon = cdn.transport.falcon.driver:TransportDriver
|
||||
falcon = cdn.transport.falcon:Driver
|
||||
|
||||
cdn.manager =
|
||||
default = cdn.manager.default:Driver
|
||||
|
||||
cdn.storage =
|
||||
mongodb = cdn.storage.mongodb.driver:StorageDriver
|
||||
cassandra = cdn.storage.cassandra.driver:StorageDriver
|
||||
mockdb = cdn.storage.mockdb.driver:StorageDriver
|
||||
mongodb = cdn.storage.mongodb:Driver
|
||||
cassandra = cdn.storage.cassandra:Driver
|
||||
mockdb = cdn.storage.mockdb:Driver
|
||||
|
||||
cdn.provider =
|
||||
fastly = cdn.provider.fastly.driver:CDNProvider
|
||||
mock = cdn.provider.mock.driver:CDNProvider
|
||||
fastly = cdn.provider.fastly:Driver
|
||||
mock = cdn.provider.mock:Driver
|
||||
|
||||
[nosetests]
|
||||
where=tests
|
||||
@ -57,4 +63,3 @@ cover-inclusive = true
|
||||
; method in nose/inspector.py requires a traceback-like object.
|
||||
;
|
||||
; detailed-errors = 1
|
||||
|
||||
|
18
setup.py
18
setup.py
@ -15,8 +15,26 @@
|
||||
# limitations under the License.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import os
|
||||
import setuptools
|
||||
from pip.req import parse_requirements
|
||||
|
||||
requirement_files = []
|
||||
# walk the requirements directory and gather requirement files
|
||||
for root, dirs, files in os.walk('requirements'):
|
||||
for requirements_file in files:
|
||||
requirements_file_path = os.path.join(root, requirements_file)
|
||||
# parse_requirements() returns generator of pip.req.InstallRequirement objects
|
||||
requirement_files.append(parse_requirements(requirements_file_path))
|
||||
|
||||
# parse all requirement files and generate requirements
|
||||
requirements = set()
|
||||
for requirement_file in requirement_files:
|
||||
requirements.update([str(requirement.req) for requirement in requirement_file])
|
||||
# convert requirements in to list
|
||||
requirements = list(requirements)
|
||||
|
||||
setuptools.setup(
|
||||
install_requires = requirements,
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
||||
|
@ -1,16 +0,0 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
# Server Specific Configurations
|
0
tests/unit/manager/__init__.py
Normal file
0
tests/unit/manager/__init__.py
Normal file
0
tests/unit/manager/default/__init__.py
Normal file
0
tests/unit/manager/default/__init__.py
Normal file
37
tests/unit/manager/default/test_driver.py
Normal file
37
tests/unit/manager/default/test_driver.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cdn.manager.default import driver
|
||||
from cdn.manager.default import services
|
||||
|
||||
|
||||
class DefaultManagerDriverTests(unittest.TestCase):
|
||||
@mock.patch('cdn.storage.base.driver.StorageDriverBase')
|
||||
@mock.patch('cdn.provider.base.driver.ProviderDriverBase')
|
||||
def setUp(self, mock_storage, mock_provider):
|
||||
conf = cfg.ConfigOpts()
|
||||
self.driver = driver.DefaultManagerDriver(conf,
|
||||
mock_storage,
|
||||
mock_provider)
|
||||
|
||||
def test_services_controller(self):
|
||||
sc = self.driver.services_controller
|
||||
|
||||
self.assertIsInstance(sc, services.DefaultServicesController)
|
84
tests/unit/manager/default/test_services.py
Normal file
84
tests/unit/manager/default/test_services.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2014 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.
|
||||
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cdn.manager.default import driver
|
||||
from cdn.manager.default import services
|
||||
|
||||
|
||||
class DefaultManagerServiceTests(unittest.TestCase):
|
||||
@mock.patch('cdn.storage.base.driver.StorageDriverBase')
|
||||
@mock.patch('cdn.provider.base.driver.ProviderDriverBase')
|
||||
def setUp(self, mock_driver, mock_provider):
|
||||
# create mocked config and driver
|
||||
conf = cfg.ConfigOpts()
|
||||
manager_driver = driver.DefaultManagerDriver(conf,
|
||||
mock_driver,
|
||||
mock_provider)
|
||||
|
||||
# stubbed driver
|
||||
self.sc = services.DefaultServicesController(manager_driver)
|
||||
|
||||
def test_create(self):
|
||||
project_id = 'mock_id'
|
||||
service_name = 'mock_service'
|
||||
service_json = ''
|
||||
|
||||
self.sc.create(project_id, service_name, service_json)
|
||||
|
||||
# ensure the manager calls the storage driver with the appropriate data
|
||||
self.sc.storage.create.assert_called_once_with(project_id,
|
||||
service_name,
|
||||
service_json)
|
||||
# and that the providers are notified.
|
||||
providers = self.sc._driver.providers
|
||||
providers.map.assert_called_once_with(self.sc.provider_wrapper.create,
|
||||
service_name,
|
||||
service_json)
|
||||
|
||||
def test_update(self):
|
||||
project_id = 'mock_id'
|
||||
service_name = 'mock_service'
|
||||
service_json = ''
|
||||
|
||||
self.sc.update(project_id, service_name, service_json)
|
||||
|
||||
# ensure the manager calls the storage driver with the appropriate data
|
||||
self.sc.storage.update.assert_called_once_with(project_id,
|
||||
service_name,
|
||||
service_json)
|
||||
# and that the providers are notified.
|
||||
providers = self.sc._driver.providers
|
||||
providers.map.assert_called_once_with(self.sc.provider_wrapper.update,
|
||||
service_name,
|
||||
service_json)
|
||||
|
||||
def test_delete(self):
|
||||
project_id = 'mock_id'
|
||||
service_name = 'mock_service'
|
||||
|
||||
self.sc.delete(project_id, service_name)
|
||||
|
||||
# ensure the manager calls the storage driver with the appropriate data
|
||||
self.sc.storage.delete.assert_called_once_with(project_id,
|
||||
service_name)
|
||||
# and that the providers are notified.
|
||||
providers = self.sc._driver.providers
|
||||
providers.map.assert_called_once_with(self.sc.provider_wrapper.delete,
|
||||
service_name)
|
@ -37,7 +37,7 @@ class TestDriver(unittest.TestCase):
|
||||
def test_init(self, mock_connect):
|
||||
provider = driver.CDNProvider(self.conf)
|
||||
mock_connect.assert_called_once_with(
|
||||
provider.conf['drivers:provider:fastly'].apikey)
|
||||
provider._conf['drivers:provider:fastly'].apikey)
|
||||
|
||||
@mock.patch.object(driver, 'FASTLY_OPTIONS', new=FASTLY_OPTIONS)
|
||||
def test_is_alive(self):
|
||||
|
@ -30,99 +30,108 @@ class TestServices(unittest.TestCase):
|
||||
@mock.patch('fastly.FastlyService')
|
||||
@mock.patch('fastly.FastlyVersion')
|
||||
@mock.patch('cdn.provider.fastly.services.ServiceController.client')
|
||||
def test_create(self, service_json, MockConnection,
|
||||
MockService, MockVersion, mock_controllerclient):
|
||||
service_name = 'scarborough'
|
||||
mock_service_id = '%020x' % random.randrange(16**20)
|
||||
mockCreateVersionResp = {
|
||||
u'service_id': mock_service_id, u'number': 5}
|
||||
|
||||
service = MockService()
|
||||
service.id = mock_service_id
|
||||
version = MockVersion(MockConnection, mockCreateVersionResp)
|
||||
MockConnection.create_service.return_value = service
|
||||
MockConnection.create_version.return_value = version
|
||||
@mock.patch('cdn.provider.fastly.driver.CDNProvider')
|
||||
def test_create(self, service_json, mock_connection,
|
||||
mock_service, mock_version, mock_controllerclient,
|
||||
mock_driver):
|
||||
driver = mock_driver()
|
||||
driver.provider_name = 'Fastly'
|
||||
|
||||
# instantiate
|
||||
controller = services.ServiceController(None)
|
||||
controller = services.ServiceController(driver)
|
||||
|
||||
service_name = 'scarborough'
|
||||
mock_service_id = '%020x' % random.randrange(16 ** 20)
|
||||
mock_create_version_resp = {
|
||||
u'service_id': mock_service_id, u'number': 5}
|
||||
|
||||
service = mock_service()
|
||||
service.id = mock_service_id
|
||||
version = mock_version(controller.client, mock_create_version_resp)
|
||||
controller.client.create_service.return_value = service
|
||||
controller.client.create_version.return_value = version
|
||||
|
||||
# ASSERTIONS
|
||||
# create_service
|
||||
MockConnection.create_service.side_effect = fastly.FastlyError(
|
||||
controller.client.create_service.side_effect = fastly.FastlyError(
|
||||
Exception('Creating service failed.'))
|
||||
resp = controller.create(service_name, service_json)
|
||||
self.assertIn('error', resp['fastly'])
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
MockConnection.reset_mock()
|
||||
MockConnection.create_service.side_effect = None
|
||||
controller.client.reset_mock()
|
||||
controller.client.create_service.side_effect = None
|
||||
|
||||
# create_version
|
||||
MockConnection.create_version.side_effect = fastly.FastlyError(
|
||||
controller.client.create_version.side_effect = fastly.FastlyError(
|
||||
Exception('Creating version failed.'))
|
||||
resp = controller.create(service_name, service_json)
|
||||
self.assertIn('error', resp['fastly'])
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
MockConnection.reset_mock()
|
||||
MockConnection.create_version.side_effect = None
|
||||
controller.client.reset_mock()
|
||||
controller.client.create_version.side_effect = None
|
||||
|
||||
# create domains
|
||||
MockConnection.create_domain.side_effect = fastly.FastlyError(
|
||||
controller.client.create_domain.side_effect = fastly.FastlyError(
|
||||
Exception('Creating domains failed.'))
|
||||
resp = controller.create(service_name, service_json)
|
||||
self.assertIn('error', resp['fastly'])
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
MockConnection.reset_mock()
|
||||
MockConnection.create_domain.side_effect = None
|
||||
controller.client.reset_mock()
|
||||
controller.client.create_domain.side_effect = None
|
||||
|
||||
# create backends
|
||||
MockConnection.create_backend.side_effect = fastly.FastlyError(
|
||||
controller.client.create_backend.side_effect = fastly.FastlyError(
|
||||
Exception('Creating backend failed.'))
|
||||
resp = controller.create(service_name, service_json)
|
||||
self.assertIn('error', resp['fastly'])
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
MockConnection.reset_mock()
|
||||
MockConnection.create_backend.side_effect = None
|
||||
controller.client.reset_mock()
|
||||
controller.client.create_backend.side_effect = None
|
||||
|
||||
# test a general exception
|
||||
MockConnection.create_service.side_effect = Exception(
|
||||
controller.client.create_service.side_effect = Exception(
|
||||
'Wild exception occurred.')
|
||||
resp = controller.create(service_name, service_json)
|
||||
self.assertIn('error', resp['fastly'])
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
# finally, a clear run
|
||||
MockConnection.reset_mock()
|
||||
MockConnection.create_service.side_effect = None
|
||||
controller.client.reset_mock()
|
||||
controller.client.create_service.side_effect = None
|
||||
|
||||
resp = controller.create(service_name, service_json)
|
||||
|
||||
MockConnection.create_service.assert_called_once_with(
|
||||
controller.client.create_service.assert_called_once_with(
|
||||
controller.current_customer.id, service_name)
|
||||
|
||||
MockConnection.create_version.assert_called_once_with(service.id)
|
||||
controller.client.create_version.assert_called_once_with(service.id)
|
||||
|
||||
MockConnection.create_domain.assert_any_call(
|
||||
controller.client.create_domain.assert_any_call(
|
||||
service.id, version.number, service_json['domains'][0]['domain'])
|
||||
|
||||
MockConnection.create_domain.assert_any_call(
|
||||
controller.client.create_domain.assert_any_call(
|
||||
service.id, version.number, service_json['domains'][1]['domain'])
|
||||
|
||||
MockConnection.create_backend.assert_has_any_call(
|
||||
controller.client.create_backend.assert_has_any_call(
|
||||
service.id, 1,
|
||||
service_json['origins'][0]['origin'],
|
||||
service_json['origins'][0]['origin'],
|
||||
service_json['origins'][0]['ssl'],
|
||||
service_json['origins'][0]['port'])
|
||||
|
||||
self.assertIn('domain', resp['fastly'])
|
||||
self.assertIn('domain', resp[driver.provider_name])
|
||||
|
||||
@mock.patch('fastly.FastlyConnection')
|
||||
@mock.patch('fastly.FastlyService')
|
||||
@mock.patch('cdn.provider.fastly.services.ServiceController.client')
|
||||
def test_delete(self, mock_connection, mock_service, mock_get_client):
|
||||
@mock.patch('cdn.provider.fastly.driver.CDNProvider')
|
||||
def test_delete(self, mock_connection, mock_service, mock_client,
|
||||
mock_driver):
|
||||
driver = mock_driver()
|
||||
driver.provider_name = 'Fastly'
|
||||
service_name = 'whatsitnamed'
|
||||
|
||||
# instantiate
|
||||
controller = services.ServiceController(None)
|
||||
controller = services.ServiceController(driver)
|
||||
|
||||
# mock.patch return values
|
||||
service = mock_service()
|
||||
@ -131,28 +140,34 @@ class TestServices(unittest.TestCase):
|
||||
|
||||
# test exception
|
||||
exception = fastly.FastlyError(Exception('ding'))
|
||||
mock_connection.delete_service.side_effect = exception
|
||||
controller.client.delete_service.side_effect = exception
|
||||
resp = controller.delete('wrongname')
|
||||
self.assertIn('error', resp['fastly'])
|
||||
|
||||
self.assertIn('error', resp[driver.provider_name])
|
||||
|
||||
# clear run
|
||||
mock_connection.reset_mock()
|
||||
mock_connection.delete_service.side_effect = None
|
||||
controller.client.reset_mock()
|
||||
controller.client.delete_service.side_effect = None
|
||||
|
||||
resp = controller.delete(service_name)
|
||||
mock_connection.get_service_by_name.assert_called_once_with(
|
||||
controller.client.get_service_by_name.assert_called_once_with(
|
||||
service_name)
|
||||
mock_connection.delete_service.assert_called_once_with(service.id)
|
||||
self.assertIn('domain', resp['fastly'])
|
||||
controller.client.delete_service.assert_called_once_with(service.id)
|
||||
self.assertIn('domain', resp[driver.provider_name])
|
||||
|
||||
@mock.patch('cdn.provider.fastly.services.ServiceController.client')
|
||||
def test_update(self, mock_get_client):
|
||||
controller = services.ServiceController(None)
|
||||
resp = controller.update(None, None)
|
||||
self.assertEqual(resp, None)
|
||||
@mock.patch('cdn.provider.fastly.driver.CDNProvider')
|
||||
@ddt.file_data('data_service.json')
|
||||
def test_update(self, mock_get_client, mock_driver, service_json):
|
||||
service_name = 'whatsitnamed'
|
||||
|
||||
driver = mock_driver()
|
||||
controller = services.ServiceController(driver)
|
||||
resp = controller.update(service_name, service_json)
|
||||
self.assertIn('domain', resp[driver.provider_name])
|
||||
|
||||
@mock.patch('cdn.provider.fastly.driver.CDNProvider')
|
||||
def test_client(self, MockDriver):
|
||||
driver = MockDriver()
|
||||
def test_client(self, mock_driver):
|
||||
driver = mock_driver()
|
||||
controller = services.ServiceController(driver)
|
||||
self.assertNotEquals(controller.client(), None)
|
||||
|
@ -14,12 +14,14 @@
|
||||
# limitations under the License.
|
||||
|
||||
import cassandra
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cdn.storage.cassandra import driver
|
||||
from cdn.storage.cassandra import services
|
||||
from mock import patch
|
||||
from oslo.config import cfg
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
CASSANDRA_OPTIONS = [
|
||||
cfg.ListOpt('cluster', default='mock_ip',
|
||||
@ -29,11 +31,11 @@ CASSANDRA_OPTIONS = [
|
||||
]
|
||||
|
||||
|
||||
class CassandraStorageServiceTests(TestCase):
|
||||
@patch.object(driver, 'CASSANDRA_OPTIONS', new=CASSANDRA_OPTIONS)
|
||||
class CassandraStorageServiceTests(unittest.TestCase):
|
||||
@mock.patch.object(driver, 'CASSANDRA_OPTIONS', new=CASSANDRA_OPTIONS)
|
||||
def setUp(self):
|
||||
conf = cfg.ConfigOpts()
|
||||
self.cassandra_driver = driver.StorageDriver(conf, None)
|
||||
self.cassandra_driver = driver.CassandraStorageDriver(conf)
|
||||
|
||||
def test_storage_driver(self):
|
||||
# assert that the configs are set up based on what was passed in
|
||||
@ -45,7 +47,7 @@ class CassandraStorageServiceTests(TestCase):
|
||||
def test_is_alive(self):
|
||||
self.assertEquals(self.cassandra_driver.is_alive(), True)
|
||||
|
||||
@patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
@mock.patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
def test_connection(self, mock_cluster):
|
||||
self.cassandra_driver.connection()
|
||||
mock_cluster.assert_called_with('mock_cdn')
|
||||
@ -57,11 +59,7 @@ class CassandraStorageServiceTests(TestCase):
|
||||
isinstance(sc, services.ServicesController),
|
||||
True)
|
||||
|
||||
@patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
@mock.patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
def test_service_database(self, mock_cluster):
|
||||
self.cassandra_driver.service_database
|
||||
mock_cluster.assert_called_with('mock_cdn')
|
||||
|
||||
def test_providers(self):
|
||||
providers = self.cassandra_driver.providers
|
||||
self.assertEquals(providers, None)
|
||||
|
@ -14,19 +14,18 @@
|
||||
# limitations under the License.
|
||||
|
||||
import cassandra
|
||||
import ddt
|
||||
import mock
|
||||
import unittest
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from cdn.storage.cassandra import driver
|
||||
from cdn.storage.cassandra import services
|
||||
|
||||
from ddt import ddt, file_data
|
||||
from mock import patch
|
||||
from oslo.config import cfg
|
||||
from unittest import TestCase
|
||||
|
||||
|
||||
@ddt
|
||||
class CassandraStorageServiceTests(TestCase):
|
||||
|
||||
@ddt.ddt
|
||||
class CassandraStorageServiceTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
# mock arguments to use
|
||||
self.project_id = '123456'
|
||||
@ -34,14 +33,14 @@ class CassandraStorageServiceTests(TestCase):
|
||||
|
||||
# create mocked config and driver
|
||||
conf = cfg.ConfigOpts()
|
||||
cassandra_driver = driver.StorageDriver(conf, None)
|
||||
cassandra_driver = driver.CassandraStorageDriver(conf)
|
||||
|
||||
# stubbed cassandra driver
|
||||
self.sc = services.ServicesController(cassandra_driver)
|
||||
|
||||
@file_data('data_get_service.json')
|
||||
@patch.object(services.ServicesController, 'session')
|
||||
@patch.object(cassandra.cluster.Session, 'execute')
|
||||
@ddt.file_data('data_get_service.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_get_service(self, value, mock_session, mock_execute):
|
||||
|
||||
# mock the response from cassandra
|
||||
@ -54,9 +53,9 @@ class CassandraStorageServiceTests(TestCase):
|
||||
self.assertEqual(actual_response[0][0], self.project_id)
|
||||
self.assertEqual(actual_response[0][1], self.service_name)
|
||||
|
||||
@file_data('data_create_service.json')
|
||||
@patch.object(services.ServicesController, 'session')
|
||||
@patch.object(cassandra.cluster.Session, 'execute')
|
||||
@ddt.file_data('data_create_service.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_create_service(self, value, mock_session, mock_execute):
|
||||
responses = self.sc.create(self.project_id, self.service_name, value)
|
||||
|
||||
@ -66,9 +65,9 @@ class CassandraStorageServiceTests(TestCase):
|
||||
|
||||
# TODO(amitgandhinz): need to validate the create to cassandra worked.
|
||||
|
||||
@file_data('data_list_services.json')
|
||||
@patch.object(services.ServicesController, 'session')
|
||||
@patch.object(cassandra.cluster.Session, 'execute')
|
||||
@ddt.file_data('data_list_services.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_list_services(self, value, mock_session, mock_execute):
|
||||
# mock the response from cassandra
|
||||
mock_execute.execute.return_value = value
|
||||
@ -81,8 +80,8 @@ class CassandraStorageServiceTests(TestCase):
|
||||
self.assertEqual(actual_response[0][0], self.project_id)
|
||||
self.assertEqual(actual_response[0][1], "mocksite")
|
||||
|
||||
@patch.object(services.ServicesController, 'session')
|
||||
@patch.object(cassandra.cluster.Session, 'execute')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_delete_service(self, mock_session, mock_execute):
|
||||
# mock the response from cassandra
|
||||
actual_response = self.sc.delete(self.project_id, self.service_name)
|
||||
@ -91,9 +90,9 @@ class CassandraStorageServiceTests(TestCase):
|
||||
# into the driver to respond to this call
|
||||
self.assertEqual(actual_response, None)
|
||||
|
||||
@file_data('data_update_service.json')
|
||||
@patch.object(services.ServicesController, 'session')
|
||||
@patch.object(cassandra.cluster.Session, 'execute')
|
||||
@ddt.file_data('data_update_service.json')
|
||||
@mock.patch.object(services.ServicesController, 'session')
|
||||
@mock.patch.object(cassandra.cluster.Session, 'execute')
|
||||
def test_update_service(self, value, mock_session, mock_execute):
|
||||
# mock the response from cassandra
|
||||
actual_response = self.sc.update(self.project_id,
|
||||
@ -104,7 +103,7 @@ class CassandraStorageServiceTests(TestCase):
|
||||
# into the driver to respond to this call
|
||||
self.assertEqual(actual_response, None)
|
||||
|
||||
@patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
@mock.patch.object(cassandra.cluster.Cluster, 'connect')
|
||||
def test_session(self, mock_service_database):
|
||||
session = self.sc.session
|
||||
self.assertNotEquals(session, None)
|
||||
|
Loading…
x
Reference in New Issue
Block a user