
os-resource-classes is a python library in which the standardized resource classes are maintained. It is done as a library so that multiple services (e.g., placement and nova) can use the same stuff. It is used and managed here in the same way the os-traits library is used: At system start up we compare the contents of the resource_classes table with the classes in the library and add any that are missing. CUSTOM resource classes are added with a high id (and always were, even before this change). Because we need to insert standard resource classes with an id of zero, so we need to protect against mysql thinking 0 on a primary key id is "generate the next one". We don't need a similar thing in os-traits because we don't care about the ids there. And we don't need to guard against postgresql or sqlite at this point because they do not have the same behavior. The resource_class_cache of id to string and string to id mappings continues to be maintained, but now it looks solely in the database. As part of confirming that code, it was discovered that the reader context manager was being entered twice, this has been fixed. Locking around every access to the resource class cache is fairly expensive (changes the perfload job from <2s to >5s). Prior to this change we would only go to cache if the resource classes in the query were not standards. Now we always look at the cache so rather than locking around reads and writes we only lock around writes. This should be okay, because as long as we do a get (intead of the previous two separate accesses) on the cache's dict that operation is safe and if it misses (because something else destroyed the cache) the fall through is to refresh the cache, which still has the lock. While updating the database fixture to ensure that the resource classes are synched properly, it was discovered that addCleanup was being called twice with the same args. That has been fixed. In objects/resource_provider.py the ResourceClass field is changed to a StringField. The field definition was in rc_field and we simply don't need it anymore. This is satisfactory because we don't do any validation on the field internal to the objects (but do elsewhere). Based on initial feedback, 'os_resource_classes' is imported as 'orc' throughout to avoid unwieldy length. Change-Id: Ib7e8081519c3b310cd526284db28c623c8410fbe
122 lines
4.8 KiB
Python
122 lines
4.8 KiB
Python
# 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.
|
|
"""Deployment handling for Placmenent API."""
|
|
|
|
from microversion_parse import middleware as mp_middleware
|
|
import oslo_middleware
|
|
from oslo_middleware import cors
|
|
|
|
from placement import auth
|
|
from placement import db_api
|
|
from placement import fault_wrap
|
|
from placement import handler
|
|
from placement import microversion
|
|
from placement.objects import resource_provider
|
|
from placement import requestlog
|
|
from placement import util
|
|
|
|
|
|
# TODO(cdent): NAME points to the config project being used, so for
|
|
# now this is "nova" but we probably want "placement" eventually.
|
|
NAME = "nova"
|
|
|
|
|
|
def deploy(conf):
|
|
"""Assemble the middleware pipeline leading to the placement app."""
|
|
if conf.api.auth_strategy == 'noauth2':
|
|
auth_middleware = auth.NoAuthMiddleware
|
|
else:
|
|
# Do not use 'oslo_config_project' param here as the conf
|
|
# location may have been overridden earlier in the deployment
|
|
# process with OS_PLACEMENT_CONFIG_DIR in wsgi.py.
|
|
auth_middleware = auth.filter_factory(
|
|
{}, oslo_config_config=conf)
|
|
|
|
# Pass in our CORS config, if any, manually as that's a)
|
|
# explicit, b) makes testing more straightfoward, c) let's
|
|
# us control the use of cors by the presence of its config.
|
|
conf.register_opts(cors.CORS_OPTS, 'cors')
|
|
if conf.cors.allowed_origin:
|
|
cors_middleware = oslo_middleware.CORS.factory(
|
|
{}, **conf.cors)
|
|
else:
|
|
cors_middleware = None
|
|
|
|
context_middleware = auth.PlacementKeystoneContext
|
|
req_id_middleware = oslo_middleware.RequestId
|
|
microversion_middleware = mp_middleware.MicroversionMiddleware
|
|
fault_middleware = fault_wrap.FaultWrapper
|
|
request_log = requestlog.RequestLog
|
|
|
|
application = handler.PlacementHandler(config=conf)
|
|
# configure microversion middleware in the old school way
|
|
application = microversion_middleware(
|
|
application, microversion.SERVICE_TYPE, microversion.VERSIONS,
|
|
json_error_formatter=util.json_error_formatter)
|
|
|
|
# NOTE(cdent): The ordering here is important. The list is ordered
|
|
# from the inside out. For a single request req_id_middleware is called
|
|
# first and microversion_middleware last. Then the request is finally
|
|
# passed to the application (the PlacementHandler). At that point
|
|
# the response ascends the middleware in the reverse of the
|
|
# order the request went in. This order ensures that log messages
|
|
# all see the same contextual information including request id and
|
|
# authentication information.
|
|
for middleware in (fault_middleware,
|
|
request_log,
|
|
context_middleware,
|
|
auth_middleware,
|
|
cors_middleware,
|
|
req_id_middleware,
|
|
):
|
|
if middleware:
|
|
application = middleware(application)
|
|
|
|
# NOTE(mriedem): Ignore scope check UserWarnings from oslo.policy.
|
|
if not conf.oslo_policy.enforce_scope:
|
|
import warnings
|
|
warnings.filterwarnings('ignore',
|
|
message="Policy .* failed scope check",
|
|
category=UserWarning)
|
|
|
|
return application
|
|
|
|
|
|
def update_database():
|
|
"""Do any database updates required at process boot time, such as
|
|
updating the traits table.
|
|
"""
|
|
ctx = db_api.DbContext()
|
|
resource_provider.ensure_trait_sync(ctx)
|
|
resource_provider.ensure_resource_classes_sync(ctx)
|
|
resource_provider.ensure_rc_cache(ctx)
|
|
|
|
|
|
# NOTE(cdent): Althought project_name is no longer used because of the
|
|
# resolution of https://bugs.launchpad.net/nova/+bug/1734491, loadapp()
|
|
# is considered a public interface for the creation of a placement
|
|
# WSGI app so must maintain its interface. The canonical placement WSGI
|
|
# app is created by init_application in wsgi.py, but this is not
|
|
# required and in fact can be limiting. loadapp() may be used from
|
|
# fixtures or arbitrary WSGI frameworks and loaders.
|
|
def loadapp(config, project_name=NAME):
|
|
"""WSGI application creator for placement.
|
|
|
|
:param config: An olso_config.cfg.ConfigOpts containing placement
|
|
configuration.
|
|
:param project_name: oslo_config project name. Ignored, preserved for
|
|
backwards compatibility
|
|
"""
|
|
application = deploy(config)
|
|
update_database()
|
|
return application
|