-
Notifications
You must be signed in to change notification settings - Fork 34
/
Copy pathboot-eos
executable file
·209 lines (179 loc) · 7.08 KB
/
boot-eos
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
#!/usr/bin/env python3
import argparse
import hashlib
import logging
import os
import re
import shutil
import subprocess
import urllib
import zipfile
sonicMount = "/host"
def verifySWI(swiPath):
zf = zipfile.ZipFile(swiPath, 'r')
reVer = re.compile(r'.*SWI_VERSION=.*(\d+)\.(\d+)\.(\d+).*')
for str in zf.read("version").splitlines():
m = reVer.match(str)
if m:
if int(m.group(1)) >= 4 and int(m.group(2)) >= 19 and int(m.group(3)) >= 0:
return ""
else:
return "Unsupported version of EOS"
return "Cannot find version of EOS in SWI file"
unsupportedPlatforms = ["x86_64-arista_7050_qx32", "x86_64-arista_7050_qx32s"]
def verifyPlatform():
# Check that this script run on an ext4 flash
platform = subprocess.check_output(["sonic-cfggen",
"-v", "DEVICE_METADATA.localhost.platform", "-d"]).rstrip()
if not platform:
platform = subprocess.check_output(["sonic-cfggen",
"-v", "platform", "-m", "/etc/sonic/minigraph.xml"]).rstrip()
return (platform not in unsupportedPlatforms)
def checkSpace(swiPath):
# for EOS we need ~(2 * <swi size>). swi already on fs, so we need additional
# <swi size> bytes.
mb = (1 << 20)
swiMB = os.stat(swiPath).st_size / mb / 100 * 100 + 100
statvfs = os.statvfs(sonicMount)
freeMB = statvfs.f_frsize * statvfs.f_bavail / mb
return swiMB - freeMB
def getEOSFiles(ignoreFile):
res = []
eosFile = os.path.join( sonicMount, "EOS.swi" )
if eosFile != ignoreFile and os.path.exists(eosFile):
res.append(eosFile)
eosFile = os.path.join( sonicMount, ".boot-image.swi" )
if eosFile != ignoreFile and os.path.exists(eosFile):
res.append(eosFile)
return res
def deleteFiles(files):
for file in files:
os.remove(file)
def queryYesNo(question, default="yes"):
"""Ask a yes/no question via raw_input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "yes" (the default), "no" or None (meaning
an answer is required of the user).
The "answer" return value is True for "yes" or False for "no".
"""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
print(question + prompt)
choice = raw_input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
print("Please respond with 'yes' or 'no' "
"(or 'y' or 'n').\n")
def main():
parser = argparse.ArgumentParser(
description='Arista tool to boot EOS',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser._action_groups.pop()
required = parser.add_argument_group('required arguments')
optional = parser.add_argument_group('optional arguments')
required.add_argument('--swi', "-s", help="EOS.swi file", required=True)
optional.add_argument("--md5", "-m", help="MD5 of EOS.swi file")
startupConfig = os.path.join(sonicMount, "startup-config")
startupHelp = "EOS startup-config url. Or you can just put your " \
"startup-config file to %s" % startupConfig
if os.path.exists(startupConfig):
optional.add_argument("--config", "-c", help=startupHelp)
else:
required.add_argument("--config", "-c", help=startupHelp, required=True)
optional.add_argument("--remove", "-r", help="remove old EOS files if there "
"is not enough space for installation of new version of EOS",
action="store_true")
optional.add_argument("--no-reboot", "-n", help="config only (without reboot)",
action="store_true")
args = parser.parse_args()
# 1) check that we have root privileges
if os.geteuid() != 0:
exit("You need to have root privileges to run this script. Please try "
"again using 'sudo'. Exiting.")
# 2) download EOS swi
if args.swi.startswith(('http://', 'https://', 'ftp://')):
swi_path = "/tmp/EOS.swi"
try:
urllib.urlretrieve(args.swi, swi_path)
except IOError as e:
exit("Cannot download SWI '{}'. Error: '{}'. Exiting.".format(
args.swi, e))
else:
swi_path = args.swi
if not os.path.exists(swi_path):
exit("SWI path %s does not exists. Exiting." % swi_path)
# 3) check MD5 of EOS swi
if args.md5:
hash_md5 = hashlib.md5()
with open(swi_path, "rb") as f:
for chunk in iter(lambda: f.read(10 * (1 << 20)), b""):
hash_md5.update(chunk)
if hash_md5.hexdigest() != args.md5:
exit("Invalid MD5 %s. Exiting." % hash_md5.hexdigest())
# 4) check that platform supports ext4
if not verifyPlatform():
exit("Unsupported platform. Exiting.")
# 5) check that EOS supports installation on ext4
err = verifySWI(swi_path)
if err != "":
exit("%s. Exiting." % err)
# 6) check that we have enough free space for EOS installation
need = checkSpace(swi_path)
if need > 0:
filesToDelete = getEOSFiles(swi_path)
if len(filesToDelete):
if args.remove or queryYesNo("There is not enough space. Do you want to "
"delete old EOS files: "):
deleteFiles(filesToDelete)
need = checkSpace(swi_path)
if need > 0:
exit("Do not have enough space. We need additional ~%dMB in %s to install "
"EOS. Exiting." % (need, sonicMount))
# 7) download startup-config
if args.config:
try:
urllib.urlretrieve(args.config, startupConfig)
except Exception as e:
exit("Cannot download startup-config '{}'. Error: '{}'. Exiting.".format(
args.config, e))
# 8) move EOS swi file to /host/EOS.swi
swiName = os.path.basename(swi_path)
swiPath = os.path.join(sonicMount, swiName)
logging.info('moving %s to %s', swi_path, swiPath)
shutil.move(swi_path, swiPath)
# 9) create backup copy of boot-config and create new boot-config to start EOS
bootConfig = os.path.join(sonicMount, "boot-config")
bootConfigBk = os.path.join(sonicMount, "boot-config.sonic")
if os.path.exists(bootConfig):
if os.path.exists(bootConfigBk):
logging.warning('creating backup of current boot-config: %s, but old '
'version of boot-config was found',
bootConfigBk + ".1")
os.rename(bootConfig, bootConfigBk + ".1")
else:
logging.info('creating backup of sonic boot-config: %s', bootConfigBk)
os.rename(bootConfig, bootConfigBk)
with open(bootConfig, 'w+') as f:
logging.info('creating EOS boot-config')
f.write("SWI=flash:/%s" % swiName)
subprocess.call(['sync'])
# 10) reboot to EOS
if not args.no_reboot:
logging.info('rebooting to EOS')
subprocess.call(['reboot', '-f'])
if __name__ == '__main__':
main()