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