forked from jpmondet/norniring-bgpfabric
/
fabric.py
254 lines (220 loc) · 9.17 KB
/
fabric.py
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
#! /usr/bin/env python
# pylint: disable=missing-docstring, line-too-long, bad-continuation
from __future__ import (
absolute_import,
division,
generators,
generator_stop,
unicode_literals,
print_function,
nested_scopes,
with_statement,
) # , annotations
from nornir.plugins.tasks import commands, apis, text, files
from nornir.plugins.functions.text import print_result
from nornir.core.filter import F
class Fabric:
def __init__(self, nornir):
self._nornir = nornir
def linux_local_cmd(self, cmd):
local = self._nornir.filter(F(role="local"))
cmd_res = local.run(task=commands.command, command=cmd)
print_result(cmd_res)
@staticmethod
def run_remote_cmd(task, cmd):
res = task.run(commands.remote_command, command=cmd)
print_result(res)
def to_local_file(self, filename, content, path="./resources/"):
hosts = self._nornir.filter(F(platform="linux"))
hosts.run(files.write_file, filename=path + filename, content=content)
def calling_api(self, url, method):
local = self._nornir.filter(F(platform="linux"))
api_res = local.run(task=apis.http_method, method=method, url=url)
print_result(api_res)
def render_template(self, tplate, path="./templates"):
# hosts = self._nornir.filter(~F(platform="linux"))
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
rendered_cfg = hosts.run(
text.template_file, template=tplate, path=path
)
# print_result(rendered_cfg)
rendered_cfg_dict = dict()
for name, res in rendered_cfg.items():
rendered_cfg_dict[name] = res.result
return rendered_cfg_dict
def to_remote_file(
self, filename, content, name=None, path="./resources/"
):
# hosts = self._nornir.filter(~F(platform="linux"))
if not name:
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
else:
hosts = self._nornir.filter(F(hostname=name))
command = f'sudo su ; echo "{content}" > {path}{filename}'
hosts.run(commands.remote_command, command=command)
# print_result(res)
@staticmethod
def copy_files(task, src_file, dst_file, named=True):
if named:
task.run(
files.sftp,
action="put",
src=f"{src_file}-{task.host.name}",
dst=dst_file,
)
else:
task.run(files.sftp, action="put", src=f"{src_file}", dst=dst_file)
def send_j2_command(self, filtered_nr, command_j2):
commands_rendered = filtered_nr.run(
text.template_string, template=command_j2
)
for name, cmds in commands_rendered.items():
unique_srv = self._nornir.filter(F(hostname=name))
unique_srv.run(self.run_remote_cmd, cmd=cmds.result)
def configuring_interfaces(self):
rendered = self.render_template("interfaces.j2")
for name, config in rendered.items():
self.to_local_file(f"interfaces-{name}", config)
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
res = hosts.run(
task=self.copy_files,
src_file="./resources/interfaces",
dst_file="/tmp/interfaces",
)
print_result(res)
hosts.run(
task=self.run_remote_cmd,
cmd="sudo cp /tmp/interfaces /etc/network/interfaces",
)
def flushing_interfaces(self):
# hosts = self._nornir.filter(~F(platform="linux"))
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
command_j2 = "{% for intf in host.interfaces -%} sudo ip addr flush dev {{ intf.name }} && sudo ifup {{ intf.name }} --force ; {% endfor -%}"
self.send_j2_command(hosts, command_j2)
def net_restart(self):
# hosts = self._nornir.filter(~F(platform="linux"))
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
command = "sudo systemctl restart networking"
hosts.run(self.run_remote_cmd, cmd=command)
def _install_frr_cumulus(self, task):
install_cmds = "sudo apt install -y frr"
res = task.run(task=self.run_remote_cmd, cmd=install_cmds)
print_result(res)
def _install_frr_debian(self, task):
# Trick to retrieve the frr version from the set of servers
# first_srv = next(iter(srvs.inventory.hosts.keys()))
# frr_ver = self._nornir.inventory.hosts[first_srv]["frr_version"]
install_cmds = [
"curl -s https://deb.frrouting.org/frr/keys.asc | sudo apt-key add",
"echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable | sudo tee /etc/apt/sources.list.d/frr.list",
"sudo apt-get update -y",
"sudo apt-get install -y frr frr-pythontools",
]
# install_cmds = f"curl -s https://deb.frrouting.org/frr/keys.asc | sudo apt-key add -i ; echo deb https://deb.frrouting.org/frr $(lsb_release -s -c) frr-stable | sudo tee /etc/apt/sources.list.d/frr.list ; sudo apt install -y --allow-unauthenticated frr frr-pythontools"
# install_cmds = "curl -sLO https://github.com/FRRouting/frr/releases/download/frr-6.0.2/frr_6.0.2-0.ubuntu16.04.1_amd64.deb ; sudo apt-get install -y --allow-unauthenticated ./frr_6.0.2-0.ubuntu16.04.1_amd64.deb"
# install_cmds = "sudo apt install -y frr"
for cmd in install_cmds:
res = task.run(task=self.run_remote_cmd, cmd=cmd)
print_result(res)
def install_frr(self):
hosts = self._nornir.filter(F(role="spine") | F(role="leaf"))
res = hosts.run(task=self._install_frr_cumulus)
print_result(res)
hosts = self._nornir.filter(F(role="servers"))
res = hosts.run(task=self._install_frr_debian)
print_result(res)
def configuring_frr(self):
rendered = self.render_template("bgp.j2")
for name, config in rendered.items():
self.to_local_file(f"frrconf-{name}", config)
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
res = hosts.run(
task=self.copy_files,
src_file="./resources/frrconf",
dst_file="/tmp/frr.conf",
)
print_result(res)
res = hosts.run(
task=self.copy_files,
src_file="./templates/daemons",
dst_file="/tmp/daemons",
named=False,
)
print_result(res)
hosts.run(
task=self.run_remote_cmd,
cmd="sudo cp /tmp/frr.conf /etc/frr/frr.conf",
)
hosts.run(
task=self.run_remote_cmd,
cmd="sudo cp /tmp/daemons /etc/frr/daemons",
)
def restart_frr(self):
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
command = "sudo systemctl restart frr"
hosts.run(self.run_remote_cmd, cmd=command)
@staticmethod
def delimiter(action):
print("#" * 50)
print(action)
print("#" * 50)
def deploy(self):
""" Workflow to deploy a fully bgp fabric on CITC """
# self.linux_local_cmd('ls -alh')
# self.calling_api("https://api.chucknorris.io/jokes/random", 'get')
# Installing FRR
self.delimiter("Installing FRR")
self.install_frr()
# Handling interfaces
self.delimiter("Prep ifaces config")
self.configuring_interfaces()
self.delimiter("Flushing Ifaces just in case")
self.flushing_interfaces()
self.delimiter("Restarting the network")
self.net_restart()
# Configuring BGP and restarting FRR on all nodes
self.delimiter("Prep bgp config")
self.configuring_frr()
self.delimiter("Restart frr")
self.restart_frr()
def uninstall_frr(self):
# srvs = self._nornir.filter(F(role="servers"))
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
uninstall_cmds = "sudo apt remove -y frr ; sudo rm -rf /etc/frr/ /tmp/frr.conf /tmp/interfaces /tmp/daemons"
res = hosts.run(task=self.run_remote_cmd, cmd=uninstall_cmds)
print_result(res)
def unconfigure_ifaces(self):
hosts = self._nornir.filter(
F(role="servers") | F(role="spine") | F(role="leaf")
)
res = hosts.run(
task=self.run_remote_cmd,
cmd='echo -e "auto lo\niface lo inet loopback\nauto eth0\niface eth0 inet dhcp" | sudo tee /etc/network/interfaces',
)
def undeploy(self):
""" Unconfigure all the fabric """
self.delimiter("Uninstalling FRR and removing its files")
self.uninstall_frr()
# Handling interfaces
self.delimiter("Unconfigure interfaces")
self.unconfigure_ifaces()
self.delimiter("Flushing Ifaces")
self.flushing_interfaces()
self.delimiter("Restarting the network")
self.net_restart()