nova/nova/api/openstack/wsgi_app.py
Erik Berg 2d79baf6a3 Fix binary name
Before an upgrade, we have these type of entries in the db.

MariaDB [nova]> SELECT id, host, `binary`, deleted, version FROM services;
+----+--------------+--------------------+---------+---------+
| id | host         | binary             | deleted | version |
+----+--------------+--------------------+---------+---------+
|  5 | r1-n-os-api  | nova-osapi_compute | 0       |      16 |
| 21 | r1-n-m-api   | nova-metadata      | 0       |      16 |

The wsgi files we run basically boil down to something like

  NAME=metadata
  return wsgi_app.init_application(NAME)

In the wsgi_app.py we see this function

  service_ref = objects.Service.get_by_host_and_binary(ctxt, host, name)

Which results in a really big query, which again comes down to

  SELECT host, `binary` FROM services
    WHERE host = 'r1-n-m-api' AND `binary` == 'metadata'

No results. service_ref is set to None. Carry on.

  if service_ref:
    #Nope.
  else:
    try:
      ...
      service_obj.host = host
      service_obj.binary = 'nova-%s' % name
      service_obj.create()

Which results in a INSERT statement something like this;

  INSERT INTO services(host, `binary`, report_count, disabled, deleted, version)
    VALUES ('r1-n-m-api', 'nova-metadata', 0, 0, 0, 22)

  ERROR 1062 (23000): Duplicate entry 'r1-n-m-api-nova-metadata-0' for key 'uniq_services0host0binary0deleted'

So the first suggested fix is to prepend 'nova-' to the name, and make both
queries ask for 'nova-metadata'.  There's also a check that it doesn't start
with 'nova-', incase someone decides to prepend 'nova-' to the NAME= in the
wsgi-file. Which migth be a litte overkill, but just a safeguard none the less.

Change-Id: I58cf9a0115a98c78e5d2fb57c41c13ba6fac0fad
Closes-bug: 1715463
(cherry picked from commit 0b4a021e42)
2017-12-01 09:00:15 +00:00

88 lines
2.6 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.
"""WSGI application initialization for Nova APIs."""
import os
from oslo_config import cfg
from oslo_log import log as logging
from paste import deploy
from nova import config
from nova import context
from nova import exception
from nova import objects
from nova import service
from nova import utils
CONF = cfg.CONF
CONFIG_FILES = ['api-paste.ini', 'nova.conf']
utils.monkey_patch()
objects.register_all()
def _get_config_files(env=None):
if env is None:
env = os.environ
dirname = env.get('OS_NOVA_CONFIG_DIR', '/etc/nova').strip()
return [os.path.join(dirname, config_file)
for config_file in CONFIG_FILES]
def _setup_service(host, name):
binary = name if name.startswith('nova-') else "nova-%s" % name
ctxt = context.get_admin_context()
service_ref = objects.Service.get_by_host_and_binary(
ctxt, host, binary)
if service_ref:
service._update_service_ref(service_ref)
else:
try:
service_obj = objects.Service(ctxt)
service_obj.host = host
service_obj.binary = binary
service_obj.topic = None
service_obj.report_count = 0
service_obj.create()
except (exception.ServiceTopicExists,
exception.ServiceBinaryExists):
# If we race to create a record with a sibling, don't
# fail here.
pass
def error_application(exc, name):
# TODO(cdent): make this something other than a stub
def application(environ, start_response):
start_response('500 Internal Server Error', [
('Content-Type', 'text/plain; charset=UTF-8')])
return ['Out of date %s service %s\n' % (name, exc)]
return application
def init_application(name):
conf_files = _get_config_files()
config.parse_args([], default_config_files=conf_files)
logging.setup(CONF, "nova")
try:
_setup_service(CONF.host, name)
except exception.ServiceTooOld as exc:
return error_application(exc, name)
conf = conf_files[0]
return deploy.loadapp('config:%s' % conf, name=name)