neutron/neutron/cmd/upgrade_checks/checks.py

287 lines
12 KiB
Python

# Copyright 2018 Red Hat 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 neutron_lib import constants
from neutron_lib import context
from neutron_lib.db import model_query
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_upgradecheck import common_checks
from oslo_upgradecheck import upgradecheck
from sqlalchemy import or_
from neutron._i18n import _
from neutron.cmd.upgrade_checks import base
from neutron.db.models import agent as agent_model
from neutron.db.models.plugins.ml2 import vlanallocation
from neutron.db import models_v2
OVN_ALEMBIC_TABLE_NAME = "ovn_alembic_version"
LAST_NETWORKING_OVN_EXPAND_HEAD = "e55d09277410"
LAST_NETWORKING_OVN_CONTRACT_HEAD = "1d271ead4eb6"
def get_agents(agt_type):
"""Get agent information from Database
:param agt_type: agent type, one of constants.AGENT_TYPE_*
:return: list of database query results
"""
filters = {'agent_type': [agt_type]}
ctx = context.get_admin_context()
query = model_query.get_collection_query(ctx,
agent_model.Agent,
filters=filters)
return query.all()
def get_l3_agents():
return get_agents(constants.AGENT_TYPE_L3)
def get_nic_switch_agents():
return get_agents(constants.AGENT_TYPE_NIC_SWITCH)
def get_networks():
ctx = context.get_admin_context()
query = model_query.get_collection_query(ctx,
models_v2.Network)
return query.all()
def table_exists(table_name):
ctx = context.get_admin_context()
tables = [t[0] for t in ctx.session.execute("SHOW TABLES;")]
return table_name in tables
def get_ovn_db_revisions():
ctx = context.get_admin_context()
return [row[0] for row in ctx.session.execute(
"SELECT version_num from %s;" % OVN_ALEMBIC_TABLE_NAME)] # nosec
def count_vlan_allocations_invalid_segmentation_id():
ctx = context.get_admin_context()
query = ctx.session.query(vlanallocation.VlanAllocation)
query = query.filter(or_(
vlanallocation.VlanAllocation.vlan_id < constants.MIN_VLAN_TAG,
vlanallocation.VlanAllocation.vlan_id > constants.MAX_VLAN_TAG))
return query.count()
class CoreChecks(base.BaseChecks):
def get_checks(self):
return [
(_("Gateway external network"),
self.gateway_external_network_check),
(_("External network bridge"),
self.external_network_bridge_check),
(_("Worker counts configured"), self.worker_count_check),
(_("Networking-ovn database revision"),
self.ovn_db_revision_check),
(_("NIC Switch agent check kernel"),
self.nic_switch_agent_min_kernel_check),
(_("VLAN allocations valid segmentation ID check"),
self.vlan_allocations_segid_check),
(_('Policy File JSON to YAML Migration'),
(common_checks.check_policy_json, {'conf': cfg.CONF})),
]
@staticmethod
def worker_count_check(checker):
if cfg.CONF.api_workers and cfg.CONF.rpc_workers:
return upgradecheck.Result(
upgradecheck.Code.SUCCESS, _("Number of workers already "
"defined in config"))
else:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("The default number of workers "
"has changed. Please see release notes for the new values, "
"but it is strongly encouraged for deployers to manually "
"set the values for api_workers and rpc_workers."))
@staticmethod
def external_network_bridge_check(checker):
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. Check of usage of "
"'external_network_bridge' config option in L3 agents "
"can't be done"))
agents_with_external_bridge = []
for agent in get_l3_agents():
config_string = agent.get('configurations')
if not config_string:
continue
config = jsonutils.loads(config_string)
if config.get("external_network_bridge"):
agents_with_external_bridge.append(agent.get("host"))
if agents_with_external_bridge:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("L3 agents on hosts %s are still using "
"'external_network_bridge' config option to provide "
"gateway connectivity. This option is now removed. "
"Migration of routers from those L3 agents will be "
"required to connect them to external network through "
"integration bridge.") % agents_with_external_bridge)
else:
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("L3 agents are using integration bridge to connect external "
"gateways"))
@staticmethod
def gateway_external_network_check(checker):
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. Check of usage of "
"'gateway_external_network_id' config option in L3 agents "
"can't be done"))
agents_with_gateway_external_net = []
for agent in get_l3_agents():
config_string = agent.get('configurations')
if not config_string:
continue
config = jsonutils.loads(config_string)
if config.get("gateway_external_network_id"):
agents_with_gateway_external_net.append(agent.get("host"))
if agents_with_gateway_external_net:
agents_list = ", ".join(agents_with_gateway_external_net)
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("L3 agents on hosts %s are still using "
"'gateway_external_network_id' config option to configure "
"external network used as gateway for routers. "
"This option is now removed and routers on those hosts can "
"use multiple external networks as gateways.") % agents_list)
else:
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("L3 agents can use multiple networks as external gateways."))
@staticmethod
def network_mtu_check(checker):
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. Check of 'mtu' in "
"networks can't be done"))
networks_with_empty_mtu_attr = []
for network in get_networks():
mtu = network.get('mtu', None)
if not mtu:
networks_with_empty_mtu_attr.append(network.get("id"))
if networks_with_empty_mtu_attr:
networks_list = ", ".join(networks_with_empty_mtu_attr)
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("The 'mtu' attribute of networks %s are not set "
"This attribute can't be null now.") % networks_list)
else:
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("The 'mtu' attribute of all networks are set."))
@staticmethod
def ovn_db_revision_check(checker):
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. Check of "
"networking-ovn database revision can't be done."))
if not table_exists(OVN_ALEMBIC_TABLE_NAME):
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("Networking-ovn alembic version table don't exists in "
"the database yet."))
revisions = get_ovn_db_revisions()
if (LAST_NETWORKING_OVN_EXPAND_HEAD not in revisions or
LAST_NETWORKING_OVN_CONTRACT_HEAD not in revisions):
return upgradecheck.Result(
upgradecheck.Code.FAILURE,
_("Networking-ovn database tables are not up to date. "
"Please firts update networking-ovn to the latest version "
"from Train release."))
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("Networking-ovn database tables are up to date."))
@staticmethod
def nic_switch_agent_min_kernel_check(checker):
# TODO(adrianc): This was introduced in U release, consider removing
# in 1-2 cycles.
# Background: Issue with old kernel is appernet in CentOS 7 and older.
# U release is the first release that moves from CentOS-7 to CentOS-8,
# this was added as a "heads-up" for operators to make sure min kernel
# requirement is fullfiled.
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. "
"Check for NIC Switch agent can't be done."))
agents = get_nic_switch_agents()
if len(agents):
hosts = ','.join([agent.get("host") for agent in agents])
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("NIC Switch agents detected on hosts %s, please ensure the "
"hosts run with a kernel version 3.13 or newer.") % hosts)
else:
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("No NIC Switch agents detected."))
@staticmethod
def vlan_allocations_segid_check(checker):
"""Checks that "ml2_vlan_allocations.vlan_id" has a valid value
Database register column "ml2_vlan_allocations.vlan_id" must be between
1 and 4094.
"""
if not cfg.CONF.database.connection:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("Database connection string is not set. Check for VLAN "
"allocations with invalid segmentation IDs can't be done."))
count = count_vlan_allocations_invalid_segmentation_id()
if count:
return upgradecheck.Result(
upgradecheck.Code.WARNING,
_("There are %(count)s registers in 'ml2_vlan_allocations' "
"table with an invalid segmentation ID. 'vlan_id' must be "
"between %(min_vlan)s and %(max_vlan)s") %
{'count': count, 'min_vlan': constants.MIN_VLAN_TAG,
'max_vlan': constants.MAX_VLAN_TAG})
return upgradecheck.Result(
upgradecheck.Code.SUCCESS,
_("All 'ml2_vlan_allocations' registers have a valid "
"segmentation ID."))