02adbab969
The action will run `nova-manage db purge --verbose --all` to delete rows from shadow tables. Related-Bug: 2066940 Change-Id: I4602a9cf38126b50bfe188b68a75cff1d2597342
230 lines
7.8 KiB
Python
Executable File
230 lines
7.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Copyright 2016 Canonical Ltd
|
|
#
|
|
# 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 sys
|
|
|
|
_path = os.path.dirname(os.path.realpath(__file__))
|
|
_root = os.path.abspath(os.path.join(_path, '..'))
|
|
|
|
|
|
def _add_path(path):
|
|
if path not in sys.path:
|
|
sys.path.insert(1, path)
|
|
|
|
|
|
_add_path(_root)
|
|
|
|
|
|
import charmhelpers.core.hookenv as hookenv
|
|
import charmhelpers.contrib.openstack.utils as ch_utils
|
|
import hooks.nova_cc_utils as utils
|
|
import hooks.nova_cc_hooks as ncc_hooks
|
|
|
|
|
|
def pause(args):
|
|
"""Pause the Ceilometer services.
|
|
@raises Exception should the service fail to stop.
|
|
"""
|
|
utils.pause_unit_helper(utils.register_configs())
|
|
|
|
|
|
def resume(args):
|
|
"""Resume the Ceilometer services.
|
|
@raises Exception should the service fail to start."""
|
|
utils.resume_unit_helper(utils.register_configs())
|
|
|
|
|
|
def purge_data(args):
|
|
"""Run data purge process
|
|
@raises Exception should the purge fail"""
|
|
hookenv.action_set({
|
|
'output': utils.purge_stale_soft_deleted_rows(
|
|
before=hookenv.action_get('before'),
|
|
)
|
|
})
|
|
|
|
|
|
def archive_data(args):
|
|
"""Run data archival process
|
|
@raises Exception should the archival fail"""
|
|
hookenv.action_set({
|
|
'archive-deleted-rows': utils.archive_deleted_rows(
|
|
max_rows=hookenv.action_get('batch-size'))})
|
|
|
|
|
|
def clear_unit_knownhost_cache(args):
|
|
"""Clear the knownhost cache for a unit (or all units), and then refresh
|
|
the knownhosts, and potentially set the relation data for the associated
|
|
service.
|
|
|
|
If the target param doesn't match any unit or service, then the action does
|
|
nothing.
|
|
"""
|
|
target = hookenv.action_get('target')
|
|
hookenv.action_set({
|
|
"units-updated": clear_knownhost_cache(target)
|
|
})
|
|
|
|
|
|
def clear_knownhost_cache(target):
|
|
"""Clear the known host cache for a target, rescan the affected units,
|
|
and then update the knownhosts file for the affected service(s) and set the
|
|
appropriate relation data.
|
|
|
|
Examples of target are:
|
|
- "" = all services, all units (clear all the caches)
|
|
- "aservice" = clear all the units' caches on 'aservice'
|
|
- "aservice/4" = just clear this specific unit and update the relation on
|
|
that service.
|
|
|
|
Note that if target doesn't match anything, then the function takes no
|
|
action and no Exception is raised.
|
|
|
|
:param target: The target to clear.
|
|
:type target: str
|
|
:returns: a list of units that were affected.
|
|
:rtype: List[Dict[str, str]]
|
|
"""
|
|
affected_units = []
|
|
|
|
parts = target.split('/', 1)
|
|
target_service = parts[0]
|
|
is_unit = len(parts) > 1
|
|
|
|
for r_id in hookenv.relation_ids('cloud-compute'):
|
|
units = hookenv.related_units(r_id)
|
|
if not units:
|
|
continue
|
|
service = utils.remote_service_from_unit(unit=units[0])
|
|
if target_service and service != target_service:
|
|
continue
|
|
|
|
updated = False
|
|
for unit in units:
|
|
if is_unit and unit != target:
|
|
continue
|
|
private_address = hookenv.relation_get(
|
|
attribute='private-address', unit=unit, rid=r_id)
|
|
if private_address:
|
|
utils.clear_hostset_cache_for(private_address)
|
|
ncc_hooks.update_ssh_key(rid=r_id, unit=unit)
|
|
updated = True
|
|
affected_units.append({unit: private_address})
|
|
|
|
# Note that this uses the last unit in the relation; that's ok as it's
|
|
# only used to identify the service
|
|
if updated:
|
|
ncc_hooks.notify_ssh_keys_to_compute_units(rid=r_id, unit=unit)
|
|
|
|
return affected_units
|
|
|
|
|
|
def sync_compute_availability_zones(args):
|
|
"""Sync the nova-compute Juju units' availability zones with the OpenStack
|
|
hypervisors' availability zones."""
|
|
# Due to python3 issues, we do a check here to see which version of
|
|
# OpenStack is installed and gate the availability of the action on
|
|
# that. See note below.
|
|
release = ch_utils.CompareOpenStackReleases(
|
|
ch_utils.os_release('nova-common'))
|
|
if release < 'stein':
|
|
msg = ('The sync_compute_availability_zones action is not available'
|
|
'for the {} release.'.format(release))
|
|
hookenv.action_fail(msg)
|
|
return
|
|
|
|
# Note (wolsen): There's a problem with the action script using only
|
|
# python3 (/usr/bin/env python3) above, however on versions lower than
|
|
# rocky, the python2 versions of the following python packages are
|
|
# installed. The imports are moved to here to avoid causing actions
|
|
# to fail outright.
|
|
import hooks.nova_cc_context as ncc_context
|
|
from keystoneauth1 import session
|
|
from keystoneauth1.identity import v3
|
|
from novaclient import client as nova_client
|
|
from novaclient import exceptions as nova_exceptions
|
|
|
|
ctxt = ncc_context.IdentityServiceContext()()
|
|
if not ctxt:
|
|
hookenv.action_fail("Identity service context cannot be generated")
|
|
return
|
|
|
|
keystone_auth = ctxt['keystone_authtoken']
|
|
keystone_creds = {
|
|
'auth_url': keystone_auth.get('auth_url'),
|
|
'username': keystone_auth.get('username'),
|
|
'password': keystone_auth.get('password'),
|
|
'user_domain_name': keystone_auth.get('user_domain_name'),
|
|
'project_domain_name': keystone_auth.get('project_domain_name'),
|
|
'project_name': keystone_auth.get('project_name'),
|
|
}
|
|
keystone_session = session.Session(auth=v3.Password(**keystone_creds))
|
|
client = nova_client.Client(2, session=keystone_session)
|
|
output_str = ''
|
|
for r_id in hookenv.relation_ids('cloud-compute'):
|
|
units = hookenv.related_units(r_id)
|
|
for unit in units:
|
|
rel_data = hookenv.relation_get(rid=r_id, unit=unit)
|
|
unit_az = rel_data.get('availability_zone')
|
|
if not unit_az:
|
|
continue
|
|
aggregate_name = '{}_az'.format(unit_az)
|
|
try:
|
|
aggregate = client.aggregates.find(
|
|
name=aggregate_name, availability_zone=unit_az)
|
|
except nova_exceptions.NotFound:
|
|
aggregate = client.aggregates.create(
|
|
aggregate_name, availability_zone=unit_az)
|
|
unit_ip = rel_data.get('private-address')
|
|
hypervisor = client.hypervisors.find(host_ip=unit_ip)
|
|
if hypervisor.hypervisor_hostname not in aggregate.hosts:
|
|
client.aggregates.add_host(
|
|
aggregate, hypervisor.hypervisor_hostname)
|
|
output_str += \
|
|
"Hypervisor {} added to availability zone {}\n".format(
|
|
hypervisor.hypervisor_hostname, unit_az)
|
|
hookenv.action_set({'output': output_str})
|
|
|
|
|
|
# A dictionary of all the defined actions to callables (which take
|
|
# parsed arguments).
|
|
ACTIONS = {
|
|
"pause": pause,
|
|
"resume": resume,
|
|
"archive-data": archive_data,
|
|
"clear-unit-knownhost-cache": clear_unit_knownhost_cache,
|
|
"sync-compute-availability-zones": sync_compute_availability_zones,
|
|
"purge-data": purge_data,
|
|
}
|
|
|
|
|
|
def main(args):
|
|
action_name = os.path.basename(args[0])
|
|
try:
|
|
action = ACTIONS[action_name]
|
|
except KeyError:
|
|
return "Action %s undefined" % action_name
|
|
else:
|
|
try:
|
|
action(args)
|
|
except Exception as e:
|
|
hookenv.action_fail(str(e))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv))
|