Move upgrade handlers to a separate package, use stevedore to discover them

Change-Id: Id4de523fe39cf0b9330cb228fbc07ea05a394d3a
This commit is contained in:
Yuriy Taraday 2015-08-26 15:56:57 +03:00
parent 9e8269b9c9
commit 4d2a47720b
5 changed files with 187 additions and 122 deletions

View File

@ -11,130 +11,17 @@
# under the License.
import logging
import os
import yaml
from cliff import command as cmd
from fuelclient.objects import environment as environment_obj
from fuelclient.objects import node as node_obj
from octane.helpers import tasks as tasks_helpers
from octane.helpers import transformations
from octane import magic_consts
from octane.handlers import upgrade as upgrade_handlers
from octane.util import env as env_util
from octane.util import ssh
LOG = logging.getLogger(__name__)
class UpgradeHandler(object):
def __init__(self, node, env, isolated):
self.node = node
self.env = env
self.isolated = isolated
def preupgrade(self):
raise NotImplementedError('preupgrade')
def prepare(self):
raise NotImplementedError('prepare')
def predeploy(self):
raise NotImplementedError('predeploy')
def postdeploy(self):
raise NotImplementedError('postdeploy')
class ControllerUpgrade(UpgradeHandler):
def __init__(self, node, env, isolated):
super(ControllerUpgrade, self).__init__(node, env, isolated)
self.service_tenant_id = None
self.gateway = None
def preupgrade(self):
self.service_tenant_id = env_util.get_service_tenant_id(
self.env, self.node)
def predeploy(self):
deployment_info = self.env.get_default_facts(
'deployment', nodes=[self.node.data['id']])
if self.isolated:
# From backup_deployment_info
backup_path = os.path.join(
magic_consts.FUEL_CACHE,
"deployment_{0}.orig".format(self.node.data['cluster']),
)
if not os.path.exists(backup_path):
os.makedirs(backup_path)
# Roughly taken from Environment.write_facts_to_dir
for info in deployment_info:
fname = os.path.join(
backup_path,
"{0}_{1}.yaml".format(info['role'], info['uid']),
)
with open(fname, 'w') as f:
yaml.dump(info, f, default_flow_style=False)
for info in deployment_info:
if self.isolated:
transformations.remove_physical_ports(info)
endpoints = deployment_info[0]["network_scheme"]["endpoints"]
self.gateway = endpoints["br-ex"]["gateway"]
# From run_ping_checker
info['run_ping_checker'] = False
transformations.remove_predefined_nets(info)
transformations.reset_gw_admin(info)
self.env.upload_facts('deployment', deployment_info)
tasks = self.env.get_deployment_tasks()
tasks_helpers.skip_tasks(tasks)
self.env.update_deployment_tasks(tasks)
def postdeploy(self):
# From neutron_update_admin_tenant_id
sftp = ssh.sftp(self.node)
with ssh.update_file(sftp, '/etc/neutron/neutron.conf') as (old, new):
for line in old:
if line.startswith('nova_admin_tenant_id'):
new.write('nova_admin_tenant_id = {0}\n'.format(
self.service_tenant_id))
else:
new.write(line)
ssh.call(['restart', 'neutron-server'], node=self.node)
if self.isolated:
# From restore_default_gateway
ssh.call(['ip', 'route', 'delete', 'default'], node=self.node)
ssh.call(['ip', 'route', 'add', 'default', 'via', self.gateway],
node=self.node)
# TODO: use stevedore for this
role_upgrade_handlers = {
'controller': ControllerUpgrade,
}
def get_role_upgrade_handlers(node, env, isolated):
role_handlers = []
for role in node.data['roles']:
try:
cls = role_upgrade_handlers[role]
except KeyError:
LOG.warn("Role '%s' is not supported, skipping")
else:
role_handlers.append(cls(node, env, isolated))
return role_handlers
def call_role_upgrade_handlers(handlers, method):
for node_handlers in handlers.values():
for handler in node_handlers:
try:
getattr(handler, method)()
except NotImplementedError:
LOG.debug("Method '%s' not implemented in handler %s",
method, type(handler).__name__)
def upgrade_node(env_id, node_ids, isolated=False):
# From check_deployment_status
env = environment_obj.Environment(env_id)
@ -158,18 +45,15 @@ def upgrade_node(env_id, node_ids, isolated=False):
orig_id, one_orig_id,
)
one_orig_id = orig_id
call_handlers = upgrade_handlers.get_nodes_handlers(nodes, env, isolated)
role_handlers = {}
for node in nodes:
role_handlers[node] = get_role_upgrade_handlers(node, env, isolated)
call_role_upgrade_handlers(role_handlers, 'preupgrade')
call_role_upgrade_handlers(role_handlers, 'prepare')
call_handlers('preupgrade')
call_handlers('prepare')
env_util.move_nodes(env, nodes)
env_util.provision_nodes(env, nodes)
call_role_upgrade_handlers(role_handlers, 'predeploy')
call_handlers('predeploy')
env_util.deploy_nodes(env, nodes)
call_role_upgrade_handlers(role_handlers, 'postdeploy')
call_handlers('postdeploy')
class UpgradeNodeCommand(cmd.Command):

View File

@ -0,0 +1,61 @@
# 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 functools
import logging
import stevedore
LOG = logging.getLogger(__name__)
class _GetNodesHandlersFactory(object):
_extension_manager = None
def __init__(self, name):
self.name = name
def on_load_failure_callback(self, endpoint, exc):
LOG.error('Failed to load %s: %s', endpoint.name, exc)
raise # Avoid unexpectedly skipped steps
@property
def extensions(self):
extension_manager = stevedore.ExtensionManager(
'octane.handlers.' + self.name,
on_load_failure_callback=self.on_load_failure_callback,
)
extensions = dict(extension_manager.map(
lambda ext: (ext.name, ext.plugin)))
self.__dict__['extensions'] = extensions
return extensions
def __call__(self, nodes, *args, **kwargs):
handlers = []
for node in nodes:
for role in node.data['roles']:
try:
cls = self.extensions[role]
except KeyError:
LOG.warn("Role '%s' is not supported, skipping", role)
else:
handlers.append(cls(node, *args, **kwargs))
return functools.partial(self.call_method_on_all, handlers)
@staticmethod
def call_method_on_all(handlers, method):
for handler in handlers:
try:
getattr(handler, method)()
except NotImplementedError:
LOG.info("Method '%s' not implemented in handler %s",
method, type(handler).__name__)

View File

@ -0,0 +1,34 @@
# 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 octane import handlers
class UpgradeHandler(object):
def __init__(self, node, env, isolated):
self.node = node
self.env = env
self.isolated = isolated
def preupgrade(self):
raise NotImplementedError('preupgrade')
def prepare(self):
raise NotImplementedError('prepare')
def predeploy(self):
raise NotImplementedError('predeploy')
def postdeploy(self):
raise NotImplementedError('postdeploy')
get_nodes_handlers = handlers._GetNodesHandlersFactory('upgrade')

View File

@ -0,0 +1,84 @@
# 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 os
import yaml
from octane.handlers import upgrade
from octane.helpers import tasks as tasks_helpers
from octane.helpers import transformations
from octane import magic_consts
from octane.util import env as env_util
from octane.util import ssh
class ControllerUpgrade(upgrade.UpgradeHandler):
def __init__(self, node, env, isolated):
super(ControllerUpgrade, self).__init__(node, env, isolated)
self.service_tenant_id = None
self.gateway = None
def preupgrade(self):
self.service_tenant_id = env_util.get_service_tenant_id(
self.env, self.node)
def predeploy(self):
deployment_info = self.env.get_default_facts(
'deployment', nodes=[self.node.data['id']])
if self.isolated:
# From backup_deployment_info
backup_path = os.path.join(
magic_consts.FUEL_CACHE,
"deployment_{0}.orig".format(self.node.data['cluster']),
)
if not os.path.exists(backup_path):
os.makedirs(backup_path)
# Roughly taken from Environment.write_facts_to_dir
for info in deployment_info:
fname = os.path.join(
backup_path,
"{0}_{1}.yaml".format(info['role'], info['uid']),
)
with open(fname, 'w') as f:
yaml.dump(info, f, default_flow_style=False)
for info in deployment_info:
if self.isolated:
transformations.remove_physical_ports(info)
endpoints = deployment_info[0]["network_scheme"]["endpoints"]
self.gateway = endpoints["br-ex"]["gateway"]
# From run_ping_checker
info['run_ping_checker'] = False
transformations.remove_predefined_nets(info)
transformations.reset_gw_admin(info)
self.env.upload_facts('deployment', deployment_info)
tasks = self.env.get_deployment_tasks()
tasks_helpers.skip_tasks(tasks)
self.env.update_deployment_tasks(tasks)
def postdeploy(self):
# From neutron_update_admin_tenant_id
sftp = ssh.sftp(self.node)
with ssh.update_file(sftp, '/etc/neutron/neutron.conf') as (old, new):
for line in old:
if line.startswith('nova_admin_tenant_id'):
new.write('nova_admin_tenant_id = {0}\n'.format(
self.service_tenant_id))
else:
new.write(line)
ssh.call(['restart', 'neutron-server'], node=self.node)
if self.isolated:
# From restore_default_gateway
ssh.call(['ip', 'route', 'delete', 'default'], node=self.node)
ssh.call(['ip', 'route', 'add', 'default', 'via', self.gateway],
node=self.node)

View File

@ -33,6 +33,8 @@ octane =
upgrade-env = octane.commands.upgrade_env:UpgradeEnvCommand
upgrade-node = octane.commands.upgrade_node:UpgradeNodeCommand
upgrade-db = octane.commands.upgrade_db:UpgradeDBCommand
octane.handlers.upgrade =
controller = octane.handlers.upgrade.controller:ControllerUpgrade
fuelclient =
env_clone = octane.fuelclient.clone_env:EnvClone
env_move_node = octane.fuelclient.move_node:EnvMoveNode