# Copyright 2015 Red Hat, Inc. # # 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 osc_lib.i18n import _ from tripleoclient import command from tripleoclient import exceptions from tripleoclient import utils class MatchProfiles(command.Command): """Assign and validate profiles on nodes""" log = logging.getLogger(__name__ + ".MatchProfiles") def get_parser(self, prog_name): parser = super(MatchProfiles, self).get_parser(prog_name) parser.add_argument( '--dry-run', action='store_true', default=False, help=_('Only run validations, but do not apply any changes.') ) utils.add_deployment_plan_arguments(parser) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) bm_client = self.app.client_manager.baremetal flavors = self._collect_flavors(parsed_args) errors, warnings = utils.assign_and_verify_profiles( bm_client, flavors, assign_profiles=True, dry_run=parsed_args.dry_run ) if errors: raise exceptions.ProfileMatchingError( _('Failed to validate and assign profiles.')) def _collect_flavors(self, parsed_args): """Collect nova flavors in use. :returns: dictionary flavor name -> (flavor object, scale) """ compute_client = self.app.client_manager.compute flavors = {f.name: f for f in compute_client.flavors.list()} result = {} message = "Provided --{}-flavor, '{}', does not exist" for target, (flavor_name, scale) in ( utils.get_roles_info(parsed_args).items() ): if flavor_name is None or not scale: self.log.debug("--{}-flavor not used".format(target)) continue try: flavor = flavors[flavor_name] except KeyError: raise exceptions.ProfileMatchingError( message.format(target, flavor_name)) result[flavor_name] = (flavor, scale) return result POSTFIX = '_profile' class ListProfiles(command.Lister): """List overcloud node profiles""" log = logging.getLogger(__name__ + ".ListProfiles") def get_parser(self, prog_name): parser = super(ListProfiles, self).get_parser(prog_name) parser.add_argument( '--all', action='store_true', default=False, help=_('List all nodes, even those not available to Nova.') ) utils.add_deployment_plan_arguments(parser) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) bm_client = self.app.client_manager.baremetal compute_client = self.app.client_manager.compute hypervisors = {h.hypervisor_hostname: h for h in compute_client.hypervisors.list() if h.hypervisor_type == 'ironic'} result = [] maintenance = None if parsed_args.all else False for node in bm_client.node.list(detail=True, maintenance=maintenance): error = '' if node.provision_state not in ('active', 'available'): error = "Provision state %s" % node.provision_state elif node.power_state in (None, 'error'): error = "Power state %s" % node.power_state elif node.maintenance: error = "Maintenance" else: try: hypervisor = hypervisors[node.uuid] except KeyError: error = 'No hypervisor record' else: if hypervisor.status != 'enabled': error = 'Compute service disabled' elif hypervisor.state != 'up': error = 'Compute service down' if error and not parsed_args.all: continue caps = utils.node_get_capabilities(node) profile = caps.get('profile') possible_profiles = [k[:-len(POSTFIX)] for k, v in caps.items() if k.endswith(POSTFIX) and v.lower() in ('1', 'true')] # sorting for convenient display and testing possible_profiles.sort() record = (node.uuid, node.name or '', node.provision_state, profile, ', '.join(possible_profiles)) if parsed_args.all: record += (error,) result.append(record) cols = ("Node UUID", "Node Name", "Provision State", "Current Profile", "Possible Profiles") if parsed_args.all: cols += ('Error',) return (cols, result)