Rename pharos-dashboard and pharos-validator
[pharos-tools.git] / validator / src / validation_tool / src / config.py
1 ##############################################################################
2 # Copyright (c) 2015 Todd Gaunt 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 logging
11 import sys
12 import os
13 import yaml
14 import struct
15 import socket
16
17 from pharosvalidator import util
18 from collections import namedtuple
19
20 class Topology():
21     """
22     Topology: Class to store any number of Network classes
23     and metadata about them
24     """
25     def __init__(self, yaml_config):
26         # Dictionary of available networks
27         self.logger = logging.getLogger(__name__)
28         self.networks = {}
29         self.external_networks = []
30
31         # Fill the above dictionaries
32         self.parse_yaml(yaml_config)
33
34     def parse_yaml(self, yaml_config):
35         """
36         parse_yaml: parses the yaml configuration file this program uses
37         for all the network and node information
38         """
39         config = safe_yaml_read(yaml_config)
40         for network in config["networks"]:
41             self.logger.info("Reading network section {}".format(network))
42             if network == "admin":
43                 self.networks[network] = Network(config["networks"][network])
44             #TODO
45             elif network == "external":
46                 for external_network in config["networks"][network]:
47                     self.external_networks.append(Network(external_network))
48
49 class Network():
50     """
51     Network: Class to store all information on a given network
52     """
53     def __init__(self, network):
54         try:
55             self.logger = logging.getLogger(__name__)
56
57             # Some generic settings
58             self.enabled = network["enabled"]
59             self.vlan = network["vlan"]
60
61             # VM settings
62             self.installer_nic_type = network["installer_vm"]["nic_type"]
63             self.installer_members = network["installer_vm"]["members"]
64             self.installer_ip = network["installer_vm"]["ip"]
65
66             # Tuple containing the minimum and maximum
67             self.usable_ip_range = self.parse_ip_range(network["usable_ip_range"])
68             self.gateway = network["gateway"]
69             self.cidr = network["cidr"]
70             self.dhcp_range = network["dhcp_range"]
71             self.dns_domain = network["dns-domain"]
72             self.dns_search = network["dns-search"]
73
74             subnet, netmask = self.split_cidr(network["cidr"])
75             self.subnet = subnet
76             self.netmask = netmask
77
78             # List of all dns servers
79             self.dns_upstream = network["dns-upstream"]
80
81             self.nic_mapping = {}
82         except KeyError as e:
83             self.logger.error("Field {} not available in network configuration file".format(e))
84
85     def split_cidr(self, cidr):
86         """
87         split_cidr: Split up cidr notation subnets into a subnet string and a
88         netmask string
89
90         input: cidr notation of a subnet
91
92         output: Subnet string; Netmask string
93         """
94         split = cidr.split('/')
95         host_bits = int(split[1])
96         netmask = self.cidr_to_netmask(host_bits)
97         subnet = split[0]
98
99         return subnet, netmask
100
101     def parse_ip_range(self, ip_range_string):
102         """
103         parse_ip_range: Create a named tuple object that contains the lowest
104         ip address and the highest ip address from a configuration file
105
106         input: String formatted like so "min, max" where min/max are ip addresses
107
108         output: Named tuple object containing a minimum and maximum field
109         """
110         rp = ip_range_string.split(",")
111         ip_range = namedtuple("ip_range", ['minimum', 'maximum'])(minimum=min(rp), maximum=max(rp))
112         return ip_range
113
114     def cidr_to_netmask(self, cidr):
115         bits = 0xffffffff ^ (1 << 32 - cidr) - 1
116         netmask = socket.inet_ntoa(struct.pack('>I', bits))
117         self.logger.debug("Netmask generated from cidr '{}': '{}'".format(cidr, netmask))
118         return netmask
119
120 class Inventory():
121     """
122     Inventory: Class to hold configuration file data
123     """
124     def __init__(self, yaml_config):
125         # Create the class logger
126         self.logger = logging.getLogger(__name__)
127
128         self.nodes = []
129
130         # Fill the above list
131         self.parse_yaml(yaml_config)
132
133     def parse_yaml(self, yaml_config):
134         config = safe_yaml_read(yaml_config)
135         nodes = []
136         for node in config["nodes"]:
137             self.nodes.append(Node(node))
138
139     def nodecount(self):
140         return len(self.nodes)
141
142 class Node():
143     """
144     Node: Class to hold
145     """
146     def __init__(self, node):
147         self.logger = logging.getLogger(__name__)
148         try:
149             self.name = node["name"]
150             self.tags = node["tags"]
151             self.arch = node["arch"]
152             self.mac_address = node["mac_address"] # ipmi mac address
153             self.cpus = node["cpus"]
154             self.memory = node["memory"]
155             self.disk = node["disk"]
156         except KeyError as e:
157             self.logger.error("Field {} not available in inventory file".format(e))
158
159         # Power sub section
160         if node["power"]["type"] == "ipmi":
161             try:
162                 self.ipmi_addr = node["power"]["address"]
163                 self.ipmi_user = node["power"]["user"]
164                 self.ipmi_pass = node["power"]["pass"]
165             except KeyError as e:
166                 self.logger.error("Field {} not available in inventory file".format(e))
167         else:
168             pass
169
170 def safe_yaml_read(yamlfile):
171     logger = logging.getLogger(__name__)
172     if os.path.isfile(yamlfile) == False:
173         logger.critical("Could not open find {}".format(yamlfile))
174         quit(1)
175     with open(yamlfile, 'r') as fd:
176         return yaml.load(fd.read())