Enhanced pause/resume for maintenance mode
Implemented pause/resume with some services checking. Charm-helpers sync to bring in pause/resume support. Change-Id: I8bcd7464a606f6fc9db374be6004d851935e749a
This commit is contained in:
parent
da4f012a9b
commit
914e639b52
|
@ -2,3 +2,7 @@ git-reinstall:
|
||||||
description: Reinstall nova-compute from the openstack-origin-git repositories.
|
description: Reinstall nova-compute from the openstack-origin-git repositories.
|
||||||
openstack-upgrade:
|
openstack-upgrade:
|
||||||
description: Perform openstack upgrades. Config option action-managed-upgrade must be set to True.
|
description: Perform openstack upgrades. Config option action-managed-upgrade must be set to True.
|
||||||
|
pause:
|
||||||
|
description: Pause the nova_compute unit. This action will stop nova_compute services.
|
||||||
|
resume:
|
||||||
|
descrpition: Resume the nova_compute unit. This action will start nova_compute services.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
pause_resume.py
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('hooks')
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import action_fail
|
||||||
|
from nova_compute_utils import (
|
||||||
|
pause_unit_helper,
|
||||||
|
resume_unit_helper,
|
||||||
|
register_configs,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pause(args):
|
||||||
|
"""Pause the Ceilometer services.
|
||||||
|
@raises Exception should the service fail to stop.
|
||||||
|
"""
|
||||||
|
pause_unit_helper(register_configs())
|
||||||
|
|
||||||
|
|
||||||
|
def resume(args):
|
||||||
|
"""Resume the Ceilometer services.
|
||||||
|
@raises Exception should the service fail to start."""
|
||||||
|
resume_unit_helper(register_configs())
|
||||||
|
|
||||||
|
|
||||||
|
# A dictionary of all the defined actions to callables (which take
|
||||||
|
# parsed arguments).
|
||||||
|
ACTIONS = {"pause": pause, "resume": resume}
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
action_name = os.path.basename(args[0])
|
||||||
|
try:
|
||||||
|
action = ACTIONS[action_name]
|
||||||
|
except KeyError:
|
||||||
|
s = "Action {} undefined".format(action_name)
|
||||||
|
action_fail(s)
|
||||||
|
return s
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
action(args)
|
||||||
|
except Exception as e:
|
||||||
|
action_fail("Action {} failed: {}".format(action_name, str(e)))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv))
|
|
@ -0,0 +1 @@
|
||||||
|
pause_resume.py
|
|
@ -191,6 +191,15 @@ get_iface_for_address = partial(_get_for_address, key='iface')
|
||||||
get_netmask_for_address = partial(_get_for_address, key='netmask')
|
get_netmask_for_address = partial(_get_for_address, key='netmask')
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_network_cidr(ip_address):
|
||||||
|
'''
|
||||||
|
Resolves the full address cidr of an ip_address based on
|
||||||
|
configured network interfaces
|
||||||
|
'''
|
||||||
|
netmask = get_netmask_for_address(ip_address)
|
||||||
|
return str(netaddr.IPNetwork("%s/%s" % (ip_address, netmask)).cidr)
|
||||||
|
|
||||||
|
|
||||||
def format_ipv6_addr(address):
|
def format_ipv6_addr(address):
|
||||||
"""If address is IPv6, wrap it in '[]' otherwise return None.
|
"""If address is IPv6, wrap it in '[]' otherwise return None.
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
||||||
# Charms which can not use openstack-origin, ie. many subordinates
|
# Charms which can not use openstack-origin, ie. many subordinates
|
||||||
no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe',
|
no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe',
|
||||||
'openvswitch-odl', 'neutron-api-odl', 'odl-controller',
|
'openvswitch-odl', 'neutron-api-odl', 'odl-controller',
|
||||||
'cinder-backup']
|
'cinder-backup', 'nexentaedge-data',
|
||||||
|
'nexentaedge-iscsi-gw', 'nexentaedge-swift-gw',
|
||||||
|
'cinder-nexentaedge', 'nexentaedge-mgmt']
|
||||||
|
|
||||||
if self.openstack:
|
if self.openstack:
|
||||||
for svc in services:
|
for svc in services:
|
||||||
|
|
|
@ -14,16 +14,19 @@
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
config,
|
config,
|
||||||
unit_get,
|
unit_get,
|
||||||
service_name,
|
service_name,
|
||||||
|
network_get_primary_address,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.network.ip import (
|
from charmhelpers.contrib.network.ip import (
|
||||||
get_address_in_network,
|
get_address_in_network,
|
||||||
is_address_in_network,
|
is_address_in_network,
|
||||||
is_ipv6,
|
is_ipv6,
|
||||||
get_ipv6_addr,
|
get_ipv6_addr,
|
||||||
|
resolve_network_cidr,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.hahelpers.cluster import is_clustered
|
from charmhelpers.contrib.hahelpers.cluster import is_clustered
|
||||||
|
|
||||||
|
@ -33,16 +36,19 @@ ADMIN = 'admin'
|
||||||
|
|
||||||
ADDRESS_MAP = {
|
ADDRESS_MAP = {
|
||||||
PUBLIC: {
|
PUBLIC: {
|
||||||
|
'binding': 'public',
|
||||||
'config': 'os-public-network',
|
'config': 'os-public-network',
|
||||||
'fallback': 'public-address',
|
'fallback': 'public-address',
|
||||||
'override': 'os-public-hostname',
|
'override': 'os-public-hostname',
|
||||||
},
|
},
|
||||||
INTERNAL: {
|
INTERNAL: {
|
||||||
|
'binding': 'internal',
|
||||||
'config': 'os-internal-network',
|
'config': 'os-internal-network',
|
||||||
'fallback': 'private-address',
|
'fallback': 'private-address',
|
||||||
'override': 'os-internal-hostname',
|
'override': 'os-internal-hostname',
|
||||||
},
|
},
|
||||||
ADMIN: {
|
ADMIN: {
|
||||||
|
'binding': 'admin',
|
||||||
'config': 'os-admin-network',
|
'config': 'os-admin-network',
|
||||||
'fallback': 'private-address',
|
'fallback': 'private-address',
|
||||||
'override': 'os-admin-hostname',
|
'override': 'os-admin-hostname',
|
||||||
|
@ -110,7 +116,7 @@ def resolve_address(endpoint_type=PUBLIC):
|
||||||
correct network. If clustered with no nets defined, return primary vip.
|
correct network. If clustered with no nets defined, return primary vip.
|
||||||
|
|
||||||
If not clustered, return unit address ensuring address is on configured net
|
If not clustered, return unit address ensuring address is on configured net
|
||||||
split if one is configured.
|
split if one is configured, or a Juju 2.0 extra-binding has been used.
|
||||||
|
|
||||||
:param endpoint_type: Network endpoing type
|
:param endpoint_type: Network endpoing type
|
||||||
"""
|
"""
|
||||||
|
@ -125,23 +131,45 @@ def resolve_address(endpoint_type=PUBLIC):
|
||||||
net_type = ADDRESS_MAP[endpoint_type]['config']
|
net_type = ADDRESS_MAP[endpoint_type]['config']
|
||||||
net_addr = config(net_type)
|
net_addr = config(net_type)
|
||||||
net_fallback = ADDRESS_MAP[endpoint_type]['fallback']
|
net_fallback = ADDRESS_MAP[endpoint_type]['fallback']
|
||||||
|
binding = ADDRESS_MAP[endpoint_type]['binding']
|
||||||
clustered = is_clustered()
|
clustered = is_clustered()
|
||||||
if clustered:
|
|
||||||
if not net_addr:
|
if clustered and vips:
|
||||||
# If no net-splits defined, we expect a single vip
|
if net_addr:
|
||||||
resolved_address = vips[0]
|
|
||||||
else:
|
|
||||||
for vip in vips:
|
for vip in vips:
|
||||||
if is_address_in_network(net_addr, vip):
|
if is_address_in_network(net_addr, vip):
|
||||||
resolved_address = vip
|
resolved_address = vip
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
# NOTE: endeavour to check vips against network space
|
||||||
|
# bindings
|
||||||
|
try:
|
||||||
|
bound_cidr = resolve_network_cidr(
|
||||||
|
network_get_primary_address(binding)
|
||||||
|
)
|
||||||
|
for vip in vips:
|
||||||
|
if is_address_in_network(bound_cidr, vip):
|
||||||
|
resolved_address = vip
|
||||||
|
break
|
||||||
|
except NotImplementedError:
|
||||||
|
# If no net-splits configured and no support for extra
|
||||||
|
# bindings/network spaces so we expect a single vip
|
||||||
|
resolved_address = vips[0]
|
||||||
else:
|
else:
|
||||||
if config('prefer-ipv6'):
|
if config('prefer-ipv6'):
|
||||||
fallback_addr = get_ipv6_addr(exc_list=vips)[0]
|
fallback_addr = get_ipv6_addr(exc_list=vips)[0]
|
||||||
else:
|
else:
|
||||||
fallback_addr = unit_get(net_fallback)
|
fallback_addr = unit_get(net_fallback)
|
||||||
|
|
||||||
|
if net_addr:
|
||||||
resolved_address = get_address_in_network(net_addr, fallback_addr)
|
resolved_address = get_address_in_network(net_addr, fallback_addr)
|
||||||
|
else:
|
||||||
|
# NOTE: only try to use extra bindings if legacy network
|
||||||
|
# configuration is not in use
|
||||||
|
try:
|
||||||
|
resolved_address = network_get_primary_address(binding)
|
||||||
|
except NotImplementedError:
|
||||||
|
resolved_address = fallback_addr
|
||||||
|
|
||||||
if resolved_address is None:
|
if resolved_address is None:
|
||||||
raise ValueError("Unable to resolve a suitable IP address based on "
|
raise ValueError("Unable to resolve a suitable IP address based on "
|
||||||
|
|
|
@ -19,7 +19,8 @@ from charmhelpers.core.hookenv import (
|
||||||
from charmhelpers.contrib.openstack.utils import (
|
from charmhelpers.contrib.openstack.utils import (
|
||||||
get_host_ip,
|
get_host_ip,
|
||||||
get_os_version_package,
|
get_os_version_package,
|
||||||
get_os_version_codename
|
get_os_version_codename,
|
||||||
|
is_unit_paused_set,
|
||||||
)
|
)
|
||||||
from charmhelpers.contrib.network.ovs import add_bridge
|
from charmhelpers.contrib.network.ovs import add_bridge
|
||||||
|
|
||||||
|
@ -487,6 +488,9 @@ class NeutronComputeContext(context.NeutronContext):
|
||||||
return _neutron_security_groups()
|
return _neutron_security_groups()
|
||||||
|
|
||||||
def _ensure_bridge(self):
|
def _ensure_bridge(self):
|
||||||
|
# NOTE(ajkavanagh) as this is called during the context usage (via
|
||||||
|
# __call__()) it should get executed when the unit comes off pause.
|
||||||
|
if not is_unit_paused_set():
|
||||||
if not service_running('openvswitch-switch'):
|
if not service_running('openvswitch-switch'):
|
||||||
service_start('openvswitch-switch')
|
service_start('openvswitch-switch')
|
||||||
add_bridge(OVS_BRIDGE)
|
add_bridge(OVS_BRIDGE)
|
||||||
|
|
|
@ -17,7 +17,6 @@ from charmhelpers.core.hookenv import (
|
||||||
status_set,
|
status_set,
|
||||||
)
|
)
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
restart_on_change,
|
|
||||||
service_restart,
|
service_restart,
|
||||||
)
|
)
|
||||||
from charmhelpers.core.strutils import (
|
from charmhelpers.core.strutils import (
|
||||||
|
@ -36,7 +35,8 @@ from charmhelpers.contrib.openstack.utils import (
|
||||||
git_install_requested,
|
git_install_requested,
|
||||||
openstack_upgrade_available,
|
openstack_upgrade_available,
|
||||||
os_requires_version,
|
os_requires_version,
|
||||||
set_os_workload_status,
|
is_unit_paused_set,
|
||||||
|
pausable_restart_on_change as restart_on_change,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.storage.linux.ceph import (
|
from charmhelpers.contrib.storage.linux.ceph import (
|
||||||
|
@ -71,9 +71,8 @@ from nova_compute_utils import (
|
||||||
assert_charm_supports_ipv6,
|
assert_charm_supports_ipv6,
|
||||||
manage_ovs,
|
manage_ovs,
|
||||||
install_hugepages,
|
install_hugepages,
|
||||||
REQUIRED_INTERFACES,
|
|
||||||
check_optional_relations,
|
|
||||||
get_hugepage_number,
|
get_hugepage_number,
|
||||||
|
assess_status,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.network.ip import (
|
from charmhelpers.contrib.network.ip import (
|
||||||
|
@ -287,6 +286,7 @@ def ceph_joined():
|
||||||
status_set('maintenance', 'Installing apt packages')
|
status_set('maintenance', 'Installing apt packages')
|
||||||
apt_install(filter_installed_packages(['ceph-common']), fatal=True)
|
apt_install(filter_installed_packages(['ceph-common']), fatal=True)
|
||||||
# Bug 1427660
|
# Bug 1427660
|
||||||
|
if not is_unit_paused_set():
|
||||||
service_restart('libvirt-bin')
|
service_restart('libvirt-bin')
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,7 +325,8 @@ def ceph_changed(rid=None, unit=None):
|
||||||
if is_request_complete(get_ceph_request()):
|
if is_request_complete(get_ceph_request()):
|
||||||
log('Request complete')
|
log('Request complete')
|
||||||
# Ensure that nova-compute is restarted since only now can we
|
# Ensure that nova-compute is restarted since only now can we
|
||||||
# guarantee that ceph resources are ready.
|
# guarantee that ceph resources are ready, but only if not paused.
|
||||||
|
if not is_unit_paused_set():
|
||||||
service_restart('nova-compute')
|
service_restart('nova-compute')
|
||||||
else:
|
else:
|
||||||
send_request_if_needed(get_ceph_request())
|
send_request_if_needed(get_ceph_request())
|
||||||
|
@ -428,6 +429,7 @@ def lxc_changed():
|
||||||
if nonce and db.get('lxd-nonce') != nonce:
|
if nonce and db.get('lxd-nonce') != nonce:
|
||||||
db.set('lxd-nonce', nonce)
|
db.set('lxd-nonce', nonce)
|
||||||
configure_lxd(user='nova')
|
configure_lxd(user='nova')
|
||||||
|
if not is_unit_paused_set():
|
||||||
service_restart('nova-compute')
|
service_restart('nova-compute')
|
||||||
|
|
||||||
|
|
||||||
|
@ -448,8 +450,7 @@ def main():
|
||||||
hooks.execute(sys.argv)
|
hooks.execute(sys.argv)
|
||||||
except UnregisteredHookError as e:
|
except UnregisteredHookError as e:
|
||||||
log('Unknown hook {} - skipping.'.format(e))
|
log('Unknown hook {} - skipping.'.format(e))
|
||||||
set_os_workload_status(CONFIGS, REQUIRED_INTERFACES,
|
assess_status(CONFIGS)
|
||||||
charm_func=check_optional_relations)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -37,7 +37,6 @@ from charmhelpers.core.hookenv import (
|
||||||
relation_get,
|
relation_get,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
INFO,
|
INFO,
|
||||||
status_get,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.core.templating import render
|
from charmhelpers.core.templating import render
|
||||||
|
@ -55,7 +54,10 @@ from charmhelpers.contrib.openstack.utils import (
|
||||||
git_pip_venv_dir,
|
git_pip_venv_dir,
|
||||||
git_yaml_value,
|
git_yaml_value,
|
||||||
os_release,
|
os_release,
|
||||||
set_os_workload_status,
|
is_unit_paused_set,
|
||||||
|
make_assess_status_func,
|
||||||
|
pause_unit,
|
||||||
|
resume_unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.contrib.python.packages import (
|
from charmhelpers.contrib.python.packages import (
|
||||||
|
@ -538,7 +540,9 @@ def do_openstack_upgrade(configs):
|
||||||
|
|
||||||
configs.set_release(openstack_release=new_os_rel)
|
configs.set_release(openstack_release=new_os_rel)
|
||||||
configs.write_all()
|
configs.write_all()
|
||||||
[service_restart(s) for s in services()]
|
if not is_unit_paused_set():
|
||||||
|
for s in services():
|
||||||
|
service_restart(s)
|
||||||
|
|
||||||
|
|
||||||
def import_keystone_ca_cert():
|
def import_keystone_ca_cert():
|
||||||
|
@ -837,19 +841,84 @@ def install_hugepages():
|
||||||
subprocess.check_call(['update-rc.d', 'qemu-hugefsdir', 'defaults'])
|
subprocess.check_call(['update-rc.d', 'qemu-hugefsdir', 'defaults'])
|
||||||
|
|
||||||
|
|
||||||
def check_optional_relations(configs):
|
def get_optional_relations():
|
||||||
required_interfaces = {}
|
"""Return a dictionary of optional relations.
|
||||||
|
|
||||||
|
@returns {relation: relation_name}
|
||||||
|
"""
|
||||||
|
optional_interfaces = {}
|
||||||
if relation_ids('ceph'):
|
if relation_ids('ceph'):
|
||||||
required_interfaces['storage-backend'] = ['ceph']
|
optional_interfaces['storage-backend'] = ['ceph']
|
||||||
|
|
||||||
if relation_ids('neutron-plugin'):
|
if relation_ids('neutron-plugin'):
|
||||||
required_interfaces['neutron-plugin'] = ['neutron-plugin']
|
optional_interfaces['neutron-plugin'] = ['neutron-plugin']
|
||||||
|
|
||||||
if relation_ids('shared-db') or relation_ids('pgsql-db'):
|
if relation_ids('shared-db') or relation_ids('pgsql-db'):
|
||||||
required_interfaces['database'] = ['shared-db', 'pgsql-db']
|
optional_interfaces['database'] = ['shared-db', 'pgsql-db']
|
||||||
|
return optional_interfaces
|
||||||
|
|
||||||
if required_interfaces:
|
|
||||||
set_os_workload_status(configs, required_interfaces)
|
def assess_status(configs):
|
||||||
return status_get()
|
"""Assess status of current unit
|
||||||
else:
|
Decides what the state of the unit should be based on the current
|
||||||
return 'unknown', 'No optional relations'
|
configuration.
|
||||||
|
SIDE EFFECT: calls set_os_workload_status(...) which sets the workload
|
||||||
|
status of the unit.
|
||||||
|
Also calls status_set(...) directly if paused state isn't complete.
|
||||||
|
@param configs: a templating.OSConfigRenderer() object
|
||||||
|
@returns None - this function is executed for its side-effect
|
||||||
|
"""
|
||||||
|
assess_status_func(configs)()
|
||||||
|
|
||||||
|
|
||||||
|
def assess_status_func(configs):
|
||||||
|
"""Helper function to create the function that will assess_status() for
|
||||||
|
the unit.
|
||||||
|
Uses charmhelpers.contrib.openstack.utils.make_assess_status_func() to
|
||||||
|
create the appropriate status function and then returns it.
|
||||||
|
Used directly by assess_status() and also for pausing and resuming
|
||||||
|
the unit.
|
||||||
|
|
||||||
|
NOTE(ajkavanagh) ports are not checked due to race hazards with services
|
||||||
|
that don't behave sychronously w.r.t their service scripts. e.g.
|
||||||
|
apache2.
|
||||||
|
@param configs: a templating.OSConfigRenderer() object
|
||||||
|
@return f() -> None : a function that assesses the unit's workload status
|
||||||
|
"""
|
||||||
|
required_interfaces = REQUIRED_INTERFACES.copy()
|
||||||
|
required_interfaces.update(get_optional_relations())
|
||||||
|
return make_assess_status_func(
|
||||||
|
configs, required_interfaces,
|
||||||
|
services=services(), ports=None)
|
||||||
|
|
||||||
|
|
||||||
|
def pause_unit_helper(configs):
|
||||||
|
"""Helper function to pause a unit, and then call assess_status(...) in
|
||||||
|
effect, so that the status is correctly updated.
|
||||||
|
Uses charmhelpers.contrib.openstack.utils.pause_unit() to do the work.
|
||||||
|
@param configs: a templating.OSConfigRenderer() object
|
||||||
|
@returns None - this function is executed for its side-effect
|
||||||
|
"""
|
||||||
|
_pause_resume_helper(pause_unit, configs)
|
||||||
|
|
||||||
|
|
||||||
|
def resume_unit_helper(configs):
|
||||||
|
"""Helper function to resume a unit, and then call assess_status(...) in
|
||||||
|
effect, so that the status is correctly updated.
|
||||||
|
Uses charmhelpers.contrib.openstack.utils.resume_unit() to do the work.
|
||||||
|
@param configs: a templating.OSConfigRenderer() object
|
||||||
|
@returns None - this function is executed for its side-effect
|
||||||
|
"""
|
||||||
|
_pause_resume_helper(resume_unit, configs)
|
||||||
|
|
||||||
|
|
||||||
|
def _pause_resume_helper(f, configs):
|
||||||
|
"""Helper function that uses the make_assess_status_func(...) from
|
||||||
|
charmhelpers.contrib.openstack.utils to create an assess_status(...)
|
||||||
|
function that can be used with the pause/resume of the unit
|
||||||
|
@param f: the function to be used with the assess_status(...) function
|
||||||
|
@returns None - this function is executed for its side-effect
|
||||||
|
"""
|
||||||
|
# TODO(ajkavanagh) - ports= has been left off because of the race hazard
|
||||||
|
# that exists due to service_start()
|
||||||
|
f(assess_status_func(configs),
|
||||||
|
services=services(),
|
||||||
|
ports=None)
|
||||||
|
|
|
@ -551,3 +551,19 @@ class NovaBasicDeployment(OpenStackAmuletDeployment):
|
||||||
sleep_time = 0
|
sleep_time = 0
|
||||||
|
|
||||||
self.d.configure(juju_service, set_default)
|
self.d.configure(juju_service, set_default)
|
||||||
|
|
||||||
|
def test_910_pause_and_resume(self):
|
||||||
|
"""The services can be paused and resumed. """
|
||||||
|
u.log.debug('Checking pause and resume actions...')
|
||||||
|
sentry_unit = self.nova_compute_sentry
|
||||||
|
|
||||||
|
assert u.status_get(sentry_unit)[0] == "active"
|
||||||
|
|
||||||
|
action_id = u.run_action(sentry_unit, "pause")
|
||||||
|
assert u.wait_on_action(action_id), "Pause action failed."
|
||||||
|
assert u.status_get(sentry_unit)[0] == "maintenance"
|
||||||
|
|
||||||
|
action_id = u.run_action(sentry_unit, "resume")
|
||||||
|
assert u.wait_on_action(action_id), "Resume action failed."
|
||||||
|
assert u.status_get(sentry_unit)[0] == "active"
|
||||||
|
u.log.debug('OK')
|
||||||
|
|
|
@ -126,7 +126,9 @@ class OpenStackAmuletDeployment(AmuletDeployment):
|
||||||
# Charms which can not use openstack-origin, ie. many subordinates
|
# Charms which can not use openstack-origin, ie. many subordinates
|
||||||
no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe',
|
no_origin = ['cinder-ceph', 'hacluster', 'neutron-openvswitch', 'nrpe',
|
||||||
'openvswitch-odl', 'neutron-api-odl', 'odl-controller',
|
'openvswitch-odl', 'neutron-api-odl', 'odl-controller',
|
||||||
'cinder-backup']
|
'cinder-backup', 'nexentaedge-data',
|
||||||
|
'nexentaedge-iscsi-gw', 'nexentaedge-swift-gw',
|
||||||
|
'cinder-nexentaedge', 'nexentaedge-mgmt']
|
||||||
|
|
||||||
if self.openstack:
|
if self.openstack:
|
||||||
for svc in services:
|
for svc in services:
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import mock
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
|
from test_utils import CharmTestCase
|
||||||
|
|
||||||
|
with patch('nova_compute_utils.register_configs') as configs:
|
||||||
|
configs.return_value = 'test-config'
|
||||||
|
import pause_resume as actions
|
||||||
|
|
||||||
|
|
||||||
|
class PauseTestCase(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PauseTestCase, self).setUp(
|
||||||
|
actions, ["pause_unit_helper"])
|
||||||
|
|
||||||
|
def test_pauses_services(self):
|
||||||
|
actions.pause([])
|
||||||
|
self.pause_unit_helper.assert_called_once_with('test-config')
|
||||||
|
|
||||||
|
|
||||||
|
class ResumeTestCase(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ResumeTestCase, self).setUp(
|
||||||
|
actions, ["resume_unit_helper"])
|
||||||
|
|
||||||
|
def test_pauses_services(self):
|
||||||
|
actions.resume([])
|
||||||
|
self.resume_unit_helper.assert_called_once_with('test-config')
|
||||||
|
|
||||||
|
|
||||||
|
class MainTestCase(CharmTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(MainTestCase, self).setUp(actions, ["action_fail"])
|
||||||
|
|
||||||
|
def test_invokes_action(self):
|
||||||
|
dummy_calls = []
|
||||||
|
|
||||||
|
def dummy_action(args):
|
||||||
|
dummy_calls.append(True)
|
||||||
|
|
||||||
|
with mock.patch.dict(actions.ACTIONS, {"foo": dummy_action}):
|
||||||
|
actions.main(["foo"])
|
||||||
|
self.assertEqual(dummy_calls, [True])
|
||||||
|
|
||||||
|
def test_unknown_action(self):
|
||||||
|
"""Unknown actions aren't a traceback."""
|
||||||
|
exit_string = actions.main(["foo"])
|
||||||
|
self.assertEqual("Action foo undefined", exit_string)
|
||||||
|
|
||||||
|
def test_failing_action(self):
|
||||||
|
"""Actions which traceback trigger action_fail() calls."""
|
||||||
|
dummy_calls = []
|
||||||
|
|
||||||
|
self.action_fail.side_effect = dummy_calls.append
|
||||||
|
|
||||||
|
def dummy_action(args):
|
||||||
|
raise ValueError("uh oh")
|
||||||
|
|
||||||
|
with mock.patch.dict(actions.ACTIONS, {"foo": dummy_action}):
|
||||||
|
actions.main(["foo"])
|
||||||
|
self.assertEqual(dummy_calls, ["Action foo failed: uh oh"])
|
|
@ -711,3 +711,51 @@ class NovaComputeUtilsTests(CharmTestCase):
|
||||||
_call.return_value = 0
|
_call.return_value = 0
|
||||||
utils.install_hugepages()
|
utils.install_hugepages()
|
||||||
self.assertEqual(self.fstab_mount.call_args_list, [])
|
self.assertEqual(self.fstab_mount.call_args_list, [])
|
||||||
|
|
||||||
|
def test_assess_status(self):
|
||||||
|
with patch.object(utils, 'assess_status_func') as asf:
|
||||||
|
callee = MagicMock()
|
||||||
|
asf.return_value = callee
|
||||||
|
utils.assess_status('test-config')
|
||||||
|
asf.assert_called_once_with('test-config')
|
||||||
|
callee.assert_called_once_with()
|
||||||
|
|
||||||
|
@patch.object(utils, 'REQUIRED_INTERFACES')
|
||||||
|
@patch.object(utils, 'services')
|
||||||
|
@patch.object(utils, 'make_assess_status_func')
|
||||||
|
@patch.object(utils, 'get_optional_relations')
|
||||||
|
def test_assess_status_func(self,
|
||||||
|
get_optional_relations,
|
||||||
|
make_assess_status_func,
|
||||||
|
services,
|
||||||
|
REQUIRED_INTERFACES):
|
||||||
|
services.return_value = 's1'
|
||||||
|
REQUIRED_INTERFACES.copy.return_value = {'test-interface': True}
|
||||||
|
get_optional_relations.return_value = {'optional': False}
|
||||||
|
test_interfaces = {
|
||||||
|
'test-interface': True,
|
||||||
|
'optional': False,
|
||||||
|
}
|
||||||
|
utils.assess_status_func('test-config')
|
||||||
|
# ports=None whilst port checks are disabled.
|
||||||
|
make_assess_status_func.assert_called_once_with(
|
||||||
|
'test-config', test_interfaces, services='s1', ports=None)
|
||||||
|
|
||||||
|
def test_pause_unit_helper(self):
|
||||||
|
with patch.object(utils, '_pause_resume_helper') as prh:
|
||||||
|
utils.pause_unit_helper('random-config')
|
||||||
|
prh.assert_called_once_with(utils.pause_unit, 'random-config')
|
||||||
|
with patch.object(utils, '_pause_resume_helper') as prh:
|
||||||
|
utils.resume_unit_helper('random-config')
|
||||||
|
prh.assert_called_once_with(utils.resume_unit, 'random-config')
|
||||||
|
|
||||||
|
@patch.object(utils, 'services')
|
||||||
|
def test_pause_resume_helper(self, services):
|
||||||
|
f = MagicMock()
|
||||||
|
services.return_value = 's1'
|
||||||
|
with patch.object(utils, 'assess_status_func') as asf:
|
||||||
|
asf.return_value = 'assessor'
|
||||||
|
utils._pause_resume_helper(f, 'some-config')
|
||||||
|
asf.assert_called_once_with('some-config')
|
||||||
|
# ports=None whilst port checks are disabled.
|
||||||
|
f.assert_called_once_with('assessor', services='s1', ports=None)
|
||||||
|
|
Loading…
Reference in New Issue