charm-nova-compute/actions/cloud.py

170 lines
5.0 KiB
Python
Executable File

#!/usr/bin/env python3
#
# Copyright 2020 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
from enum import Enum
sys.path.append('lib')
sys.path.append('hooks')
import nova_compute_hooks
from nova_compute import cloud_utils
from charmhelpers.core.host import (
service_pause,
service_resume,
)
from charmhelpers.core.hookenv import (
DEBUG,
function_set,
function_fail,
INFO,
log,
status_get,
status_set,
WORKLOAD_STATES,
)
UNIT_REMOVED_MSG = 'Unit was removed from the cloud'
class ServiceState(Enum):
"""State of the nova-compute service in the cloud controller"""
enabled = 0
disabled = 1
def _set_service(state):
"""
Set state of the nova-compute service in the nova-cloud-controller.
Available states:
- ServiceState.enabled: nova-scheduler can use this unit to run new VMs
- ServiceState.disabled : nova-scheduler wont schedule new VMs on this
unit
:type state: ServiceState
"""
nova = cloud_utils.nova_client()
hostname = cloud_utils.service_hostname()
if state == ServiceState.disabled:
log('Disabling nova-compute service on host {}'.format(hostname))
nova.services.disable(hostname, 'nova-compute')
elif state == ServiceState.enabled:
log('Enabling nova-compute service on host {}'.format(hostname))
nova.services.enable(hostname, 'nova-compute')
else:
raise RuntimeError('Unknown service state')
def disable():
"""Disable nova-scheduler from starting new VMs on this unit"""
_set_service(ServiceState.disabled)
def enable():
"""Enable nova-scheduler to start new VMs on this unit"""
_set_service(ServiceState.enabled)
def remove_from_cloud():
"""
Implementation of 'remove-from-cloud' action.
This action is preparation for clean removal of nova-compute unit from
juju model. If this action succeeds , user can run `juju remove-unit`
command.
Steps performed by this action:
- Checks that this nova-compute unit can be removed from the cloud
- If not, action fails
- Stops nova-compute system service
- Unregisters nova-compute service from the nova cloud controller
"""
nova = cloud_utils.nova_client()
if cloud_utils.running_vms(nova) > 0:
raise RuntimeError("This unit can not be removed from the "
"cloud because it's still running VMs. Please "
"remove these VMs or migrate them to another "
"nova-compute unit")
nova_service_id = cloud_utils.nova_service_id(nova)
log("Stopping nova-compute service", DEBUG)
service_pause('nova-compute')
log("Deleting nova service '{}'".format(nova_service_id), DEBUG)
nova.services.delete(nova_service_id)
status_set(WORKLOAD_STATES.BLOCKED, UNIT_REMOVED_MSG)
function_set({'message': UNIT_REMOVED_MSG})
def register_to_cloud():
"""
Implementation of `register-to-cloud` action.
This action reverts `remove-from-cloud` action. It starts nova-comptue
system service which will trigger its re-registration in the cloud.
"""
log("Starting nova-compute service", DEBUG)
service_resume('nova-compute')
current_status = status_get()
if current_status[0] == WORKLOAD_STATES.BLOCKED.value and \
current_status[1] == UNIT_REMOVED_MSG:
status_set(WORKLOAD_STATES.ACTIVE, 'Unit is ready')
nova_compute_hooks.update_status()
function_set({
'command': 'openstack compute service list',
'message': "Nova compute service started. It should get registered "
"with the cloud controller in a short time. Use the "
"'openstack' command to verify that it's registered."
})
def node_name():
function_set({'node-name': cloud_utils.service_hostname()})
ACTIONS = {
'disable': disable,
'enable': enable,
'remove-from-cloud': remove_from_cloud,
'register-to-cloud': register_to_cloud,
'node-name': node_name,
}
def main(args):
action_name = os.path.basename(args.pop(0))
try:
action = ACTIONS[action_name]
except KeyError:
s = "Action {} undefined".format(action_name)
function_fail(s)
return
else:
try:
log("Running action '{}'.".format(action_name), INFO)
action()
except Exception as exc:
function_fail("Action {} failed: {}".format(action_name, str(exc)))
if __name__ == '__main__':
main(sys.argv)