Skip to content
This repository was archived by the owner on Jul 30, 2024. It is now read-only.

Commit 70149d8

Browse files
Shift inventory into faros-config.
1 parent 9724ac4 commit 70149d8

File tree

2 files changed

+3
-352
lines changed

2 files changed

+3
-352
lines changed

app/inventory.py

100755100644
+2-351
Original file line numberDiff line numberDiff line change
@@ -1,355 +1,6 @@
11
#!/usr/bin/env python3
2-
import argparse
3-
from collections import defaultdict
4-
from faros_config import FarosConfig, PydanticEncoder
5-
import ipaddress
6-
import json
7-
import os
82
import sys
9-
import pickle
103

11-
SSH_PRIVATE_KEY = '/data/id_rsa'
12-
IP_RESERVATIONS = '/data/ip_addresses'
4+
from faros_config.inventory.cli import main
135

14-
15-
class InventoryGroup(object):
16-
17-
def __init__(self, parent, name):
18-
self._parent = parent
19-
self._name = name
20-
21-
def add_group(self, name, **groupvars):
22-
return(self._parent.add_group(name, self._name, **groupvars))
23-
24-
def add_host(self, name, hostname=None, **hostvars):
25-
return(self._parent.add_host(name, self._name, hostname, **hostvars))
26-
27-
def host(self, name):
28-
return self._parent.host(name)
29-
30-
31-
class Inventory(object):
32-
33-
_modes = ['list', 'host', 'verify', 'none']
34-
_data = {"_meta": {"hostvars": defaultdict(dict)}}
35-
36-
def __init__(self, mode=0, host=None):
37-
if mode == 1:
38-
# host info requested
39-
# current, only list and none are implimented
40-
raise NotImplementedError()
41-
42-
self._mode = mode
43-
self._host = host
44-
45-
def host(self, name):
46-
return self._data['_meta']['hostvars'].get(name)
47-
48-
def group(self, name):
49-
if name in self._data:
50-
return InventoryGroup(self, name)
51-
else:
52-
return None
53-
54-
def add_group(self, name, parent=None, **groupvars):
55-
self._data[name] = {'hosts': [], 'vars': groupvars, 'children': []}
56-
57-
if parent:
58-
if parent not in self._data:
59-
self.add_group(parent)
60-
self._data[parent]['children'].append(name)
61-
62-
return InventoryGroup(self, name)
63-
64-
def add_host(self, name, group=None, hostname=None, **hostvars):
65-
if not group:
66-
group = 'all'
67-
if group not in self._data:
68-
self.add_group(group)
69-
70-
if hostname:
71-
hostvars.update({'ansible_host': hostname})
72-
73-
self._data[group]['hosts'].append(name)
74-
self._data['_meta']['hostvars'][name].update(hostvars)
75-
76-
def to_json(self):
77-
return json.dumps(self._data, sort_keys=True, indent=4,
78-
separators=(',', ': '), cls=PydanticEncoder)
79-
80-
81-
class IPAddressManager(dict):
82-
83-
def __init__(self, save_file, subnet):
84-
super().__init__()
85-
self._save_file = save_file
86-
87-
# parse the subnet definition into a static and dynamic pool
88-
divided = subnet.subnets()
89-
self._static_pool = next(divided)
90-
self._dynamic_pool = next(divided)
91-
self._generator = self._static_pool.hosts()
92-
93-
# calculate reverse dns zone
94-
classful_prefix = [32, 24, 16, 8, 0]
95-
classful = subnet
96-
while classful.prefixlen not in classful_prefix:
97-
classful = classful.supernet()
98-
host_octets = classful_prefix.index(classful.prefixlen)
99-
self._reverse_ptr_zone = \
100-
'.'.join(classful.reverse_pointer.split('.')[host_octets:])
101-
102-
# load the last saved state
103-
try:
104-
restore = pickle.load(open(save_file, 'rb'))
105-
except: # noqa: E722
106-
restore = {}
107-
self.update(restore)
108-
109-
# reserve the first ip for the bastion
110-
_ = self['bastion']
111-
112-
def __getitem__(self, key):
113-
key = key.lower()
114-
try:
115-
return super().__getitem__(key)
116-
except KeyError:
117-
new_ip = self._next_ip()
118-
self[key] = new_ip
119-
return new_ip
120-
121-
def __setitem__(self, key, value):
122-
return super().__setitem__(key.lower(), value)
123-
124-
def _next_ip(self):
125-
used_ips = list(self.values())
126-
loop = True
127-
128-
while loop:
129-
new_ip = next(self._generator).exploded
130-
loop = new_ip in used_ips
131-
return new_ip
132-
133-
def get(self, key, value=None):
134-
if value and value not in self.values():
135-
self[key] = value
136-
return self[key]
137-
138-
def save(self):
139-
with open(self._save_file, 'wb') as handle:
140-
pickle.dump(dict(self), handle)
141-
142-
@property
143-
def static_pool(self):
144-
return str(self._static_pool)
145-
146-
@property
147-
def dynamic_pool(self):
148-
return str(self._dynamic_pool)
149-
150-
@property
151-
def reverse_ptr_zone(self):
152-
return str(self._reverse_ptr_zone)
153-
154-
155-
class Config(object):
156-
shim_var_keys = [
157-
'WAN_INT',
158-
'BASTION_IP_ADDR',
159-
'BASTION_INTERFACES',
160-
'BASTION_HOST_NAME',
161-
'BASTION_SSH_USER',
162-
'CLUSTER_DOMAIN',
163-
'CLUSTER_NAME',
164-
'BOOT_DRIVE',
165-
]
166-
167-
def __init__(self, yaml_file):
168-
self.shim_vars = {}
169-
for var in self.shim_var_keys:
170-
self.shim_vars[var] = os.getenv(var)
171-
config = FarosConfig.from_yaml(yaml_file)
172-
self.network = config.network
173-
self.bastion = config.bastion
174-
self.cluster = config.cluster
175-
self.proxy = config.proxy
176-
177-
178-
def parse_args():
179-
parser = argparse.ArgumentParser()
180-
parser.add_argument('--list', action='store_true')
181-
parser.add_argument('--verify', action='store_true')
182-
parser.add_argument('--host', action='store')
183-
args = parser.parse_args()
184-
return args
185-
186-
187-
def main(config, ipam, inv):
188-
# GATHER INFORMATION FOR EXTRA NODES
189-
for node in config.network.lan.dhcp.extra_reservations:
190-
addr = ipam.get(node.mac, str(node.ip))
191-
node.ip = ipaddress.IPv4Address(addr)
192-
193-
# CREATE INVENTORY
194-
inv.add_group(
195-
'all', None,
196-
ansible_ssh_private_key_file=SSH_PRIVATE_KEY,
197-
cluster_name=config.shim_vars['CLUSTER_NAME'],
198-
cluster_domain=config.shim_vars['CLUSTER_DOMAIN'],
199-
admin_password=config.bastion.become_pass,
200-
pull_secret=json.loads(config.cluster.pull_secret),
201-
mgmt_provider=config.cluster.management.provider,
202-
mgmt_user=config.cluster.management.user,
203-
mgmt_password=config.cluster.management.password,
204-
install_disk=config.shim_vars['BOOT_DRIVE'],
205-
loadbalancer_vip=ipam['loadbalancer'],
206-
dynamic_ip_range=ipam.dynamic_pool,
207-
reverse_ptr_zone=ipam.reverse_ptr_zone,
208-
subnet=str(config.network.lan.subnet.network_address),
209-
subnet_mask=config.network.lan.subnet.prefixlen,
210-
wan_ip=config.shim_vars['BASTION_IP_ADDR'],
211-
extra_nodes=config.network.lan.dhcp.extra_reservations,
212-
ignored_macs=config.network.lan.dhcp.ignore_macs,
213-
dns_forwarders=config.network.lan.dns_forward_resolvers,
214-
proxy=config.proxy is not None,
215-
proxy_http=config.proxy.http if config.proxy is not None else '',
216-
proxy_https=config.proxy.https if config.proxy is not None else '',
217-
proxy_noproxy=config.proxy.noproxy if config.proxy is not None else [],
218-
proxy_ca=config.proxy.ca if config.proxy is not None else ''
219-
)
220-
221-
infra = inv.add_group('infra')
222-
router = infra.add_group(
223-
'router',
224-
wan_interface=config.shim_vars['WAN_INT'],
225-
lan_interfaces=config.network.lan.interfaces,
226-
all_interfaces=config.shim_vars['BASTION_INTERFACES'].split(),
227-
allowed_services=config.network.port_forward
228-
)
229-
# ROUTER INTERFACES
230-
router.add_host(
231-
'wan', config.shim_vars['BASTION_IP_ADDR'],
232-
ansible_become_pass=config.bastion.become_pass,
233-
ansible_ssh_user=config.shim_vars['BASTION_SSH_USER']
234-
)
235-
router.add_host(
236-
'lan',
237-
ipam['bastion'],
238-
ansible_become_pass=config.bastion.become_pass,
239-
ansible_ssh_user=config.shim_vars['BASTION_SSH_USER']
240-
)
241-
# DNS NODE
242-
router.add_host(
243-
'dns',
244-
ipam['bastion'],
245-
ansible_become_pass=config.bastion.become_pass,
246-
ansible_ssh_user=config.shim_vars['BASTION_SSH_USER']
247-
)
248-
# DHCP NODE
249-
router.add_host(
250-
'dhcp',
251-
ipam['bastion'],
252-
ansible_become_pass=config.bastion.become_pass,
253-
ansible_ssh_user=config.shim_vars['BASTION_SSH_USER']
254-
)
255-
# LOAD BALANCER NODE
256-
router.add_host(
257-
'loadbalancer',
258-
ipam['loadbalancer'],
259-
ansible_become_pass=config.bastion.become_pass,
260-
ansible_ssh_user=config.shim_vars['BASTION_SSH_USER']
261-
)
262-
263-
# BASTION NODE
264-
bastion = infra.add_group('bastion_hosts')
265-
bastion.add_host(
266-
config.shim_vars['BASTION_HOST_NAME'],
267-
ipam['bastion'],
268-
ansible_become_pass=config.bastion.become_pass,
269-
ansible_ssh_user=config.shim_vars['BASTION_SSH_USER']
270-
)
271-
272-
# CLUSTER NODES
273-
cluster = inv.add_group('cluster')
274-
# BOOTSTRAP NODE
275-
ip = ipam['bootstrap']
276-
cluster.add_host(
277-
'bootstrap', ip,
278-
ansible_ssh_user='core',
279-
node_role='bootstrap'
280-
)
281-
# CLUSTER CONTROL PLANE NODES
282-
cp = cluster.add_group('control_plane', node_role='master')
283-
for count, node in enumerate(config.cluster.nodes):
284-
ip = ipam[node.mac]
285-
mgmt_ip = ipam[node.mgmt_mac]
286-
cp.add_host(
287-
node.name, ip,
288-
mac_address=node.mac,
289-
mgmt_mac_address=node.mgmt_mac,
290-
mgmt_hostname=mgmt_ip,
291-
ansible_ssh_user='core',
292-
cp_node_id=count
293-
)
294-
if node.install_drive is not None:
295-
cp.host(node['name'])['install_disk'] = node.install_drive
296-
297-
# VIRTUAL NODES
298-
virt = inv.add_group(
299-
'virtual',
300-
mgmt_provider='kvm',
301-
mgmt_hostname='bastion',
302-
install_disk='vda'
303-
)
304-
virt.add_host('bootstrap')
305-
306-
# MGMT INTERFACES
307-
mgmt = inv.add_group(
308-
'management',
309-
ansible_ssh_user=config.cluster.management.user,
310-
ansible_ssh_pass=config.cluster.management.password
311-
)
312-
for node in config.cluster.nodes:
313-
mgmt.add_host(
314-
node.name + '-mgmt', ipam[node.mgmt_mac],
315-
mac_address=node.mgmt_mac
316-
)
317-
318-
319-
if __name__ == "__main__":
320-
# PARSE ARGUMENTS
321-
args = parse_args()
322-
if args.list:
323-
mode = 0
324-
elif args.verify:
325-
mode = 2
326-
else:
327-
mode = 1
328-
329-
# INTIALIZE CONFIG
330-
config = Config('/data/config.yml')
331-
332-
# INTIALIZE IPAM
333-
ipam = IPAddressManager(
334-
IP_RESERVATIONS,
335-
config.network.lan.subnet
336-
)
337-
338-
# INITIALIZE INVENTORY
339-
inv = Inventory(mode, args.host)
340-
341-
# CREATE INVENTORY
342-
try:
343-
main(config, ipam, inv)
344-
if mode == 0:
345-
print(inv.to_json())
346-
347-
except Exception as e:
348-
if mode == 2:
349-
sys.stderr.write(config.error)
350-
sys.exit(1)
351-
raise(e)
352-
353-
# DONE
354-
ipam.save()
355-
sys.exit(0)
6+
main(sys.argv[1:])

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
ansible<2.11
2-
faros-config==0.1.3
2+
faros-config==0.2.0
33

44
# for management/ilo provider
55
python-hpilo==4.4.3

0 commit comments

Comments
 (0)