forked from jamiecaesar/securecrt-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
m_update_dhcp_relay.py
206 lines (173 loc) · 10.6 KB
/
m_update_dhcp_relay.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
# $language = "python"
# $interface = "1.0"
import os
import sys
import logging
# Add script directory to the PYTHONPATH so we can import our modules (only if run from SecureCRT)
if 'crt' in globals():
script_dir, script_name = os.path.split(crt.ScriptFullName)
if script_dir not in sys.path:
sys.path.insert(0, script_dir)
else:
script_dir, script_name = os.path.split(os.path.realpath(__file__))
# Now we can import our custom modules
from securecrt_tools import scripts
from securecrt_tools import sessions
from securecrt_tools import utilities
# Import message box constants names for use specifying the design of message boxes
from securecrt_tools.message_box_const import *
from s_update_dhcp_relay import build_valid_ip_list, update_helpers
# Create global logger so we can write debug messages from any function (if debug mode setting is enabled in settings).
logger = logging.getLogger("securecrt")
logger.debug("Starting execution of {0}".format(script_name))
# ################################################ SCRIPT LOGIC ##################################################
def script_main(script):
"""
| MULTIPLE device script
| Author: Jamie Caesar
| Email: [email protected]
This script will scan the running configuration of the provided list of devices, looking for instances of old IP
helper/DHCP relay addresses (IOS/NXOS) on interfaces and if found will update the helper/relay addresses with the
newer ones. The new and old addresses that the script looks for is saved in the settings.ini file, as documented
below.
Any devices that cannot be connected to will be logged in a separate file saved in the output directory.
This script will prompt you to run in "Check Mode", where the configuration changes the script would be pushed to
the devices are ONLY written to a file and NO CHANGES will be made to the devices. If you select "No" when prompted
this script will push the configuration changes to the devices. Also, when the changes are pushed to the devices
this script will save the running config before and after the changes are made, and will also output a log of the
configuration sessions showing all the commands pushed.
**Script Settings** (found in settings/settings.ini):
* | **show_instructions** - When True, displays a pop-up upon launching the script
| explaining where to modify the list of commands sent to devices. This window also
| prompts the user if they want to continue seeing this message. If not, the script
| changes this setting to False.
* | **old_relays** - This is a comma separated list of IP addresses that the script should
| search for as relay addresses in the device's configuration.
* | **new_relays** - This is a comma separated list of IP addresses that are the new relay
| addresses that should be added to any interface that has at least one of the old
| helper/relay addresses on it.
* | **remove_old_relays** - If True, the script will add the new relays and REMOVE the old
| relays immediately after adding the new ones. If False (default), the script will
| only add the new relays to interfaces where at least one old relay is found. This
| is useful when you want to push out new relays as part of a migration process
| without removing the old relays. Since this script will not try to push new relay
| addresses that already exist on an interface, the script can be run again with this
| option set to True to later remove the old relays.
:param script: A subclass of the scripts.Script object that represents the execution of this particular script
(either CRTScript or DirectScript)
:type script: scripts.Script
"""
session = script.get_main_session()
# If this is launched on an active tab, disconnect before continuing.
logger.debug("<M_SCRIPT> Checking if current tab is connected.")
if session.is_connected():
logger.debug("<M_SCRIPT> Existing tab connected. Stopping execution.")
raise scripts.ScriptError("This script must be launched in a not-connected tab.")
# Load a device list
device_list = script.import_device_list()
if not device_list:
return
# ######################################### GET VALUES FROM SETTINGS ###########################################
# Display instructions message, unless settings prevent it
show_instructions = script.settings.getboolean("update_dhcp_relay", "show_instructions")
if show_instructions:
response = script.message_box("The list of old and new ip-helper/dhcp relay IPs can be edited in the "
"'settings/settings.ini' file in the main securecrt-tools directory.\nSee the "
"documentation for this script ('docs/index.html') for more details.\n\n"
"Do you want to stop seeing this message?",
"Instructions", ICON_QUESTION + BUTTON_YESNO)
if response == IDYES:
script.settings.update("update_dhcp_relay", "show_instructions", False)
# Collection of old helpers/relays is in a set data structure to make membership checks easier. A list works fine
# for new helpers/relays.
old_helpers = set(build_valid_ip_list(script.settings.getlist("update_dhcp_relay", "old_relays")))
new_helpers = build_valid_ip_list(script.settings.getlist("update_dhcp_relay", "new_relays"))
remove_old_helpers = script.settings.getboolean("update_dhcp_relay", "remove_old_relays")
# ######################################### START CHECK MODE SECTION ###########################################
# Ask if this should be a test run (generate configs only) or full run (push updates to devices)
# Comment out or remove the entire CHECK MODE SECTION if you don't want to prompt for check mode
check_mode_message = "Do you want to run this script in check mode? (Only generate configs)\n" \
"\n" \
"Yes = Connect to device and write change scripts to a file ONLY\n" \
"No = Connect to device and PUSH configuration changes"
message_box_design = ICON_QUESTION | BUTTON_YESNOCANCEL
logger.debug("Prompting the user to run in check mode.")
result = script.message_box(check_mode_message, "Run in Check Mode?", message_box_design)
if result == IDYES:
logger.debug("<M_SCRIPT> Received 'True' for Check Mode.")
check_mode = True
elif result == IDNO:
logger.debug("<M_SCRIPT> Received 'False' for Check Mode.")
check_mode = False
else:
return
# ########################################### END CHECK MODE SECTION ############################################
# Check settings if we should use a proxy/jumpbox
use_proxy = script.settings.getboolean("Global", "use_proxy")
default_proxy_session = script.settings.get("Global", "proxy_session")
# ######################################## START DEVICE CONNECT LOOP ###########################################
# Create a filename to keep track of our connection logs, if we have failures. Use script name without extension
failed_log = session.create_output_filename("{0}-LOG".format(script_name.split(".")[0]), include_hostname=False)
for device in device_list:
hostname = device['Hostname']
protocol = device['Protocol']
username = device['Username']
password = device['Password']
enable = device['Enable']
try:
proxy = device['Proxy Session']
except KeyError:
proxy = None
if not proxy and use_proxy:
proxy = default_proxy_session
logger.debug("<M_SCRIPT> Connecting to {0}.".format(hostname))
try:
script.connect(hostname, username, password, protocol=protocol, proxy=proxy)
session = script.get_main_session()
per_device_work(session, check_mode, enable, old_helpers, new_helpers, remove_old_helpers)
script.disconnect()
except scripts.ConnectError as e:
with open(failed_log, 'a') as logfile:
logfile.write("<M_SCRIPT> Connect to {0} failed: {1}\n".format(hostname, e.message.strip()))
session.disconnect()
except sessions.InteractionError as e:
with open(failed_log, 'a') as logfile:
logfile.write("<M_SCRIPT> Failure on {0}: {1}\n".format(hostname, e.message.strip()))
session.disconnect()
except sessions.UnsupportedOSError as e:
with open(failed_log, 'a') as logfile:
logfile.write("<M_SCRIPT> Unsupported OS on {0}: {1}\n".format(hostname, e.message.strip()))
session.disconnect()
except Exception as e:
with open(failed_log, 'a') as logfile:
logfile.write("<M_SCRIPT> Exception on {0}: {1} ({2})\n".format(hostname, e.message.strip(), e))
session.disconnect()
# ######################################### END DEVICE CONNECT LOOP ############################################
def per_device_work(session, check_mode, enable_pass, old_helpers, new_helpers, remove_old_helpers):
"""
This function contains the code that should be executed on each device that this script connects to. It is called
after establishing a connection to each device in the loop above.
You can either put your own code here, or if there is a single-device version of a script that performs the correct
task, it can be imported and called here, essentially making this script connect to all the devices in the chosen
CSV file and then running a single-device script on each of them.
"""
session.start_cisco_session(enable_pass=enable_pass)
update_helpers(session, check_mode, old_helpers, new_helpers, remove_old_helpers)
session.end_cisco_session()
# ################################################ SCRIPT LAUNCH ###################################################
# If this script is run from SecureCRT directly, use the SecureCRT specific class
if __name__ == "__builtin__":
# Initialize script object
crt_script = scripts.CRTScript(crt)
# Run script's main logic against the script object
script_main(crt_script)
# Shutdown logging after
logging.shutdown()
# If the script is being run directly, use the simulation class
elif __name__ == "__main__":
# Initialize script object
direct_script = scripts.DebugScript(os.path.realpath(__file__))
# Run script's main logic against the script object
script_main(direct_script)
# Shutdown logging after
logging.shutdown()