Remove unsupported commands
Delete commands that are not required for upgrade from version 7 to version 8 and above. Change-Id: Ibd30603b60eb8ca3b68eefb70be39937c5f9d584
This commit is contained in:
parent
a4ed2778ab
commit
fb061f4ef6
|
@ -1,70 +0,0 @@
|
|||
# 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 logging
|
||||
import os
|
||||
|
||||
from cliff import command as cmd
|
||||
from fuelclient import objects
|
||||
|
||||
from octane import magic_consts
|
||||
from octane.util import env as env_util
|
||||
from octane.util import node as node_util
|
||||
from octane.util import ssh
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def cleanup_environment(env_id):
|
||||
env = objects.Environment(env_id)
|
||||
|
||||
nodes = env.get_all_nodes()
|
||||
for node in nodes:
|
||||
node_util.remove_compute_upgrade_levels(node)
|
||||
|
||||
controller = env_util.get_one_controller(env)
|
||||
sftp = ssh.sftp(controller)
|
||||
admin_pass = env_util.get_admin_password(env, controller)
|
||||
script_filename = 'clean_env.py'
|
||||
|
||||
with ssh.tempdir(controller) as tempdir:
|
||||
script_src_filename = os.path.join(
|
||||
magic_consts.CWD, "helpers", script_filename)
|
||||
script_dst_filename = os.path.join(tempdir, script_filename)
|
||||
sftp.put(script_src_filename, script_dst_filename)
|
||||
|
||||
command = [
|
||||
'sh', '-c', '. /root/openrc; export OS_PASSWORD={0}; python {1}'
|
||||
.format(admin_pass, script_dst_filename),
|
||||
]
|
||||
|
||||
with ssh.popen(command, node=controller, stdin=ssh.PIPE) as proc:
|
||||
roles = ["controller", "compute"]
|
||||
for node in env_util.get_nodes(env, roles):
|
||||
data = "{0}\n{1}\n".format(node.data['fqdn'].split('.')[0],
|
||||
node.data['fqdn'])
|
||||
proc.stdin.write(data)
|
||||
|
||||
|
||||
class CleanupCommand(cmd.Command):
|
||||
"""Cleanup upgraded environment"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CleanupCommand, self).get_parser(prog_name)
|
||||
|
||||
parser.add_argument(
|
||||
'env', type=int, metavar='ENV_ID',
|
||||
help="ID of environment to cleanup")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
cleanup_environment(parsed_args.env)
|
|
@ -1,74 +0,0 @@
|
|||
# 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.path
|
||||
|
||||
from cliff import command as cmd
|
||||
|
||||
from octane import magic_consts
|
||||
from octane.util import archivate
|
||||
from octane.util import docker
|
||||
from octane.util import subprocess
|
||||
|
||||
|
||||
def apply_patches(revert=False):
|
||||
for container, prefix, patch in magic_consts.PATCHES:
|
||||
docker.apply_patches(container, prefix,
|
||||
os.path.join(magic_consts.CWD, patch),
|
||||
revert=revert)
|
||||
|
||||
|
||||
def revert_initramfs():
|
||||
backup = magic_consts.BOOTSTRAP_INITRAMFS + '.bkup'
|
||||
os.rename(backup, magic_consts.BOOTSTRAP_INITRAMFS)
|
||||
|
||||
|
||||
def patch_initramfs():
|
||||
with archivate.update_cpio(magic_consts.BOOTSTRAP_INITRAMFS) as chroot:
|
||||
patch_fuel_agent(chroot)
|
||||
docker.run_in_container("cobbler", ["cobbler", "sync"])
|
||||
|
||||
|
||||
def patch_fuel_agent(chroot):
|
||||
patch_dir = os.path.join(magic_consts.CWD, "patches", "fuel_agent")
|
||||
with open(os.path.join(patch_dir, "patch")) as patch:
|
||||
subprocess.call(["patch", "-N", "-p0"], stdin=patch, cwd=chroot)
|
||||
|
||||
|
||||
def prepare():
|
||||
if not os.path.isdir(magic_consts.FUEL_CACHE):
|
||||
os.makedirs(magic_consts.FUEL_CACHE)
|
||||
subprocess.call(["yum", "-y", "install"] + magic_consts.PACKAGES)
|
||||
# From patch_all_containers
|
||||
apply_patches()
|
||||
docker.run_in_container("nailgun", ["pkill", "-f", "wsgi"])
|
||||
patch_initramfs()
|
||||
|
||||
|
||||
def revert_prepare():
|
||||
apply_patches(revert=True)
|
||||
docker.run_in_container("nailgun", ["pkill", "-f", "wsgi"])
|
||||
revert_initramfs()
|
||||
|
||||
|
||||
class PrepareCommand(cmd.Command):
|
||||
"""Prepare the Fuel master node to upgrade an environment"""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
prepare()
|
||||
|
||||
|
||||
class RevertCommand(cmd.Command):
|
||||
"""Revert all patches applied by 'prepare' command"""
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
revert_prepare()
|
|
@ -1,74 +0,0 @@
|
|||
# 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 tempfile
|
||||
|
||||
from cliff import command as cmd
|
||||
from fuelclient.objects import environment as environment_obj
|
||||
|
||||
from octane.helpers.sync_glance_images import sync_glance_images
|
||||
from octane.util import db
|
||||
from octane.util import env as env_util
|
||||
from octane.util import ssh
|
||||
|
||||
|
||||
def prepare(orig_id, seed_id):
|
||||
orig_env = environment_obj.Environment(orig_id)
|
||||
seed_env = environment_obj.Environment(seed_id)
|
||||
controller = env_util.get_one_controller(seed_env)
|
||||
|
||||
with tempfile.NamedTemporaryFile() as temp:
|
||||
db.mysqldump_from_env(orig_env, ['keystone'], temp.name)
|
||||
db.mysqldump_restore_to_env(seed_env, temp.name)
|
||||
|
||||
ssh.call(['keystone-manage', 'db_sync'],
|
||||
node=controller, parse_levels=True)
|
||||
for controller in env_util.get_controllers(seed_env):
|
||||
ssh.call(['service', 'memcached', 'restart'], node=controller)
|
||||
|
||||
|
||||
class SyncImagesCommand(cmd.Command):
|
||||
"""Sync glance images between ORIG and SEED environments"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SyncImagesCommand, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'orig_id', type=int, metavar='ORIG_ID',
|
||||
help="ID of original environment")
|
||||
parser.add_argument(
|
||||
'seed_id', type=int, metavar='SEED_ID',
|
||||
help="ID of seed environment")
|
||||
parser.add_argument(
|
||||
'swift_ep', type=str,
|
||||
help="Endpoint's name where swift-proxy service is listening on")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
sync_glance_images(parsed_args.orig_id, parsed_args.seed_id,
|
||||
parsed_args.swift_ep)
|
||||
|
||||
|
||||
class SyncImagesPrepareCommand(cmd.Command):
|
||||
"""Sync glance images between ORIG and SEED environments"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SyncImagesPrepareCommand, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'orig_id', type=int, metavar='ORIG_ID',
|
||||
help="ID of original environment")
|
||||
parser.add_argument(
|
||||
'seed_id', type=int, metavar='SEED_ID',
|
||||
help="ID of seed environment")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
prepare(parsed_args.orig_id, parsed_args.seed_id)
|
|
@ -1,92 +0,0 @@
|
|||
# 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 logging
|
||||
|
||||
from cliff import command as cmd
|
||||
from fuelclient import objects
|
||||
from requests import HTTPError
|
||||
|
||||
from octane.util import env as env_util
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
KEEP_NETWORK_NAMES = ['fuelweb_admin', 'management', 'public']
|
||||
|
||||
|
||||
def update_env_networks(env_id, networks):
|
||||
fields_to_update = ['meta', 'ip_ranges']
|
||||
env = objects.Environment(env_id)
|
||||
release_id = env.get_fresh_data()['release_id']
|
||||
network_data = env.get_network_data()
|
||||
node_group_id = None
|
||||
|
||||
for ng in network_data['networks']:
|
||||
if ng['name'] in KEEP_NETWORK_NAMES:
|
||||
continue
|
||||
if node_group_id is None:
|
||||
# for now we'll have only one node group
|
||||
# so just take it id from any network
|
||||
node_group_id = ng['group_id']
|
||||
objects.NetworkGroup(ng['id']).delete()
|
||||
|
||||
data_to_update = {}
|
||||
for ng in networks:
|
||||
if ng['name'] in KEEP_NETWORK_NAMES:
|
||||
continue
|
||||
try:
|
||||
objects.NetworkGroup.create(
|
||||
ng['name'],
|
||||
release_id,
|
||||
ng['vlan_start'],
|
||||
ng['cidr'],
|
||||
ng['gateway'],
|
||||
node_group_id,
|
||||
ng['meta']
|
||||
)
|
||||
except HTTPError:
|
||||
LOG.error("Cannot sync network '{0}'".format(ng['name']))
|
||||
continue
|
||||
data = {}
|
||||
for key in fields_to_update:
|
||||
data[key] = ng[key]
|
||||
data_to_update[ng['name']] = data
|
||||
|
||||
# now we need to update new networks with
|
||||
# correct ip_ranges and meta
|
||||
network_data = env.get_network_data()
|
||||
network_data['networks'] = [ng for ng in network_data['networks']
|
||||
if ng['name'] not in KEEP_NETWORK_NAMES]
|
||||
for ng in network_data['networks']:
|
||||
if ng['name'] in data_to_update:
|
||||
for k in fields_to_update:
|
||||
ng[k] = data_to_update[ng['name']][k]
|
||||
env.set_network_data(network_data)
|
||||
|
||||
|
||||
class SyncNetworksCommand(cmd.Command):
|
||||
"""Synchronize network groups in original and seed environments"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SyncNetworksCommand, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'original_env', type=int, metavar='ORIGINAL_ENV_ID',
|
||||
help="ID of original environment")
|
||||
parser.add_argument(
|
||||
'seed_env', type=int, metavar='SEED_ENV_ID',
|
||||
help="ID of seed environment")
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
orig_env = objects.Environment(parsed_args.original_env)
|
||||
networks = env_util.get_env_networks(orig_env)
|
||||
update_env_networks(parsed_args.seed_env, networks)
|
|
@ -1,204 +0,0 @@
|
|||
# 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 argparse
|
||||
import logging
|
||||
import re
|
||||
import requests
|
||||
|
||||
from cliff import command as cmd
|
||||
from fuelclient.objects import environment
|
||||
from fuelclient.objects import node as node_obj
|
||||
|
||||
from octane.util import env as env_util
|
||||
from octane.util import ssh
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_template_hosts_by_name(client, plugin_name):
|
||||
return client.template.get(filter={'name': plugin_name},
|
||||
selectHosts=['name'])[0]['hosts']
|
||||
|
||||
|
||||
def get_host_snmp_ip(client, host_id):
|
||||
# second type is SNMP type
|
||||
return client.hostinterface.get(hosids=host_id,
|
||||
output=['ip'],
|
||||
filter={'type': 2})[0]['ip']
|
||||
|
||||
|
||||
def get_zabbix_url(astute):
|
||||
return 'http://{0}/zabbix'.format(astute['public_vip'])
|
||||
|
||||
|
||||
def get_zabbix_credentials(astute):
|
||||
return astute['zabbix']['username'], astute['zabbix']['password']
|
||||
|
||||
|
||||
def zabbix_monitoring_settings(astute, attrs):
|
||||
attrs['username']['value'] = astute['zabbix']['username']
|
||||
attrs['password']['value'] = astute['zabbix']['password']
|
||||
attrs['db_password']['value'] = astute['zabbix']['db_password']
|
||||
attrs['metadata']['enabled'] = astute['zabbix']['enabled']
|
||||
|
||||
|
||||
def emc_vnx_settings(astute, attrs):
|
||||
attrs['emc_sp_a_ip']['value'] = astute['storage']['emc_sp_a_ip']
|
||||
attrs['emc_sp_b_ip']['value'] = astute['storage']['emc_sp_b_ip']
|
||||
attrs['emc_password']['value'] = astute['storage']['emc_password']
|
||||
attrs['emc_username']['value'] = astute['storage']['emc_username']
|
||||
attrs['emc_pool_name']['value'] = astute['storage']['emc_pool_name']
|
||||
attrs['metadata']['enabled'] = astute['storage']['volumes_emc']
|
||||
|
||||
|
||||
def zabbix_snmptrapd_settings(astute, attrs):
|
||||
node = node_obj.Node(astute['uid'])
|
||||
with ssh.sftp(node).open('/etc/snmp/snmptrapd.conf') as f:
|
||||
data = f.read()
|
||||
template = re.compile(r"authCommunity\s[a-z-,]+\s([a-z-]+)")
|
||||
match = template.search(data)
|
||||
attrs['community']['value'] = match.group(1)
|
||||
attrs['metadata']['enabled'] = True
|
||||
|
||||
|
||||
def get_zabbix_client(astute):
|
||||
url = get_zabbix_url(astute)
|
||||
user, password = get_zabbix_credentials(astute)
|
||||
session = requests.Session()
|
||||
node_cidr = astute['network_scheme']['endpoints']['br-fw-admin']['IP'][0]
|
||||
node_ip = node_cidr.split('/')[0]
|
||||
session.proxies = {
|
||||
'http': 'http://{0}:8888'.format(node_ip)
|
||||
}
|
||||
import pyzabbix
|
||||
client = pyzabbix.ZabbixAPI(server=url, session=session)
|
||||
client.login(user=user, password=password)
|
||||
return client
|
||||
|
||||
|
||||
def zabbix_monitoring_emc_settings(astute, attrs):
|
||||
client = get_zabbix_client(astute)
|
||||
|
||||
hosts = get_template_hosts_by_name(client, 'Template EMC VNX')
|
||||
for host in hosts:
|
||||
host['ip'] = get_host_snmp_ip(client, host['hostid'])
|
||||
settings = ','.join('{0}:{1}'.format(host['name'], host['ip'])
|
||||
for host in hosts)
|
||||
|
||||
attrs['hosts']['value'] = settings
|
||||
attrs['metadata']['enabled'] = True
|
||||
|
||||
|
||||
def zabbix_monitoring_extreme_networks_settings(astute, attrs):
|
||||
client = get_zabbix_client(astute)
|
||||
|
||||
hosts = get_template_hosts_by_name(client, 'Template Extreme Networks')
|
||||
for host in hosts:
|
||||
host['ip'] = get_host_snmp_ip(client, host['hostid'])
|
||||
settings = ','.join('{0}:{1}'.format(host['name'], host['ip'])
|
||||
for host in hosts)
|
||||
|
||||
attrs['hosts']['value'] = settings
|
||||
attrs['metadata']['enabled'] = True
|
||||
|
||||
|
||||
class UnknownPlugin(Exception):
|
||||
message = "Unknown plugin '{0}'"
|
||||
|
||||
def __init__(self, plugin):
|
||||
super(UnknownPlugin, self).__init__(self.message.format(plugin))
|
||||
|
||||
|
||||
class PluginNotConfigured(Exception):
|
||||
message = "No settings for plugin '{0}' in environment #{1}. " \
|
||||
"Was it installed before environment #{1} has been created?"
|
||||
|
||||
def __init__(self, plugin, env_id):
|
||||
super(PluginNotConfigured, self).__init__(self.message.format(
|
||||
plugin, env_id))
|
||||
|
||||
|
||||
def transfer_plugins_settings(orig_env_id, seed_env_id, plugins):
|
||||
orig_env = environment.Environment(orig_env_id)
|
||||
seed_env = environment.Environment(seed_env_id)
|
||||
astute = env_util.get_astute_yaml(orig_env)
|
||||
attrs = seed_env.get_settings_data()
|
||||
editable_attrs = attrs['editable']
|
||||
|
||||
plugin_fns = {}
|
||||
plugin_attrs = {}
|
||||
for plugin in plugins:
|
||||
try:
|
||||
plugin_fns[plugin] = PLUGINS[plugin]
|
||||
except KeyError:
|
||||
raise UnknownPlugin(plugin)
|
||||
try:
|
||||
plugin_attrs[plugin] = editable_attrs[plugin]
|
||||
except KeyError:
|
||||
raise PluginNotConfigured(plugin, seed_env_id)
|
||||
|
||||
for plugin in plugins:
|
||||
LOG.info("Fetching settings for plugin '%s'", plugin)
|
||||
plugin_fn = plugin_fns[plugin]
|
||||
plugin_attr = plugin_attrs[plugin]
|
||||
plugin_fn(astute, plugin_attr)
|
||||
|
||||
seed_env.set_settings_data(attrs)
|
||||
|
||||
|
||||
PLUGINS = {
|
||||
'zabbix_monitoring': zabbix_monitoring_settings,
|
||||
'emc_vnx': emc_vnx_settings,
|
||||
'zabbix_snmptrapd': zabbix_snmptrapd_settings,
|
||||
'zabbix_monitoring_emc': zabbix_monitoring_emc_settings,
|
||||
'zabbix_monitoring_extreme_networks':
|
||||
zabbix_monitoring_extreme_networks_settings,
|
||||
}
|
||||
|
||||
|
||||
def plugin_names(s):
|
||||
plugins = s.split(',')
|
||||
for plugin in plugins:
|
||||
if plugin not in PLUGINS:
|
||||
raise argparse.ArgumentTypeError("Unknown plugin '{0}'"
|
||||
.format(plugin))
|
||||
return plugins
|
||||
|
||||
|
||||
class UpdatePluginSettingsCommand(cmd.Command):
|
||||
"""Transfer settings for specified plugin from ORIG_ENV to SEED_ENV"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UpdatePluginSettingsCommand, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'orig_env',
|
||||
type=int,
|
||||
metavar='ORIG_ID',
|
||||
help="ID of original environment")
|
||||
parser.add_argument(
|
||||
'seed_env',
|
||||
type=int,
|
||||
metavar='SEED_ID',
|
||||
help="ID of seed environment")
|
||||
parser.add_argument(
|
||||
'--plugins',
|
||||
type=plugin_names,
|
||||
required=True,
|
||||
help="Comma separated values: {0}".format(', '.join(PLUGINS)))
|
||||
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
transfer_plugins_settings(parsed_args.orig_env,
|
||||
parsed_args.seed_env,
|
||||
parsed_args.plugins)
|
|
@ -1,223 +0,0 @@
|
|||
# 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 logging
|
||||
|
||||
from fuelclient.objects import environment as environment_obj
|
||||
|
||||
from octane.util import env as env_util
|
||||
from octane.util import ssh
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_endpoint_ip(ep_name, yaml_data):
|
||||
endpoint = yaml_data['network_scheme']['endpoints'].get(ep_name)
|
||||
if not endpoint:
|
||||
return None
|
||||
net_data = endpoint["IP"][0]
|
||||
if net_data:
|
||||
return net_data.split('/')[0]
|
||||
|
||||
|
||||
def get_glance_password(yaml_data):
|
||||
return yaml_data['glance']['user_password']
|
||||
|
||||
|
||||
def parse_swift_out(output, field):
|
||||
for line in output.splitlines()[1:-1]:
|
||||
parts = line.split(': ')
|
||||
if parts[0].strip() == field:
|
||||
return parts[1]
|
||||
raise Exception(
|
||||
"Field {0} not found in output:\n{1}".format(field, output))
|
||||
|
||||
|
||||
def get_swift_objects(node, tenant, user, password, token, container):
|
||||
cmd = ". /root/openrc; swift --os-project-name {0} --os-username {1}"\
|
||||
" --os-password {2} --os-auth-token {3} list {4}".format(tenant,
|
||||
user,
|
||||
password,
|
||||
token,
|
||||
container)
|
||||
objects_list = ssh.call_output(["sh", "-c", cmd], node=node)
|
||||
return objects_list.split('\n')[:-1]
|
||||
|
||||
|
||||
def get_object_property(node, tenant, user, password, token, container,
|
||||
object_id, prop):
|
||||
cmd = ". /root/openrc; swift --os-project-name {0} --os-username {1}"\
|
||||
" --os-password {2} --os-auth-token {3} stat {4} {5}"\
|
||||
.format(tenant,
|
||||
user,
|
||||
password,
|
||||
token,
|
||||
container,
|
||||
object_id)
|
||||
object_data = ssh.call_output(["sh", "-c", cmd], node=node)
|
||||
return parse_swift_out(object_data, prop)
|
||||
|
||||
|
||||
def get_auth_token(node, tenant, user, password):
|
||||
cmd = ". /root/openrc; keystone --os-tenant-name {0}"\
|
||||
" --os-username {1} --os-password {2} token-get".format(tenant,
|
||||
user,
|
||||
password)
|
||||
token_info = ssh.call_output(["sh", "-c", cmd], node=node)
|
||||
return env_util.parse_tenant_get(token_info, 'id')
|
||||
|
||||
|
||||
def download_image(node, tenant, user, password, token, container, object_id):
|
||||
cmd = ". /root/openrc; swift --os-project-name {0} --os-username {1}"\
|
||||
" --os-password {2} --os-auth-token {3} download {4} {5}"\
|
||||
.format(tenant,
|
||||
user,
|
||||
password,
|
||||
token,
|
||||
container,
|
||||
object_id)
|
||||
ssh.call(["sh", "-c", cmd], node=node)
|
||||
LOG.info("Swift %s image has been downloaded", object_id)
|
||||
|
||||
|
||||
def delete_image(node, tenant, user, password, token, container, object_id):
|
||||
cmd = ". /root/openrc; swift --os-project-name {0}"\
|
||||
" --os-username {1} --os-password {2} --os-auth-token {3}"\
|
||||
" delete {4} {5}".format(tenant, user, password, token,
|
||||
container, object_id)
|
||||
ssh.call(["sh", "-c", cmd], node=node)
|
||||
LOG.info("Swift %s image has been deleted", object_id)
|
||||
|
||||
|
||||
def transfer_image(node, tenant, user, password, token, container, object_id,
|
||||
storage_ip, tenant_id):
|
||||
storage_url = "http://{0}:8080/v1/AUTH_{1}".format(storage_ip, tenant_id)
|
||||
cmd = ['swift', '--os-project-name', tenant, '--os-username', user,
|
||||
'--os-password', password, '--os-auth-token', token,
|
||||
'--os-storage-url', storage_url, 'upload', container,
|
||||
object_id]
|
||||
ssh.call(cmd, node=node)
|
||||
LOG.info("Swift %s image has been transferred", object_id)
|
||||
|
||||
|
||||
def create_container(node, tenant, user, password, token, container):
|
||||
cmd = ". /root/openrc; swift --os-project-name {0}"\
|
||||
" --os-username {1} --os-password {2} --os-auth-token {3}"\
|
||||
" post {4}".format(tenant, user, password, token, container)
|
||||
ssh.call(["sh", "-c", cmd], node=node)
|
||||
|
||||
|
||||
def sync_glance_images(source_env_id, seed_env_id, seed_swift_ep):
|
||||
"""Sync glance images from original ENV to seed ENV
|
||||
|
||||
Args:
|
||||
source_env_id (int): ID of original ENV.
|
||||
seed_env_id (int): ID of seed ENV.
|
||||
seed_swift_ep (str): endpoint's name where swift-proxy service is
|
||||
listening on.
|
||||
|
||||
Examples:
|
||||
sync_glance_images(2, 3, 'br-mgmt')
|
||||
"""
|
||||
# set glance username
|
||||
glance_user = "glance"
|
||||
# set swift container value
|
||||
container = "glance"
|
||||
# choose tenant
|
||||
tenant = "services"
|
||||
# get clusters by id
|
||||
source_env = environment_obj.Environment(source_env_id)
|
||||
seed_env = environment_obj.Environment(seed_env_id)
|
||||
# gather cics admin IPs
|
||||
source_node = next(env_util.get_controllers(source_env))
|
||||
seed_node = next(env_util.get_controllers(seed_env))
|
||||
# get cics yaml files
|
||||
source_yaml = env_util.get_astute_yaml(source_env, source_node)
|
||||
seed_yaml = env_util.get_astute_yaml(seed_env, seed_node)
|
||||
# get glance passwords
|
||||
source_glance_pass = get_glance_password(source_yaml)
|
||||
seed_glance_pass = get_glance_password(seed_yaml)
|
||||
# get seed node swift ip
|
||||
seed_swift_ip = get_endpoint_ip(seed_swift_ep, seed_yaml)
|
||||
# get service tenant id & lists of objects for source env
|
||||
source_token = get_auth_token(source_node, tenant, glance_user,
|
||||
source_glance_pass)
|
||||
source_swift_list = set(get_swift_objects(source_node,
|
||||
tenant,
|
||||
glance_user,
|
||||
source_glance_pass,
|
||||
source_token,
|
||||
container))
|
||||
# get service tenant id & lists of objects for seed env
|
||||
seed_token = get_auth_token(seed_node, tenant, glance_user,
|
||||
seed_glance_pass)
|
||||
# to be sure that glance container is present for seed env
|
||||
create_container(seed_node, tenant, glance_user, seed_glance_pass,
|
||||
seed_token, container)
|
||||
|
||||
seed_swift_list = set(get_swift_objects(seed_node,
|
||||
tenant,
|
||||
glance_user,
|
||||
seed_glance_pass,
|
||||
seed_token,
|
||||
container))
|
||||
# get service tenant for seed env
|
||||
seed_tenant = env_util.get_service_tenant_id(seed_env)
|
||||
# check consistency of matched images
|
||||
source_token = get_auth_token(source_node, tenant, glance_user,
|
||||
source_glance_pass)
|
||||
seed_token = get_auth_token(seed_node, tenant, glance_user,
|
||||
seed_glance_pass)
|
||||
for image in source_swift_list & seed_swift_list:
|
||||
source_obj_etag = get_object_property(source_node,
|
||||
tenant,
|
||||
glance_user,
|
||||
source_glance_pass,
|
||||
source_token,
|
||||
container,
|
||||
image,
|
||||
'ETag')
|
||||
seed_obj_etag = get_object_property(seed_node, tenant,
|
||||
glance_user, seed_glance_pass,
|
||||
seed_token, container, image,
|
||||
'ETag')
|
||||
if source_obj_etag != seed_obj_etag:
|
||||
# image should be resynced
|
||||
delete_image(seed_node, tenant, glance_user, seed_glance_pass,
|
||||
seed_token, container, image)
|
||||
LOG.info("Swift %s image should be resynced", image)
|
||||
seed_swift_list.remove(image)
|
||||
# migrate new images
|
||||
for image in source_swift_list - seed_swift_list:
|
||||
# download image on source's node local drive
|
||||
source_token = get_auth_token(source_node, tenant, glance_user,
|
||||
source_glance_pass)
|
||||
download_image(source_node, tenant, glance_user, source_glance_pass,
|
||||
source_token, container, image)
|
||||
# transfer image
|
||||
source_token = get_auth_token(source_node, tenant,
|
||||
glance_user, source_glance_pass)
|
||||
seed_token = get_auth_token(seed_node, tenant, glance_user,
|
||||
seed_glance_pass)
|
||||
transfer_image(source_node, tenant, glance_user, seed_glance_pass,
|
||||
seed_token, container, image, seed_swift_ip,
|
||||
seed_tenant)
|
||||
# remove transferred image
|
||||
ssh.sftp(source_node).remove(image)
|
||||
# delete outdated images
|
||||
for image in seed_swift_list - source_swift_list:
|
||||
token = get_auth_token(seed_node, tenant, glance_user,
|
||||
seed_glance_pass)
|
||||
delete_image(seed_node, tenant, glance_user, seed_glance_pass,
|
||||
token, container, image)
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
import os.path
|
||||
|
||||
PACKAGES = ["postgresql.x86_64", "pssh", "patch", "python-pip"]
|
||||
PATCHES = []
|
||||
# TODO: use pkg_resources for patches
|
||||
CWD = os.path.dirname(__file__) # FIXME
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# 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.
|
||||
|
||||
|
||||
def test_prepare_parser(mocker, octane_app):
|
||||
m = mocker.patch('octane.commands.prepare.prepare')
|
||||
octane_app.run(["prepare"])
|
||||
assert not octane_app.stdout.getvalue()
|
||||
assert not octane_app.stderr.getvalue()
|
||||
m.assert_called_once_with()
|
||||
|
||||
|
||||
def test_revert_parser(mocker, octane_app):
|
||||
mock_apply = mocker.patch('octane.commands.prepare.revert_prepare')
|
||||
octane_app.run(["revert-prepare"])
|
||||
assert not octane_app.stdout.getvalue()
|
||||
assert not octane_app.stderr.getvalue()
|
||||
mock_apply.assert_called_once_with()
|
|
@ -1,62 +0,0 @@
|
|||
# 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 mock
|
||||
import pytest
|
||||
|
||||
from octane.helpers import sync_glance_images
|
||||
|
||||
|
||||
def test_parser(mocker, octane_app):
|
||||
m = mocker.patch('octane.commands.sync_images.sync_glance_images')
|
||||
octane_app.run(['sync-images', '1', '2', 'br-mgmt'])
|
||||
assert not octane_app.stdout.getvalue()
|
||||
assert not octane_app.stderr.getvalue()
|
||||
m.assert_called_once_with(1, 2, 'br-mgmt')
|
||||
|
||||
|
||||
def test_prepare_parser(mocker, octane_app):
|
||||
m = mocker.patch('octane.commands.sync_images.prepare')
|
||||
octane_app.run(['sync-images-prepare', '1', '2'])
|
||||
assert not octane_app.stdout.getvalue()
|
||||
assert not octane_app.stderr.getvalue()
|
||||
m.assert_called_once_with(1, 2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("yaml,expected", [
|
||||
({'network_scheme': {'endpoints': {'MY_EP': {'IP': ['1.2.3.4/24']}}}},
|
||||
'1.2.3.4'),
|
||||
({'network_scheme': {'endpoints': {'MY_EP1': {'IP': ['1.2.3.4/24']}}}},
|
||||
None),
|
||||
])
|
||||
def test_get_endpoint_ip(yaml, expected):
|
||||
result = sync_glance_images.get_endpoint_ip('MY_EP', yaml)
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_get_swift_object(mock_subprocess, mock_ssh_call_output, node):
|
||||
mock_ssh_call_output.return_value = 'id1\nid2\n'
|
||||
res = sync_glance_images.get_swift_objects(
|
||||
node, 'tenant', 'user', 'password', 'token', 'container')
|
||||
assert not mock_subprocess.called
|
||||
assert mock_ssh_call_output.call_args_list == [
|
||||
mock.call(["sh", "-c", mock.ANY], node=node)]
|
||||
assert res == ['id1', 'id2']
|
||||
|
||||
|
||||
def test_download_image(mock_subprocess, mock_ssh_call, node):
|
||||
mock_ssh_call.return_value = 'id1\nid2\n'
|
||||
sync_glance_images.download_image(
|
||||
node, 'tenant', 'user', 'password', 'token', 'container', 'id')
|
||||
assert not mock_subprocess.called
|
||||
assert mock_ssh_call.call_args_list == [
|
||||
mock.call(["sh", "-c", mock.ANY], node=node)]
|
|
@ -1,27 +0,0 @@
|
|||
# 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.
|
||||
|
||||
|
||||
def test_parser(mocker, octane_app):
|
||||
networks = [{'key': 'value'}]
|
||||
|
||||
env_cls = mocker.patch('fuelclient.objects.Environment')
|
||||
|
||||
m1 = mocker.patch('octane.util.env.get_env_networks')
|
||||
m1.return_value = networks
|
||||
|
||||
m2 = mocker.patch('octane.commands.sync_networks.update_env_networks')
|
||||
octane_app.run(["sync-networks", "1", "2"])
|
||||
assert not octane_app.stdout.getvalue()
|
||||
assert not octane_app.stderr.getvalue()
|
||||
m1.assert_called_once_with(env_cls.return_value)
|
||||
m2.assert_called_once_with(2, networks)
|
|
@ -1,49 +0,0 @@
|
|||
# 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 mock
|
||||
import pytest
|
||||
|
||||
from octane.commands import update_plugin_settings
|
||||
|
||||
|
||||
def test_parser(mocker, octane_app):
|
||||
m = mocker.patch('octane.commands.update_plugin_settings'
|
||||
'.transfer_plugins_settings')
|
||||
plugins_str = ','.join(update_plugin_settings.PLUGINS)
|
||||
octane_app.run(["update-plugin-settings", "--plugins", plugins_str,
|
||||
"1", "2"])
|
||||
assert not octane_app.stdout.getvalue()
|
||||
assert not octane_app.stderr.getvalue()
|
||||
m.assert_called_once_with(1, 2, update_plugin_settings.PLUGINS.keys())
|
||||
|
||||
|
||||
def test_transfer_plugin_settings(mocker):
|
||||
plugin = mock.Mock()
|
||||
mocker.patch.object(update_plugin_settings, 'PLUGINS', {'plugin': plugin})
|
||||
env_cls = mocker.patch('fuelclient.objects.environment.Environment')
|
||||
get_astute_yaml = mocker.patch('octane.util.env.get_astute_yaml')
|
||||
attrs = {'editable': {'plugin': {}}}
|
||||
env_cls.return_value.get_settings_data.return_value = attrs
|
||||
update_plugin_settings.transfer_plugins_settings(1, 2, ['plugin'])
|
||||
plugin.assert_called_once_with(get_astute_yaml.return_value, {})
|
||||
|
||||
|
||||
def test_transfer_plugin_settings_fail(mocker):
|
||||
plugin = mock.Mock()
|
||||
mocker.patch.object(update_plugin_settings, 'PLUGINS', {'plugin': plugin})
|
||||
env_cls = mocker.patch('fuelclient.objects.environment.Environment')
|
||||
mocker.patch('octane.util.env.get_astute_yaml')
|
||||
attrs = {'editable': {'plugin1': {}}}
|
||||
env_cls.return_value.get_settings_data.return_value = attrs
|
||||
with pytest.raises(update_plugin_settings.PluginNotConfigured):
|
||||
update_plugin_settings.transfer_plugins_settings(1, 2, ['plugin'])
|
11
setup.cfg
11
setup.cfg
|
@ -23,16 +23,10 @@ classifier =
|
|||
packages =
|
||||
octane
|
||||
|
||||
[extras]
|
||||
zabbix =
|
||||
pyzabbix==0.7.3
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
octane = octane.app:main
|
||||
octane =
|
||||
prepare = octane.commands.prepare:PrepareCommand
|
||||
revert-prepare = octane.commands.prepare:RevertCommand
|
||||
upgrade-env = octane.commands.upgrade_env:UpgradeEnvCommand
|
||||
upgrade-node = octane.commands.upgrade_node:UpgradeNodeCommand
|
||||
upgrade-db = octane.commands.upgrade_db:UpgradeDBCommand
|
||||
|
@ -40,11 +34,6 @@ octane =
|
|||
install-node = octane.commands.install_node:InstallNodeCommand
|
||||
upgrade-control = octane.commands.upgrade_controlplane:UpgradeControlPlaneCommand
|
||||
rollback-control = octane.commands.rollback_controlplane:RollbackControlPlaneCommand
|
||||
sync-networks = octane.commands.sync_networks:SyncNetworksCommand
|
||||
cleanup = octane.commands.cleanup:CleanupCommand
|
||||
sync-images = octane.commands.sync_images:SyncImagesCommand
|
||||
sync-images-prepare = octane.commands.sync_images:SyncImagesPrepareCommand
|
||||
update-plugin-settings = octane.commands.update_plugin_settings:UpdatePluginSettingsCommand [zabbix]
|
||||
fuel-backup = octane.commands.backup:BackupCommand
|
||||
fuel-restore = octane.commands.restore:RestoreCommand
|
||||
fuel-repo-backup = octane.commands.backup:BackupRepoCommand
|
||||
|
|
Loading…
Reference in New Issue