UNPKG

10.7 kBPlain TextView Raw
1#!/usr/bin/env python
2
3# Modified from
4# https://github.com/f5devcentral/f5-aws-migrate/blob/master/f5-aws-migrate.py
5# Assumptions: Script will be run on a BIGI-IP itself
6
7import os
8import sys
9import re
10import shutil
11import tarfile
12from tempfile import mkstemp
13from optparse import OptionParser
14
15def get_hostname(config_file):
16 hostname = ""
17 global_settings_token_found = False
18 hostname_token_line = 0
19 with open(config_file, 'r') as f:
20 for line in f:
21 if global_settings_token_found:
22 match = re.search('\s+hostname (.+)', line)
23 if match:
24 hostname = match.group(1)
25 return hostname
26 else:
27 match = re.search('sys global-settings', line)
28 if match:
29 global_settings_token_found = True
30
31def get_ip(config_file):
32 # 1nic Autoscale IPv4 Specific
33 # Looking for entry in bigip_base.conf:
34 # net self /Common/10.0.11.241/24 {
35 # address 10.0.11.241/24
36 # allow-service {
37 # default
38 # }
39 # traffic-group /Common/traffic-group-local-only
40 # vlan /Common/external
41 # }
42 ip = ""
43 line_number = 0
44 net_self_token_line = 0
45 with open(config_file, 'r') as f:
46 for line in f:
47 match = re.search('net self', line)
48 if match:
49 net_self_token_line = line_number
50 # Search for address in that token
51 match = re.search(' address (.+)/(.+)', line)
52 if match and (line_number - net_self_token_line == 1):
53 ip = match.group(1)
54 mask = match.group(2)
55 line_number += 1
56 # could do a little more validation here to make sure not another self-ip configured by accident
57 # octets match management ip, hostname, etc.
58 return ip
59
60
61def get_local_ip():
62 # 1nic Autoscale IPv4 Specific
63 # "privateIp entry in /shared/vadc/aws/iid-document
64 # is probably most reliable
65 # otherwise, getting it from dhcp
66 # searching for field
67 # "fixed-address 10.0.1.219;"
68 ip = ""
69 with open('/var/lib/dhclient/dhclient.leases', 'r') as f:
70 for line in f:
71 match = re.search('fixed-address (.+);', line)
72 if match:
73 ip = match.group(1)
74
75 return ip
76
77
78def get_gateway(config_file):
79 # 1nic Autoscale IPv4 Specific
80 # searching for entry
81 # net route /LOCAL_ONLY/default {
82 # gw 10.0.1.1
83 # network default
84 # }
85 gateway = ""
86 with open(config_file, 'r') as f:
87 for line in f:
88 match = re.search(' gw (.+)', line)
89 if match:
90 gateway = match.group(1)
91
92 return gateway
93
94
95def get_local_gateway():
96 # 1nic Autoscale IPv4 Specific
97 # searching for field
98 # "option routers 10.0.1.1;"
99 gateway = ""
100 with open('/var/lib/dhclient/dhclient.leases', 'r') as f:
101 for line in f:
102 match = re.search('option routers (.+);', line)
103 if match:
104 gateway = match.group(1)
105
106 return gateway
107
108
109def replace(source_file_path, pattern, substring):
110 fh, target_file_path = mkstemp()
111 with open(target_file_path, 'w') as target_file:
112 with open(source_file_path, 'r') as source_file:
113 for line in source_file:
114 if "-" in pattern:
115 newline = re.sub("(%s)$" %(pattern),substring,line)
116 newline = re.sub("(%s)\." %(pattern),substring+'.',newline)
117 target_file.write(newline)
118 else:
119 newline = re.sub("(%s)$" %(pattern),substring,line)
120 newline = re.sub("(%s)\/" %(pattern),substring+'/',newline)
121 target_file.write(newline)
122 os.remove(source_file_path)
123 shutil.move(target_file_path, source_file_path)
124
125def removeFiles(dir, pattern):
126 if os.path.exists(dir):
127 for f in os.listdir(dir):
128 if re.search(pattern, f):
129 os.remove(os.path.join(dir, f))
130
131def main():
132
133 parser = OptionParser()
134 parser.add_option("--debug-level", action="store", type="int", dest="debug_level", default=0, help="debug level print debug (0-9)")
135 parser.add_option("--cloud-provider", action="store", type="string", dest="cloud_provider", default="" , help="Cloud being utilized, azure or aws, etc...")
136 parser.add_option("--original-ucs", action="store", type="string", dest="original_ucs", help="Original UCS file name")
137 parser.add_option("--updated-ucs", action="store", type="string", dest="updated_ucs", default="updated.ucs", help="Modified UCS file name")
138 parser.add_option("--extract-directory", action="store", type="string", dest="extract_dir", default="ucs_extract_dir", help="name of directory to extract to")
139 parser.add_option("--original-ucs-ip", action="store", type="string", dest="original_ucs_ip", help="ip in original ucs")
140 parser.add_option("--original-ucs-gateway", action="store", type="string", dest="original_ucs_gateway", help="gateway in original ucs")
141 parser.add_option("--dest-ip", action="store", type="string", dest="dest_ip", help="ip of destination instance")
142 parser.add_option("--dest-gateway", action="store", type="string", dest="dest_gateway", help="gateway of destination instance")
143 (options, args) = parser.parse_args()
144
145 # Set variables from options
146 debug_level = options.debug_level
147 cloud_provider = options.cloud_provider
148 original_ucs = options.original_ucs
149 updated_ucs = options.updated_ucs
150 extract_ucs_dir = options.extract_dir
151 original_ucs_ip = options.original_ucs_ip
152 original_ucs_gateway = options.original_ucs_gateway
153 dest_ip = options.dest_ip
154 dest_gateway = options.dest_gateway
155
156 if not original_ucs or not cloud_provider:
157 print "Usage: "
158 print (" ./%s --cloud-provider <aws | azure | gce> --original-ucs <ucs_filename>" % (sys.argv[0]))
159 print (" ./%s --cloud-provider <aws | azure | gce> --original-ucs <ucs_filename> --updated-ucs <ucs_filename>" % (sys.argv[0]))
160 print "ex. "
161 print (" ./%s --cloud-provider aws --original-ucs original.ucs --updated-ucs updated.ucs" % (sys.argv[0]))
162 sys.exit()
163
164 # Open files
165 tar_original = tarfile.open(original_ucs, "r:gz")
166 tar_updated = tarfile.open(updated_ucs, "w:gz")
167
168 try:
169 tar_original.extractall(path=extract_ucs_dir)
170 except IOError as e:
171 sys.exit("I/O Error({0}): {1} - If the latest UCS file is corrupted and/or the current primary host is stuck at BECOMING_PRIMARY state, the corrupted UCS file needs to be deleted as well as primary host needs to be terminated to allow re-elect the new primary.'".format(e.errno, e.strerror))
172
173
174 bigip_base_file = "/config/bigip_base.conf"
175 if cloud_provider == 'aws':
176 gateway_file = "/config/partitions/LOCAL_ONLY/bigip.conf"
177 else:
178 gateway_file = "/config/bigip.conf"
179
180 # Grab instance's hostname from UCS
181 dest_hostname = get_hostname(bigip_base_file)
182 original_hostname = get_hostname(extract_ucs_dir + bigip_base_file)
183
184 # Grab instance's IP from UCS or local config file
185 if not original_ucs_ip:
186 original_ucs_ip = get_ip(extract_ucs_dir + bigip_base_file)
187 if not original_ucs_gateway:
188 original_ucs_gateway = get_gateway(extract_ucs_dir + gateway_file)
189 if not dest_ip:
190 if os.path.isfile(gateway_file):
191 dest_ip = get_ip(bigip_base_file)
192 else:
193 dest_ip = get_local_ip()
194 if not dest_gateway:
195 if os.path.isfile(gateway_file):
196 dest_gateway = get_gateway(gateway_file)
197 else:
198 dest_gateway = get_local_gateway()
199
200 if debug_level > 0:
201 print "original_hostname: " + original_hostname
202 print "dest_hostname: " + dest_hostname
203 print "original_ucs_ip: " + original_ucs_ip
204 print "original_ucs_gateway: " + original_ucs_gateway
205 print "dest_ip: " + dest_ip
206 print "dest_gateway: " + dest_gateway
207
208 # Fix string version of addresses with "-". ex. ip-10-0-11-151
209 original_ucs_ip_str = original_ucs_ip.replace(".", "-")
210 dest_ip_str = dest_ip.replace(".", "-")
211
212 if debug_level > 0:
213 print "original_ucs_ip_str: " + original_ucs_ip_str
214 print "dest_ip_str: " + dest_ip_str
215
216 files_to_update = [
217 "/config/bigip_base.conf",
218 "/config/bigip.conf",
219 "/config/BigDB.dat",
220 "/SPEC-Manifest"
221 ]
222
223 # Replace Gateway
224 replace(extract_ucs_dir + gateway_file, original_ucs_gateway, dest_gateway)
225
226 # Replace hostname, IP, String Versions in other files
227 for f in files_to_update:
228 filename = extract_ucs_dir + f
229 if debug_level > 0:
230 print "updating : " + filename
231 replace(filename, original_ucs_ip, dest_ip)
232 replace(filename, original_ucs_ip_str, dest_ip_str)
233 if original_hostname and dest_hostname:
234 replace(filename, original_hostname, dest_hostname)
235
236 # Remove the cloud directory created by the template so we don't overwrite new code with old
237 path_to_exclude = "/config/cloud/" + cloud_provider
238 shutil.rmtree(extract_ucs_dir + path_to_exclude, ignore_errors=True)
239
240 # Remove the cloud-libs private key info as you can't load a ucs w/ a passphrase
241 shutil.rmtree(extract_ucs_dir + '/config/partitions/CloudLibsAutoscale', ignore_errors=True)
242 shutil.rmtree(extract_ucs_dir + '/var/tmp/filestore_temp/files_d/CloudLibsAutoscale_d', ignore_errors=True)
243 shutil.rmtree(extract_ucs_dir + '/config/partitions/CloudLibsLocal', ignore_errors=True)
244 shutil.rmtree(extract_ucs_dir + '/var/tmp/filestore_temp/files_d/CloudLibsLocal_d', ignore_errors=True)
245
246 # Remove the f5-cloud-libs local public keys as they won't match any private keys
247 removeFiles(extract_ucs_dir + '/config/cloud/keys', 'cloudLocal*')
248
249 # remove the dynad private key
250 os.system("sed -i '/sys dynad key {/ { N ; /\\n[[:space:]]\+key[[:space:]]*\$M\$[^\\n]*/ { N; /\\n[[:space:]]*}/ { d } } }' " + extract_ucs_dir + "/config/bigip_base.conf")
251
252 tar_updated.add(extract_ucs_dir, arcname='')
253
254 tar_original.close()
255 tar_updated.close()
256
257 shutil.rmtree(extract_ucs_dir, ignore_errors=False, onerror=None)
258
259 print "UCS Update Complete"
260 print "Load UCS with command below:"
261 print " tmsh load /sys ucs " + os.path.abspath(updated_ucs) + " no-license"
262
263 # Leverage cfn-signal here
264 # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-signal.html
265
266if __name__ == "__main__":
267 main()