Files
placement/placement/deploy.py
Chris Dent a241fcee4c Use os-resource-classes in placement
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
2018-12-19 10:43:18 +00:00

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