Move upgrade handlers to a separate package, use stevedore to discover them
Change-Id: Id4de523fe39cf0b9330cb228fbc07ea05a394d3a
This commit is contained in:
parent
9e8269b9c9
commit
4d2a47720b
|
@ -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):
|
||||
|
|
|
@ -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__)
|
|
@ -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')
|
|
@ -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)
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue