Updates to Inventory object
[apex.git] / apex / inventory / inventory.py
1 ##############################################################################
2 # Copyright (c) 2016 Dan Radez (dradez@redhat.com) and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 import json
11 import platform
12
13 import yaml
14
15 from apex.common import constants
16 from apex.common import utils
17
18
19 class Inventory(dict):
20     """
21     This class parses an APEX inventory yaml file into an object. It
22     generates or detects all missing fields for deployment.
23
24     It then collapses one level of identification from the object to
25     convert it to a structure that can be dumped into a json file formatted
26     such that Triple-O can read the resulting json as an instackenv.json file.
27     """
28     def __init__(self, source, ha=True, virtual=False):
29         init_dict = {}
30         self.root_device = constants.DEFAULT_ROOT_DEV
31         if isinstance(source, str):
32             with open(source, 'r') as inventory_file:
33                 yaml_dict = yaml.safe_load(inventory_file)
34             # collapse node identifiers from the structure
35             init_dict['nodes'] = list(map(lambda n: n[1],
36                                           yaml_dict['nodes'].items()))
37         else:
38             # assume input is a dict to build from
39             init_dict = source
40
41         # move ipmi_* to pm_*
42         # make mac a list
43         def munge_node(node):
44             pairs = (('pm_addr', 'ipmi_ip'), ('pm_password', 'ipmi_pass'),
45                      ('pm_user', 'ipmi_user'), ('mac', 'mac_address'),
46                      ('cpu', 'cpus'), (None, 'disk_device'))
47
48             for x, y in pairs:
49                 if y in node:
50                     if y == 'disk_device':
51                         self.root_device = node[y]
52                     elif x == 'mac':
53                         node[x] = [node[y]]
54                     elif x is not None and y in node:
55                         node[x] = node[y]
56                     del node[y]
57
58             # aarch64 is always uefi
59             if 'arch' in node and node['arch'] == 'aarch64':
60                 node['capabilities'] += ',boot_mode:uefi'
61
62             return node
63
64         super().__init__({'nodes': list(map(munge_node, init_dict['nodes']))})
65
66         # verify number of nodes
67         if ha and len(self['nodes']) < 5:
68             raise ApexInventoryException('You must provide at least 5 '
69                                          'nodes for HA deployment')
70         elif len(self['nodes']) < 2:
71             raise ApexInventoryException('You must provide at least 2 nodes '
72                                          'for non-HA deployment')
73
74         if virtual:
75             self['host-ip'] = '192.168.122.1'
76             self['power_manager'] = \
77                 'nova.virt.baremetal.virtual_power_driver.VirtualPowerManager'
78             self['seed-ip'] = ''
79             self['ssh-key'] = 'INSERT_STACK_USER_PRIV_KEY'
80             self['ssh-user'] = 'root'
81
82     def dump_instackenv_json(self):
83         print(json.dumps(dict(self), sort_keys=True, indent=4))
84
85     def get_node_counts(self):
86         """
87         Return numbers of controller and compute nodes in inventory
88         :param inventory: node inventory data structure
89         :return: number of controller and compute nodes in inventory
90         """
91         nodes = self['nodes']
92         num_control = 0
93         num_compute = 0
94         for node in nodes:
95             if 'profile:control' in node['capabilities']:
96                 num_control += 1
97             elif 'profile:compute' in node['capabilities']:
98                 num_compute += 1
99             else:
100                 raise ApexInventoryException("Node missing capabilities "
101                                              "key: {}".format(node))
102         return num_control, num_compute
103
104
105 class ApexInventoryException(Exception):
106     pass