-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathconfig_push.py
More file actions
executable file
·184 lines (168 loc) · 6.89 KB
/
config_push.py
File metadata and controls
executable file
·184 lines (168 loc) · 6.89 KB
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
"""
Script: config_push.py
About: Push configuration to multiple Juniper devices
By: Danny Vernals
"""
import sys
import argparse
import getpass
import os
import logging
from logging.handlers import RotatingFileHandler
from jnpr.junos.exception import (ConnectTimeoutError, ConnectRefusedError,
ConnectAuthError, ConnectUnknownHostError,
ConfigLoadError)
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
def logging_func(directory):
"""Instantiate Logging."""
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
if not os.path.exists(directory + '/logs'):
os.makedirs(directory + '/logs')
handler_file = RotatingFileHandler(directory + '/logs/config_push.log',
maxBytes=100000, backupCount=10
)
handler_file.setLevel(logging.INFO)
handler_stout = logging.StreamHandler(sys.stdout)
handler_stout.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler_file.setFormatter(formatter)
logger.addHandler(handler_file)
logger.addHandler(handler_stout)
return logger
def cli_grab():
"""take stuff from cli, output it in a dict"""
parser = argparse.ArgumentParser(description="Push config to Jnpr devices")
parser.add_argument("routers_file",
help="File that contains a list of routers to update")
parser.add_argument("config_file",
help="File that contains config you want to push")
parser.add_argument("config_format",
help="Format of the config: xml, text or set")
parser.add_argument("-k", "--key",
help="location of SSH key to authenticate with")
parser.add_argument("-C", "--comment", help="comment to commit with")
parser.add_argument("-t",
help="Test run. Apply the config, run a "
"'show | compare' then rollback. "
"The change is not committed.",
action='store_true')
parser.add_argument("-c", action='store_true', help="Execute commit full")
parser.add_argument("-f", action='store_true',
help="Force: Ignore any commit errors'")
parser.add_argument('-d', action='store_true',
help="If the -d flag is set, 'config_file' is a "
"directory. Here device specific configs "
"are stored. Is it used to push different "
"configs to each router. Configs must be "
"named ${Device}.conf")
args = vars(parser.parse_args())
args['uid'] = input('username: ')
args['pwd'] = getpass.getpass('password (blank if using SSH keys): ')
return args
def router_connect(router, uid, pwd):
""""attempt to connect to a router with the passed credentials"""
try:
dev = Device(host=router, user=uid, password=pwd)
dev.open()
# dev.timeout = 120 # Timeout for all RPCs
except (ConnectTimeoutError, ConnectRefusedError, ConnectAuthError,
ConnectUnknownHostError, ConfigLoadError) as err:
LOGGER.info(err)
LOGGER.info("Skipping {}, moving onto next router".format(router))
return None
LOGGER.info("Connected to '{}'. hostname: '{}'. JunOS: '{}'\n".format(
router,
dev.facts['hostname'],
dev.facts['version']
)
)
return dev
def instantiate_config_object(dev, config_format, config_text):
"""
Instantiate an object from Config(),
attempt to apply the configuration changes
"""
config_dev = Config(dev)
try:
config_dev.lock()
config_dev.load(config_text, format=config_format)
except ConfigLoadError as err:
LOGGER.info(err)
print("Errors detected, rolling back the change and disconnecting\n")
config_dev.rollback()
config_dev.unlock()
dev.close()
return None
return config_dev
def commit_config(config_dev, commit_full, comment):
"""Attempt to commit the loaded configuration"""
if config_dev.commit_check():
LOGGER.info("Commit check passed, commiting changes\n...")
if commit_full:
LOGGER.info("Performing 'commit full', this could take some time!")
config_dev.commit(full=True, timeout=120, comment=comment)
LOGGER.info("Commit full complete")
else:
config_dev.commit(comment=comment)
LOGGER.info("Commit complete")
else:
LOGGER.info("Commit failed, rolling back")
config_dev.rollback()
def upload_config(args):
"""push config to multiple devices"""
config_file = args['config_file']
multi_conf = args['d']
test_run = args['t']
commit_full = args['c']
force = args['f']
try:
comment = args['C']
comment = 'applied by config_push.py: ' + comment
except IndexError:
comment = 'applied by config_push.py'
with open(args['routers_file']) as routers_raw:
routers_list = routers_raw.read().splitlines()
for router in routers_list:
if multi_conf:
with open(config_file + '/' + router + ".conf") as config_raw:
config_text = config_raw.read()
else:
with open(config_file) as config_raw:
config_text = config_raw.read()
print("=" * 110)
LOGGER.info("Pushing {} to {}".format(config_file, router))
device = router_connect(router, args['uid'], args['pwd'])
if device is None:
continue
print("-" * 110)
config_dev = instantiate_config_object(device, args['config_format'],
config_text)
if config_dev is None and not multi_conf and not force:
LOGGER.info("As an error was detected in config application.\n"
"The assumption is this will happen on multiple \n"
"devices, exiting. To change this behaviour use '-f'")
sys.exit()
elif config_dev is None:
continue
config_diff = config_dev.diff()
LOGGER.info("Config diff: {}".format(config_diff))
print("-" * 110)
if test_run:
config_dev.rollback()
LOGGER.info("Test Run only, rolling back the change")
elif config_diff is None:
config_dev.rollback()
LOGGER.info("Diff is 'None' so cancelling commit")
else:
commit_config(config_dev, commit_full, comment)
print("-" * 110)
config_dev.unlock()
device.close()
LOGGER.info("Disconnected from {}\n".format(router))
if __name__ == '__main__':
DIRECTORY = os.path.dirname(os.path.realpath(__file__))
LOGGER = logging_func(DIRECTORY)
ARGS = cli_grab()
upload_config(ARGS)