srikanth | 116e6e8 | 2014-08-19 07:22:37 -0700 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # |
| 3 | # Copyright (c) 2013 Big Switch Networks, Inc. |
| 4 | # |
| 5 | # Licensed under the Eclipse Public License, Version 1.0 (the |
| 6 | # "License"); you may not use this file except in compliance with the |
| 7 | # License. You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.eclipse.org/legal/epl-v10.html |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 14 | # implied. See the License for the specific language governing |
| 15 | # permissions and limitations under the License. |
| 16 | # |
| 17 | |
| 18 | import os, atexit |
| 19 | import glob |
| 20 | from subprocess import Popen, PIPE, check_output, CalledProcessError |
| 21 | import sys, traceback, socket |
| 22 | from optparse import OptionParser |
| 23 | from types import StringType |
| 24 | import datetime |
| 25 | import json |
| 26 | import re |
| 27 | import time |
| 28 | import urllib2 |
| 29 | import httplib |
| 30 | import fcntl, shutil |
| 31 | import sys |
| 32 | import tempfile |
| 33 | import stat |
| 34 | |
| 35 | from string import Template |
| 36 | from django.forms import ValidationError |
| 37 | |
| 38 | # Can't import from `import sdncon` -- causes circular dependency! |
| 39 | SDN_ROOT = "/opt/sdnplatform" if not 'SDN_ROOT' in os.environ else os.environ['SDN_ROOT'] |
| 40 | |
| 41 | # Big Switch Networks Enterprise OID |
| 42 | BSN_ENTERPRISE_OID = '.1.3.6.1.4.1.37538' |
| 43 | BSN_ENTERPRISE_OID_CONTROLLER = BSN_ENTERPRISE_OID + '.1' |
| 44 | |
| 45 | |
| 46 | class OsWrapper(): |
| 47 | """ This base class abstracts executing os binaries without using shell in a secure, hardened way. |
| 48 | Things to keep in mind when composing command templates - because we dont use shell, args to the |
| 49 | binaries are presented as items in the list, so no need to de-specialize special characters , for |
| 50 | example, if you want to echo something into a file, command is "echo -e abc\ndef\n" and not |
| 51 | "echo -e \"abc\ndef\n\"", which would then result in the quotes also to be echo'ed. |
| 52 | """ |
| 53 | name = "none" |
| 54 | cmds_lst_for_set = [] |
| 55 | cmds_lst_for_get = [] |
| 56 | sudo_required_for_set = True |
| 57 | sudo_required_for_get = False |
| 58 | def __init__(self, name, this_cmds_list_for_set, this_cmds_list_for_get, is_sudo_reqd_for_set=True, is_sudo_required_for_get=False): |
| 59 | self.name = name |
| 60 | self.cmds_lst_for_set = this_cmds_list_for_set |
| 61 | self.cmds_lst_for_get = this_cmds_list_for_get |
| 62 | self.sudo_required_for_set = is_sudo_reqd_for_set |
| 63 | self.sudo_required_for_get = is_sudo_required_for_get |
| 64 | |
| 65 | self.IP_RE = re.compile(r'^(\d{1,3}\.){3}\d{1,3}$') |
| 66 | self.DomainName_RE = re.compile(r'^([a-zA-Z0-9-]+.?)+$') |
| 67 | |
| 68 | def validate_ip(self, value): |
| 69 | if not self.IP_RE.match(value) or len([x for x in value.split('.') if int(x) < 256]) != 4: |
| 70 | return False, "IP must be in dotted decimal format, 234.0.59.1" |
| 71 | return True, "" |
| 72 | |
| 73 | def validate_domain(self, value): |
| 74 | if not self.DomainName_RE.match(value): |
| 75 | return False, "Invalid domain name" |
| 76 | return True, "" |
| 77 | |
| 78 | def exec_cmds(self, cmds_lst, cmds_args, stdout_file_lst): |
| 79 | # traverse through the list of commands needed to set this |
| 80 | # cmd_args is a list of lists of args for each of the commands in the set list. |
| 81 | ret_out_err = {'err': [], 'out': []} |
| 82 | if len(cmds_lst) != len(cmds_args): |
| 83 | # commands to args mismatch, error out and return |
| 84 | # possibly throw an exception here as well |
| 85 | ret_out_err['err'].append("Command and args mismatch") |
| 86 | return ret_out_err |
| 87 | for indx in range(len(cmds_lst)): |
| 88 | #if self.sudo_required_for_set: |
| 89 | # cmd_string = "sudo " |
| 90 | #else: |
| 91 | # cmd_string = "" |
| 92 | cmd_string = "" |
| 93 | cmd_template = Template(cmd_string + cmds_lst[indx]) |
| 94 | args_map = dict({}) |
| 95 | for args_indx in range(len(cmds_args[indx])): |
| 96 | args_map["arg%d"%(args_indx+1)] = cmds_args[indx][args_indx] |
| 97 | cmd_string += cmds_args[indx][args_indx] + " " |
| 98 | full_cmd_string = cmd_template.substitute(args_map) |
| 99 | file_for_stdout = PIPE |
| 100 | if stdout_file_lst != None and stdout_file_lst[indx] != "": |
| 101 | file_for_stdout = open(stdout_file_lst[indx], 'a') |
| 102 | sub_proc_output = Popen(full_cmd_string.rsplit(" "), shell=False, stdin=PIPE, stdout=file_for_stdout, stderr=PIPE, close_fds=True) |
| 103 | ret_out_err['err'].append([sub_proc_output.stderr.read()]) |
| 104 | if file_for_stdout == PIPE: |
| 105 | ret_out_err['out'].append([sub_proc_output.stdout.read()]) |
| 106 | else: |
| 107 | file_for_stdout.close() |
| 108 | # not fit for pipe io - os.system(cmd_string) # need to check for errors in commands. |
| 109 | return ret_out_err |
| 110 | |
| 111 | def exec_cmds_new(self, cmds_lst, cmds_args, useShell=False, appendStdOut=True): |
| 112 | # traverse through the list of commands needed to set this |
| 113 | # cmd_args is a list of lists of args for each of the commands in the set list. |
| 114 | ret_out_err = {'err': [], 'out': []} |
| 115 | if len(cmds_lst) != len(cmds_args): |
| 116 | # commands to args mismatch, error out and return |
| 117 | # possibly throw an exception here as wella |
| 118 | # cmds_list is a list of dict maps - [{'bin_name': <bin>, 'args_lst': <args-list>, 'stdoutfile':<filename>},...] |
| 119 | ret_out_err['err'].append("Command and args mismatch") |
| 120 | print 'cmds_lst:', cmds_lst |
| 121 | print 'cmd_args:', cmds_args |
| 122 | return ret_out_err |
| 123 | for indx in range(len(cmds_lst)): |
| 124 | #if self.sudo_required_for_set: |
| 125 | # cmd_string = "sudo " |
| 126 | #else: |
| 127 | # cmd_string = "" |
| 128 | #cmd_string = "" |
| 129 | cmd_args_lst = [cmds_lst[indx]['bin_name']] |
| 130 | |
| 131 | args_map = dict({}) |
| 132 | for args_indx in range(len(cmds_args[indx])): |
| 133 | args_map["arg%d"%(args_indx+1)] = cmds_args[indx][args_indx] |
| 134 | #cmd_string += cmds_args[indx][args_indx] + " " |
| 135 | if 'args_lst' in cmds_lst[indx]: |
| 136 | for args_indx in range(len(cmds_lst[indx]['args_lst'])): |
| 137 | arg_template = Template(cmds_lst[indx]['args_lst'][args_indx]) |
| 138 | full_arg_string = arg_template.substitute(args_map) |
| 139 | cmd_args_lst.append(full_arg_string) |
| 140 | file_for_stdout = PIPE |
| 141 | if 'stdoutfile' in cmds_lst[indx] and cmds_lst[indx]['stdoutfile'] != "": |
| 142 | fMode = 'a' |
| 143 | if not appendStdOut: |
| 144 | fMode = 'r+' |
| 145 | file_for_stdout = open(cmds_lst[indx]['stdoutfile'] , fMode) |
| 146 | sub_proc_output = Popen(cmd_args_lst, shell=useShell, stdin=PIPE, stdout=file_for_stdout, stderr=PIPE, close_fds=True) |
| 147 | ret_out_err['err'].append(sub_proc_output.stderr.read()) |
| 148 | if file_for_stdout == PIPE: |
| 149 | ret_out_err['out'].append(sub_proc_output.stdout.read()) |
| 150 | else: |
| 151 | file_for_stdout.close() |
| 152 | # not fit for pipe io - os.system(cmd_string) # need to check for errors in commands. |
| 153 | return ret_out_err |
| 154 | |
| 155 | def set(self, cmds_args, stdout_file_lst): |
| 156 | return self.exec_cmds(self.cmds_lst_for_set, cmds_args, stdout_file_lst) |
| 157 | def get(self, cmds_args, stdout_file_lst): |
| 158 | return self.exec_cmds(self.cmds_lst_for_get, cmds_args, stdout_file_lst) |
| 159 | |
| 160 | def set_new(self, cmds_args_lst = [], cmds_args = [], useShell=False, appendStdOut = False): |
| 161 | if cmds_args_lst == []: |
| 162 | cmds_args_lst = self.cmds_lst_for_set |
| 163 | return self.exec_cmds_new(cmds_args_lst, cmds_args, useShell, appendStdOut) |
| 164 | def get_new(self, cmds_args_lst = [], cmds_args = [], useShell=False, appendStdOut = False): |
| 165 | if cmds_args_lst == []: |
| 166 | cmds_args_lst = self.cmds_lst_for_get |
| 167 | return self.exec_cmds_new(cmds_args_lst, cmds_args, useShell, appendStdOut) |
| 168 | |
| 169 | |
| 170 | def validate_input1(validator, value): #temporarily disabling this - TBD |
| 171 | try: |
| 172 | validator(value) |
| 173 | except ValidationError, _err: |
| 174 | return False |
| 175 | return True |
| 176 | |
| 177 | |
| 178 | def validate_input(validator, value): |
| 179 | a = True |
| 180 | #try: |
| 181 | #a, b = validator(value) |
| 182 | #except ValidationError, err: |
| 183 | # return False |
| 184 | return a |
| 185 | |
| 186 | |
| 187 | def dotted_decimal_to_int(ip): |
| 188 | """ |
| 189 | Converts a dotted decimal IP address string to a 32 bit integer |
| 190 | """ |
| 191 | bytes = ip.split('.') |
| 192 | ip_int = 0 |
| 193 | for b in bytes: |
| 194 | ip_int = (ip_int << 8) + int(b) |
| 195 | return ip_int |
| 196 | |
| 197 | |
| 198 | def same_subnet(ip1, ip2, netmask): |
| 199 | """ |
| 200 | Checks whether the two ip addresses are on the same subnet as |
| 201 | determined by the netmask argument. All of the arguments are |
| 202 | dotted decimal IP address strings. |
| 203 | """ |
| 204 | if ip1 == '' or ip2 == '' or netmask == '': |
| 205 | return False |
| 206 | ip1_int = dotted_decimal_to_int(ip1) |
| 207 | ip2_int = dotted_decimal_to_int(ip2) |
| 208 | netmask_int = dotted_decimal_to_int(netmask) |
| 209 | return (ip1_int & netmask_int) == (ip2_int & netmask_int) |
| 210 | |
| 211 | |
| 212 | class NetworkConfig(OsWrapper): |
| 213 | def __init__(self, name="network_config"): |
| 214 | OsWrapper.__init__(self, name, [], []) |
| 215 | |
| 216 | |
| 217 | def rewrite_etc_network_interfaces(self, controller, interfaces, ret_result): |
| 218 | """ |
| 219 | Return True when the /etc/network/interfaces is rewritten. Return False otherwise. |
| 220 | The file is rewritten only when the intended new contents is different from |
| 221 | the old contents, this is an attempt to not purturb the network unless something |
| 222 | really changed. |
| 223 | """ |
| 224 | |
| 225 | gateway = controller['fields']['default_gateway'] |
| 226 | if (gateway != ''): |
| 227 | (r, m) = self.validate_ip(gateway) |
| 228 | if not r: |
| 229 | ret_result['err'].append("Default gateway: %s" % m) |
| 230 | gateway = '' |
| 231 | |
| 232 | changed = False |
| 233 | new_conf = [] |
| 234 | new_conf.append("# WARNING this file is automanaged by BSN controller\n") |
| 235 | new_conf.append("# DO NOT EDIT here, use CLI with 'configure'\n") |
| 236 | new_conf.append("auto lo\niface lo inet loopback\n\n") |
| 237 | for interface in interfaces: |
| 238 | if (interface['fields']['controller'] == controller['pk']): |
| 239 | num = interface['fields']['number'] |
| 240 | new_conf.append("auto eth{0}\n".format(num)) |
| 241 | if (interface['fields']['mode'] == 'dhcp'): |
| 242 | new_conf.append("iface eth{0} inet dhcp\n".format(num)) |
| 243 | else: |
| 244 | ip = interface['fields']['ip'] |
| 245 | netmask = interface['fields']['netmask'] |
| 246 | if (ip != ""): |
| 247 | (r, m) = self.validate_ip(ip) |
| 248 | if not r: |
| 249 | ret_result['err'].append( |
| 250 | "Ethernet %s IP address %s: %s" % (num, ip, m)) |
| 251 | ip = "" |
| 252 | if (netmask != ""): |
| 253 | (r, m) = self.validate_ip(netmask) |
| 254 | if not r: |
| 255 | ret_result['err'].append( |
| 256 | "Ethernet %s netmask %s: %s" % (num, netmask, m)) |
| 257 | netmask = "" |
| 258 | |
| 259 | new_conf.append("iface eth{0} inet static\n".format(num)) |
| 260 | if (ip != ""): |
| 261 | new_conf.append(" address {0}\n".format(ip)) |
| 262 | if (netmask != ""): |
| 263 | new_conf.append(" netmask {0}\n".format(netmask)) |
| 264 | if same_subnet(gateway, ip, netmask): |
| 265 | new_conf.append(" gateway {0}\n".format(gateway)) |
| 266 | new_conf.append("\n") |
| 267 | |
| 268 | f = open("/etc/network/interfaces", "r") |
| 269 | if (''.join(new_conf) != f.read()): |
| 270 | f.close() |
| 271 | f = open("/etc/network/interfaces", "w") |
| 272 | f.write(''.join(new_conf)) |
| 273 | changed = True |
| 274 | f.close() |
| 275 | return changed |
| 276 | |
| 277 | def rewrite_etc_resolve_conf(self, controller, dns_servers, ret_result): |
| 278 | """ |
| 279 | Return True when the /etc/resolv.conf is rewritten. Return False otherwise. |
| 280 | The file is rewritten only when the intended new contents is different from |
| 281 | the old contents, this is an attempt to not purturb the network unless something |
| 282 | really changed. |
| 283 | """ |
| 284 | |
| 285 | changed = False |
| 286 | new_conf = [] |
| 287 | domain_name = controller['fields']['domain_name'] |
| 288 | if (domain_name != ""): |
| 289 | new_conf.append("domain {0}\nsearch {1}\n".format(domain_name, |
| 290 | domain_name)) |
| 291 | |
| 292 | if (controller['fields']['domain_lookups_enabled'] == True): |
| 293 | for dns in dns_servers: |
| 294 | if (dns['fields']['controller'] == controller['pk']): |
| 295 | ip = dns['fields']['ip'] |
| 296 | if (ip != ""): |
| 297 | (r, m) = self.validate_ip(ip) |
| 298 | if not r: |
| 299 | ret_result['err'].append("Name server %s: %s" % (ip, m)) |
| 300 | ip = "" |
| 301 | if (ip != ""): |
| 302 | new_conf.append("nameserver {0}\n".format(ip)) |
| 303 | |
| 304 | f = open("/etc/resolv.conf", "r") |
| 305 | if (''.join(new_conf) != f.read()): |
| 306 | f.close() |
| 307 | f = open("/etc/resolv.conf", "w") |
| 308 | f.write(''.join(new_conf)) |
| 309 | changed = True |
| 310 | |
| 311 | f.close() |
| 312 | return changed |
| 313 | |
| 314 | def set(self, args_list): |
| 315 | # args_list: [controllers, controlleDomainServers, controllerInterfaces] |
| 316 | # controllerInterfaces may be empty, which requess no rewrite |
| 317 | # of /etc/network/insterfaces |
| 318 | ifs_rewrite = False if len(args_list) < 3 else True |
| 319 | |
| 320 | ret_result = {'err': [], 'out': []} |
| 321 | controller = json.loads(args_list[0])[0] |
| 322 | network_restart = True |
| 323 | |
| 324 | rc_changed = False |
| 325 | ni_changed = False |
| 326 | |
| 327 | try: |
| 328 | domain_name = controller['fields']['domain_name'] |
| 329 | new_rc = True |
| 330 | if domain_name != "": |
| 331 | (r, m) = self.validate_domain(domain_name) |
| 332 | if not r: |
| 333 | ret_result['err'].append("Search domain %s: %s" |
| 334 | % (domain_name, m)) |
| 335 | new_rc = False |
| 336 | |
| 337 | if new_rc: |
| 338 | rc_changed = self.rewrite_etc_resolve_conf(controller, |
| 339 | json.loads(args_list[1]), |
| 340 | ret_result) |
| 341 | |
| 342 | if ifs_rewrite: |
| 343 | ni_changed = self.rewrite_etc_network_interfaces(controller, |
| 344 | json.loads(args_list[2]), |
| 345 | ret_result) |
| 346 | except Exception, _e: |
| 347 | network_restart = False |
| 348 | traceback.print_exc() |
| 349 | |
| 350 | # don't restart the network config if resolv.conf was only updated |
| 351 | if network_restart and ni_changed: |
| 352 | # Kill any dhclients that might be hanging around. When |
| 353 | # switching from dhcp to static config, networking restart |
| 354 | # won't kill these since the file has already been |
| 355 | # rewritten with a config that doesn't include DHCP. A |
| 356 | # cleaner fix for this is to stop networking before |
| 357 | # writing the file and then start it after writing the |
| 358 | # file. Longer-term, it might be better to use |
| 359 | # NetworkManager APIs for all of this rather than trying |
| 360 | # to manage this config file. |
| 361 | k = Popen(["/usr/bin/killall", "dhclient3"], |
| 362 | stdout=PIPE, stderr=PIPE) |
| 363 | k.wait() |
| 364 | |
| 365 | p = Popen(["/usr/sbin/invoke-rc.d", |
| 366 | "networking", |
| 367 | "restart"], |
| 368 | stdout=PIPE, stderr=PIPE) |
| 369 | p.wait() |
| 370 | |
| 371 | if (0 != p.returncode): |
| 372 | out = p.stdout.read() |
| 373 | ret_result['err'].append("Network restart failed:" |
| 374 | "%d: %s" % (p.returncode, out)) |
| 375 | |
| 376 | # Restart the discover-ip since the controller-interface table has been modified |
| 377 | ip = Popen(["/usr/sbin/service", |
| 378 | "discover-ip", |
| 379 | "restart"], |
| 380 | stdout=PIPE, stderr=PIPE) |
| 381 | |
| 382 | # not concerned with error messages from the requested command |
| 383 | |
| 384 | return ret_result |
| 385 | |
| 386 | class DirectNetworkConfig(NetworkConfig): |
| 387 | def __init__(self, name="direct_network_config"): |
| 388 | NetworkConfig.__init__(self, name) |
| 389 | |
| 390 | def set(self, args_list): |
| 391 | gateway = '' |
| 392 | ip = '' |
| 393 | netmask = '' |
| 394 | domain_name = '' |
| 395 | dns1 = '' |
| 396 | dns2 = '' |
| 397 | domain_lookups_enabled = False |
| 398 | if args_list[0] == 'static': |
| 399 | ip = args_list[1] |
| 400 | netmask = args_list[2] |
| 401 | gateway = args_list[3] |
| 402 | if len(args_list) >= 5: |
| 403 | domain_name = args_list[4] |
| 404 | if len(args_list) >= 6: |
| 405 | domain_lookups_enabled = True |
| 406 | dns1 = args_list[5] |
| 407 | if len(args_list) >= 7: |
| 408 | dns2 = args_list[6] |
| 409 | else: |
| 410 | if len(args_list) >= 2: |
| 411 | domain_name = args_list[1] |
| 412 | if len(args_list) >= 3: |
| 413 | domain_lookups_enabled = True |
| 414 | dns1 = args_list[2] |
| 415 | if len(args_list) >= 4: |
| 416 | dns2 = args_list[3] |
| 417 | |
| 418 | controller_interface=[{'fields' : {'id' : "localhost|ethernet|0", |
| 419 | 'type' : "ethernet", 'number' : 0, |
| 420 | 'mode' : args_list[0], 'ip' : ip, |
| 421 | 'netmask' : netmask, 'controller' : 'localhost'}}] |
| 422 | nameservers = [] |
| 423 | if dns1 != '': |
| 424 | nameservers.append({ 'fields' : {'controller' : "localhost", 'priority' : 1, 'ip' : dns1}}) |
| 425 | if dns2 != '': |
| 426 | nameservers.append({ 'fields' : {'controller' : "localhost", 'priority' : 2, 'ip' : dns2}}) |
| 427 | controller=[{'fields' : {'id' : "localhost", 'domain_name' : domain_name, |
| 428 | 'default_gateway' : gateway, |
| 429 | 'domain_lookups_enabled' : domain_lookups_enabled}, 'pk' : 'localhost'}] |
| 430 | return NetworkConfig.set(self, [json.dumps(controller), json.dumps(nameservers), |
| 431 | json.dumps(controller_interface)]) |
| 432 | |
| 433 | |
| 434 | class UfwCommand(OsWrapper): |
| 435 | def __init__(self, name = "executeufwcommand"): |
| 436 | OsWrapper.__init__(self, name, [], []) |
| 437 | def set(self, arg_list): |
| 438 | args = arg_list[0].split(" ") |
| 439 | self.cmds_lst_for_set = [{'bin_name' : '/usr/sbin/ufw', 'args_lst' : args}] |
| 440 | return OsWrapper.set_new(self, [], [[]]) |
| 441 | |
| 442 | NTP_CONF = """tinker panic 0 |
| 443 | driftfile /var/lib/ntp/ntp.drift |
| 444 | |
| 445 | # Enable this if you want statistics to be logged. |
| 446 | #statsdir /var/log/ntpstats/ |
| 447 | |
| 448 | statistics loopstats peerstats clockstats |
| 449 | filegen loopstats file loopstats type day enable |
| 450 | filegen peerstats file peerstats type day enable |
| 451 | filegen clockstats file clockstats type day enable |
| 452 | |
| 453 | # Specify one or more NTP servers. |
| 454 | |
| 455 | # Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board |
| 456 | # on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for |
| 457 | # more information. |
| 458 | server %s |
| 459 | |
| 460 | # Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for |
| 461 | # details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions> |
| 462 | # might also be helpful. |
| 463 | # |
| 464 | # Note that "restrict" applies to both servers and clients, so a configuration |
| 465 | # that might be intended to block requests from certain clients could also end |
| 466 | # up blocking replies from your own upstream servers. |
| 467 | |
| 468 | # By default, exchange time with everybody, but don't allow configuration. |
| 469 | restrict -4 default kod notrap nomodify nopeer noquery |
| 470 | restrict -6 default kod notrap nomodify nopeer noquery |
| 471 | |
| 472 | # Local users may interrogate the ntp server more closely. |
| 473 | restrict 127.0.0.1 |
| 474 | restrict ::1 |
| 475 | |
| 476 | # Clients from this (example!) subnet have unlimited access, but only if |
| 477 | # cryptographically authenticated. |
| 478 | #restrict 192.168.123.0 mask 255.255.255.0 notrust |
| 479 | |
| 480 | |
| 481 | # If you want to provide time to your local subnet, change the next line. |
| 482 | # (Again, the address is an example only.) |
| 483 | #broadcast 192.168.123.255 |
| 484 | |
| 485 | # If you want to listen to time broadcasts on your local subnet, de-comment the |
| 486 | # next lines. Please do this only if you trust everybody on the network! |
| 487 | #disable auth |
| 488 | #broadcastclient |
| 489 | """ |
| 490 | |
| 491 | class SetNtpServer(OsWrapper): |
| 492 | def __init__(self, name = "setntpserver"): |
| 493 | OsWrapper.__init__(self, name, [], []) |
| 494 | def set(self, args_list): |
| 495 | ret_result = {'err': [], 'out': []} |
| 496 | server = "127.127.1.0" |
| 497 | if (len(args_list) > 0 and |
| 498 | args_list[0] is not None and |
| 499 | args_list[0] != ""): |
| 500 | server = str(args_list[0]) |
| 501 | ntpconf = NTP_CONF % server |
| 502 | changed = False |
| 503 | |
| 504 | f = open("/etc/ntp.conf", "r") |
| 505 | if (''.join(ntpconf) != f.read()): |
| 506 | f.close() |
| 507 | f = open("/etc/ntp.conf", "w") |
| 508 | f.write(''.join(ntpconf)) |
| 509 | changed = True |
| 510 | |
| 511 | if changed: |
| 512 | ntp = Popen(["/usr/sbin/service", |
| 513 | "ntp", |
| 514 | "restart"], |
| 515 | stdout=PIPE, stderr=PIPE) |
| 516 | return ret_result |
| 517 | |
| 518 | |
| 519 | class SetTimezone(OsWrapper): |
| 520 | def __init__(self, name = "settimezone"): |
| 521 | OsWrapper.__init__(self, name, [], []) |
| 522 | def set(self, args_list): |
| 523 | self.cmds_lst_for_set = [ |
| 524 | {'bin_name' : '/bin/bash', |
| 525 | 'args_lst' : ['-c', 'echo "%s" >/etc/timezone' % (str(args_list[0]), )]}, |
| 526 | {'bin_name' : '/bin/bash', |
| 527 | 'args_lst' : ['-c', '/usr/sbin/dpkg-reconfigure -f noninteractive tzdata 2>&1']}, |
| 528 | {'bin_name' : '/sbin/initctl', |
| 529 | 'args_lst' : ['restart', 'rsyslog', ]}, |
| 530 | ] |
| 531 | return OsWrapper.set_new(self, [], [[], [], []]) |
| 532 | |
| 533 | |
| 534 | class UnsetTimezone(OsWrapper): |
| 535 | def __init__(self, name = "unsettimezone"): |
| 536 | OsWrapper.__init__(self, name, [], []) |
| 537 | def set(self, args_list): |
| 538 | self.cmds_lst_for_set = [ |
| 539 | {'bin_name' : '/bin/bash', |
| 540 | 'args_lst' : ['-c', 'echo "Etc/UTC" >/etc/timezone']}, |
| 541 | {'bin_name' : '/bin/bash', |
| 542 | 'args_lst' : ['-c', '/usr/sbin/dpkg-reconfigure -f noninteractive tzdata 2>&1']}, |
| 543 | ] |
| 544 | return OsWrapper.set_new(self, [], [[], []]) |
| 545 | |
| 546 | class SetSyslogServer(OsWrapper): |
| 547 | def __init__(self, name = "setsyslogserver"): |
| 548 | OsWrapper.__init__(self, name, [], []) |
| 549 | def set(self, args_list): |
| 550 | self.cmds_lst_for_set = [ |
| 551 | {'bin_name' : '/bin/sed', |
| 552 | 'args_lst' : ['-i', '/^*.* @/d', '/etc/rsyslog.conf']}, |
| 553 | {'bin_name' : '/bin/echo', |
| 554 | 'args_lst' : ['*.' + str(args_list[1]) + ' @' + str(args_list[0])], 'stdoutfile' : '/etc/rsyslog.conf'}, |
| 555 | {'bin_name' : '/sbin/initctl', |
| 556 | 'args_lst' : ['restart', 'rsyslog']}, |
| 557 | ] |
| 558 | return OsWrapper.set_new(self, [], [[], [], []]) |
| 559 | |
| 560 | class UnsetSyslogServer(OsWrapper): |
| 561 | def __init__(self, name = "unsetsyslogserver"): |
| 562 | OsWrapper.__init__(self, name, [], []) |
| 563 | def set(self, args_list): |
| 564 | self.cmds_lst_for_set = [ |
| 565 | {'bin_name' : '/bin/sed', |
| 566 | 'args_lst' : ['-i', '/^*./d', '/etc/rsyslog.conf']}, |
| 567 | {'bin_name' : '/sbin/initctl', |
| 568 | 'args_lst' : ['restart', 'rsyslog']} |
| 569 | ] |
| 570 | return OsWrapper.set_new(self, [], [[], []]) |
| 571 | |
| 572 | class DateTime(OsWrapper): |
| 573 | def __init__(self, name = "getdatetime"): |
| 574 | OsWrapper.__init__(self, name, [], []) |
| 575 | def set(self, args_list): |
| 576 | # If we're getting or setting the clock then the first argument |
| 577 | # should be either 'utc' or 'local' to indicate whether or not to |
| 578 | # use local time. |
| 579 | # If we're setting the clock there's a second argument which is the |
| 580 | # time to set, formatted as shown in set_cmd_args below. |
| 581 | set_parts = args_list[1].split(':') |
| 582 | set_param = '%2.2s%2.2s%2.2s%2.2s%4.4s.%2.2s' % ( |
| 583 | set_parts[1], |
| 584 | set_parts[2], |
| 585 | set_parts[3], |
| 586 | set_parts[4], |
| 587 | set_parts[0], |
| 588 | set_parts[5], |
| 589 | ) |
| 590 | cmd_args = [str(set_param)] |
| 591 | if str(args_list[0]).lower() == 'utc': |
| 592 | cmd_args.insert(0, '-u') |
| 593 | #date_info = args_list[0] |
| 594 | #is_utc = args_list[1] |
| 595 | #date_str = "%s:%s:%s:%s:%s:%s:%s" |
| 596 | self.cmds_lst_for_set = [ |
| 597 | {'bin_name' : '/bin/date', 'args_lst' : cmd_args}, |
| 598 | ] |
| 599 | OsWrapper.set_new(self, [], [[]]) |
| 600 | return self.get(args_list) |
| 601 | |
| 602 | def get(self, args_list): |
| 603 | cmd_args = ['+%Y:%m:%d:%H:%M:%S:%Z'] |
| 604 | if str(args_list[0]).lower() == 'utc': |
| 605 | cmd_args.insert(0, '-u') |
| 606 | self.cmds_lst_for_get = [ |
| 607 | {'bin_name' : '/bin/date', 'args_lst' : cmd_args}, |
| 608 | ] |
| 609 | return OsWrapper.get_new(self, [], [[]]) |
| 610 | |
| 611 | class SetControllerId(OsWrapper): |
| 612 | def __init__(self, name = "setcontrollerid"): |
| 613 | OsWrapper.__init__(self, name, [], []) |
| 614 | def set(self, args_list): |
| 615 | self.cmds_lst_for_set = [ |
| 616 | {'bin_name' : '/bin/sed', |
| 617 | 'args_lst' : ['-i', '/^controller-id=/d', "%s/run/boot-config" % SDN_ROOT]}, |
| 618 | {'bin_name' : '/bin/bash', |
| 619 | 'args_lst' : ['-c', 'echo "controller-id=%s" >> %s/run/boot-config' % ( |
| 620 | str(args_list[0]), SDN_ROOT) |
| 621 | ] |
| 622 | }, |
| 623 | ] |
| 624 | return OsWrapper.set_new(self, [], [[], []]) |
| 625 | |
| 626 | class HAFailback(OsWrapper): |
| 627 | def __init__(self, name = "hafailback"): |
| 628 | OsWrapper.__init__(self, name, [], []) |
| 629 | def set(self, args_list): |
| 630 | self.cmds_lst_for_set = [ |
| 631 | {'bin_name' : '/bin/touch', |
| 632 | 'args_lst' : ["%s/force-one-time-health-check-failure" % SDN_ROOT]}, |
| 633 | ] |
| 634 | return OsWrapper.set_new(self, [], [[]]) |
| 635 | |
| 636 | #class HAFailover(OsWrapper): |
| 637 | # def __init__(self, name = "hafailover"): |
| 638 | # OsWrapper.__init__(self, name, [], []) |
| 639 | # def set(self, args_list): |
| 640 | # self.cmds_lst_for_set = [ |
| 641 | # {'bin_name' : '/bin/bash', |
| 642 | # 'args_lst' : ["%s/sys/bin/ha-failover.sh" % SDN_ROOT, str(args_list[0])]}, |
| 643 | # ] |
| 644 | # return OsWrapper.set_new(self, [], [[]]) |
| 645 | |
| 646 | class SetVrrpVirtualRouterId(OsWrapper): |
| 647 | def __init__(self, name = "setvrrpvirtualrouterid"): |
| 648 | OsWrapper.__init__(self, name, [], []) |
| 649 | def set(self, args_list): |
| 650 | self.cmds_lst_for_set = [ |
| 651 | {'bin_name' : '/bin/bash', |
| 652 | 'args_lst' : ["%s/sys/bin/set-vrrp-virtual-router-id.sh" % SDN_ROOT, str(args_list[0])]}, |
| 653 | ] |
| 654 | return OsWrapper.set_new(self, [], [[]]) |
| 655 | |
| 656 | |
| 657 | def write_controller_restarted(): |
| 658 | """ |
| 659 | Write the controller started file with the current timestamp + 5 seconds. This will |
| 660 | ensure that health check script ignores the state of sdnplatform for 5 seconds |
| 661 | from current time |
| 662 | """ |
| 663 | f = open("/var/run/sdnplatform-healthcheck-disabled", "w") |
| 664 | # write time converted to int and then string |
| 665 | f.write(str(5 + long(time.time()))) |
| 666 | f.close() |
| 667 | |
| 668 | |
| 669 | class SetStaticFlowOnlyConfig(OsWrapper): |
| 670 | def __init__(self, name = "setstaticflowonlyconfig"): |
| 671 | OsWrapper.__init__(self, name, [], []) |
| 672 | def set(self, args_list): |
| 673 | |
| 674 | # write the controller started file. do it before actually resetting |
| 675 | # so that there is no race condition with health check script |
| 676 | try: |
| 677 | write_controller_restarted() |
| 678 | except Exception, _e: |
| 679 | traceback.print_exc() |
| 680 | |
| 681 | self.cmds_lst_for_set = [ |
| 682 | {'bin_name' : '/bin/touch', |
| 683 | 'args_lst' : ["%s/feature/staticflowonlyconfig" % SDN_ROOT]}, |
| 684 | {'bin_name' : '/sbin/initctl', |
| 685 | 'args_lst' : ['restart', 'sdnplatform']}, |
| 686 | ] |
| 687 | return OsWrapper.set_new(self, [], [[], [], []]) |
| 688 | |
| 689 | class RestartSDNPlatform(OsWrapper): |
| 690 | def __init__(self, name = "restartsdnplatform"): |
| 691 | OsWrapper.__init__(self, name, [], []) |
| 692 | def set(self, args_list): |
| 693 | |
| 694 | # write the controller started file. do it before actually resetting |
| 695 | # so that there is no race condition with health check script |
| 696 | try: |
| 697 | write_controller_restarted() |
| 698 | except Exception, _e: |
| 699 | traceback.print_exc() |
| 700 | |
| 701 | self.cmds_lst_for_set = [ |
| 702 | {'bin_name' : '/sbin/initctl', |
| 703 | 'args_lst' : ['restart', 'sdnplatform']}, |
| 704 | ] |
| 705 | return OsWrapper.set_new(self, [], [[]]) |
| 706 | |
| 707 | class AbortUpgrade(OsWrapper): |
| 708 | def __init__(self, name = "abortupgrade"): |
| 709 | OsWrapper.__init__(self, name, [], []) |
| 710 | def set(self, args_list): |
| 711 | |
| 712 | self.cmds_lst_for_set = [ |
| 713 | {'bin_name' : '/opt/sdnplatform/sys/bin/abort_upgrade.sh', |
| 714 | 'args_lst' : []}, |
| 715 | ] |
| 716 | return OsWrapper.set_new(self, [], [[]]) |
| 717 | |
| 718 | class SetDefaultConfig(OsWrapper): |
| 719 | def __init__(self, name = "setdefaultconfig"): |
| 720 | OsWrapper.__init__(self, name, [], []) |
| 721 | def set(self, args_list): |
| 722 | |
| 723 | # write the controller started file. do it before actually resetting |
| 724 | # so that there is no race condition with health check script |
| 725 | try: |
| 726 | write_controller_restarted() |
| 727 | except Exception, _e: |
| 728 | traceback.print_exc() |
| 729 | |
| 730 | self.cmds_lst_for_set = [ |
| 731 | {'bin_name' : '/bin/rm', |
| 732 | 'args_lst' : ['-f', "%s/feature/staticflowonlyconfig" % SDN_ROOT]}, |
| 733 | {'bin_name' : '/sbin/initctl', |
| 734 | 'args_lst' : ['restart', 'sdnplatform']}, |
| 735 | # if there are other configs their flag files need to be deleted here too |
| 736 | ] |
| 737 | return OsWrapper.set_new(self, [], [[], [], []]) |
| 738 | |
| 739 | class SetHostname(OsWrapper): |
| 740 | def __init__(self, name = "sethostname"): |
| 741 | OsWrapper.__init__(self, name, [], []) |
| 742 | def set(self, args_list): |
| 743 | hostname = str(args_list[0]) |
| 744 | self.cmds_lst_for_set = [ |
| 745 | # replace hostname from /etc/hosts |
| 746 | {'bin_name' : '/bin/sed', |
| 747 | 'args_lst' : ['-i', |
| 748 | r's/^127\.0\.1\.1 .*$$/127.0.1.1 %s/' % hostname, |
| 749 | '/etc/hosts']}, |
| 750 | # populate /etc/hosts |
| 751 | {'bin_name' : '/bin/bash', |
| 752 | 'args_lst' : ['-c', 'echo "%s" >/etc/hostname' % hostname]}, |
| 753 | # tell the system about the hostname |
| 754 | {'bin_name' : '/bin/hostname', |
| 755 | 'args_lst' : ['-b', '-F', '/etc/hostname'] }, |
| 756 | ] |
| 757 | return OsWrapper.set_new(self, [], [[], [], [],]) |
| 758 | |
| 759 | # In PAM, "auth" == authentication (surprise!) |
| 760 | # XXX roth -- maybe use PAP instead? |
| 761 | |
| 762 | # 'sufficient' --> tacacs+ and local authentication are enabled |
| 763 | AUTHN_TPL = """\ |
| 764 | auth [default=1 success=ignore] pam_succeed_if.so uid >= 10000 |
| 765 | auth sufficient pam_tacplus.so %(servers)s %(secrets)s %(timeout)s service=login protocol=ip login=login |
| 766 | """ |
| 767 | |
| 768 | # In PAM, "account" == authorization |
| 769 | AUTHZ_TPL = """\ |
| 770 | account [default=1 success=ignore] pam_succeed_if.so uid >= 10000 |
| 771 | account sufficient pam_tacplus.so %(secrets)s %(timeout)s service=login service_av=shell protocol=ip login=login |
| 772 | """ |
| 773 | |
| 774 | # In PAM, "session" == accounting |
| 775 | ACCT_TPL = """\ |
| 776 | session sufficient pam_tacplus.so %(servers)s %(secrets)s %(timeout)s service=login service_av=shell protocol=ip |
| 777 | """ |
| 778 | |
| 779 | # XXX roth -- keep this in sync with the current release |
| 780 | # of /etc/pam.d/sshd |
| 781 | SSHD_TACPLUS_TPL = """\ |
| 782 | # PAM configuration for the Secure Shell service |
| 783 | |
| 784 | # Read environment variables from /etc/environment and |
| 785 | # /etc/security/pam_env.conf. |
| 786 | auth required pam_env.so # [1] |
| 787 | # In Debian 4.0 (etch), locale-related environment variables were moved to |
| 788 | # /etc/default/locale, so read that as well. |
| 789 | auth required pam_env.so envfile=/etc/default/locale |
| 790 | |
| 791 | # Standard Un*x authentication. |
| 792 | %(authn)s |
| 793 | |
| 794 | # Disallow non-root logins when /etc/nologin exists. |
| 795 | account required pam_nologin.so |
| 796 | |
| 797 | # Uncomment and edit /etc/security/access.conf if you need to set complex |
| 798 | # access limits that are hard to express in sshd_config. |
| 799 | # account required pam_access.so |
| 800 | |
| 801 | # Standard Un*x authorization. |
| 802 | %(authz)s |
| 803 | |
| 804 | # Standard Un*x session setup and teardown. |
| 805 | %(acct)s |
| 806 | |
| 807 | # Print the message of the day upon successful login. |
| 808 | session optional pam_motd.so # [1] |
| 809 | |
| 810 | # Print the status of the user's mailbox upon successful login. |
| 811 | session optional pam_mail.so standard noenv # [1] |
| 812 | |
| 813 | # Set up user limits from /etc/security/limits.conf. |
| 814 | session required pam_limits.so |
| 815 | |
| 816 | # Set up SELinux capabilities (need modified pam) |
| 817 | # session required pam_selinux.so multiple |
| 818 | |
| 819 | # Standard Un*x password updating. |
| 820 | @include common-password |
| 821 | """ |
| 822 | |
| 823 | class TacacsPlusConfig(OsWrapper): |
| 824 | |
| 825 | def __init__(self, name="tacacs_plus_config"): |
| 826 | OsWrapper.__init__(self, name, [], []) |
| 827 | self.config = {} |
| 828 | self.hosts = [] |
| 829 | self.result = dict(err=[], out=[]) |
| 830 | |
| 831 | def isEnabled(self): |
| 832 | """Is TACACS+ enabled? |
| 833 | |
| 834 | If any of authn/authz/acct is set, *and* there is a non-empty |
| 835 | set of TACACS+ hosts, then we should enable the PAM plugin. |
| 836 | """ |
| 837 | |
| 838 | if (self.hosts |
| 839 | and (self.config['fields']['tacacs_plus_authn'] |
| 840 | or self.config['fields']['tacacs_plus_authz'] |
| 841 | or self.config['fields']['tacacs_plus_acct'])): |
| 842 | return True |
| 843 | |
| 844 | return False |
| 845 | |
| 846 | def disablePamDefault(self): |
| 847 | """Disable the default PAM setup. |
| 848 | |
| 849 | This is installed by the initial configuration scripts |
| 850 | for the libpam-tacplus DEB. |
| 851 | """ |
| 852 | |
| 853 | if not os.path.exists("/usr/share/pam-configs/tacplus"): |
| 854 | return |
| 855 | |
| 856 | cmd = ("/usr/sbin/pam-auth-update", "--remove", "tacplus",) |
| 857 | pipe = Popen(cmd, stdout=PIPE, stderr=PIPE) |
| 858 | out, err = pipe.communicate() |
| 859 | code = pipe.wait() |
| 860 | |
| 861 | out = (out or "").strip().split("\n") |
| 862 | err = (err or "").strip().split("\n") |
| 863 | |
| 864 | if not code: |
| 865 | self.result['out'].append("disabled tacplus via pam-auth-update\n") |
| 866 | else: |
| 867 | self.result['out'].extend([l + "\n" for l in out]) |
| 868 | self.result['err'].extend([l + "\n" for l in err]) |
| 869 | self.result['err'].append("pam-auth-update failed\n") |
| 870 | |
| 871 | def disablePam(self): |
| 872 | """Disable the TACACS+ PAM plugin.""" |
| 873 | m = dict(authn="@include common-auth", |
| 874 | authz="@include common-account", |
| 875 | acct="@include common-session") |
| 876 | self.writeLocked("/etc/pam.d/sshd", SSHD_TACPLUS_TPL % m) |
| 877 | |
| 878 | def readLocked(self, path): |
| 879 | |
| 880 | fd = open(path, "r") |
| 881 | |
| 882 | fcntl.lockf(fd, fcntl.LOCK_SH) |
| 883 | try: |
| 884 | buf = fd.read() |
| 885 | except Exception, what: |
| 886 | fcntl.lockf(fd, fcntl.LOCK_UN) |
| 887 | fd.close() |
| 888 | self.result['err'].append(str(what) + "\n") |
| 889 | self.result['err'].append("cannot read %s\n" % path) |
| 890 | return None |
| 891 | fcntl.lockf(fd, fcntl.LOCK_UN) |
| 892 | |
| 893 | fd.close() |
| 894 | |
| 895 | return buf |
| 896 | |
| 897 | def writeLocked(self, path, buf, backup=True): |
| 898 | |
| 899 | if backup: |
| 900 | shutil.copy2(path, path + "-") |
| 901 | |
| 902 | fd = open(path, "w") |
| 903 | |
| 904 | fcntl.lockf(fd, fcntl.LOCK_EX) |
| 905 | try: |
| 906 | fd.write(buf) |
| 907 | except Exception, what: |
| 908 | fcntl.lockf(fd, fcntl.LOCK_UN) |
| 909 | fd.close() |
| 910 | self.result['err'].append(str(what) + "\n") |
| 911 | self.result['err'].append("cannot write %s\n" % path) |
| 912 | return |
| 913 | fcntl.lockf(fd, fcntl.LOCK_UN) |
| 914 | |
| 915 | fd.close() |
| 916 | |
| 917 | def enableNss(self): |
| 918 | """Enable the NSS plugin.""" |
| 919 | |
| 920 | buf = self.readLocked("/etc/nsswitch.conf") |
| 921 | if buf is None: return |
| 922 | |
| 923 | p = buf.find("\npasswd:") |
| 924 | q = buf.find("\n", p+8) |
| 925 | if p < 0 or q < 0: |
| 926 | self.result['err'].append("cannot find passwd entry" |
| 927 | " in /etc/nsswitch.conf\n") |
| 928 | return |
| 929 | |
| 930 | f = buf[p+8:q] |
| 931 | if "remoteuser" in f: |
| 932 | self.result['out'].append("remoteuser already enabled" |
| 933 | " in /etc/nssswitch.conf\n") |
| 934 | return |
| 935 | |
| 936 | self.result['out'].append("enabling remoteuser" |
| 937 | " in /etc/nssswitch.conf\n") |
| 938 | f = " " + f.strip() + " remoteuser" |
| 939 | buf = buf[:p+8] + f + buf[q:] |
| 940 | |
| 941 | self.writeLocked("/etc/nsswitch.conf", buf) |
| 942 | |
| 943 | def disableNss(self): |
| 944 | """Disable the NSS plugin.""" |
| 945 | |
| 946 | buf = self.readLocked("/etc/nsswitch.conf") |
| 947 | if buf is None: return |
| 948 | |
| 949 | p = buf.find("\npasswd:") |
| 950 | q = buf.find("\n", p+8) |
| 951 | if p < 0 or q < 0: |
| 952 | self.result['err'].append("cannot find passwd entry" |
| 953 | " in /etc/nsswitch.conf\n") |
| 954 | return |
| 955 | |
| 956 | l = buf[p+8:q].strip().split() |
| 957 | if "remoteuser" not in l: |
| 958 | self.result['out'].append("remoteuser already disabled" |
| 959 | " in /etc/nssswitch.conf\n") |
| 960 | return |
| 961 | |
| 962 | self.result['out'].append("disabling remoteuser" |
| 963 | " in /etc/nssswitch.conf\n") |
| 964 | l.remove("remoteuser") |
| 965 | f = " " + " ".join(l) |
| 966 | buf = buf[:p+8] + f + buf[q:] |
| 967 | |
| 968 | self.writeLocked("/etc/nsswitch.conf", buf) |
| 969 | |
| 970 | def enablePam(self): |
| 971 | """Enable the TACACS+ PAM plugin. |
| 972 | |
| 973 | * construct consolidate server and secret lists |
| 974 | * generate a timeout line |
| 975 | * pick sufficient/required clauses as indicated by enable |
| 976 | flags in the JSON |
| 977 | |
| 978 | See |
| 979 | http://tacplus.git.sourceforge.net/git/gitweb.cgi?p=tacplus/tacplus;a=blob_plain;f=README;hb=HEAD |
| 980 | """ |
| 981 | |
| 982 | # disable TACACS+ while updating |
| 983 | self.disableNss() |
| 984 | self.disablePam() |
| 985 | |
| 986 | # XXX roth -- field is 'ip', but since it is the primary key, |
| 987 | # it get mapped to 'pk'. Go figure. |
| 988 | def svrClause(h): |
| 989 | self.result['out'].append("enabling host %s\n" % h['pk']) |
| 990 | return 'server=%s' % h['pk'] |
| 991 | |
| 992 | servers = " ".join([svrClause(h) for h in self.hosts]) |
| 993 | |
| 994 | def keyClause(h): |
| 995 | """Secret for this host, possibly the global one.""" |
| 996 | if h['fields']['key']: |
| 997 | return "secret=%s" % h['fields']['key'] |
| 998 | if self.config['fields']['key']: |
| 999 | return "secret=%s" % self.config['fields']['key'] |
| 1000 | return "" |
| 1001 | |
| 1002 | secrets = " ".join([keyClause(h) for h in self.hosts]) |
| 1003 | |
| 1004 | m = dict(servers=servers, secrets=secrets) |
| 1005 | |
| 1006 | if self.config['fields']['timeout']: |
| 1007 | m['timeout'] = 'timeout=%s' % self.config['fields']['timeout'] |
| 1008 | else: |
| 1009 | m['timeout'] = '' |
| 1010 | |
| 1011 | isLocal = self.config['fields']['local_authn'] |
| 1012 | isTacacs = self.config['fields']['tacacs_plus_authn'] |
| 1013 | |
| 1014 | authn = [] |
| 1015 | if isTacacs: |
| 1016 | authn.append(AUTHN_TPL % m) |
| 1017 | if isLocal: |
| 1018 | authn.append("@include common-auth") |
| 1019 | |
| 1020 | isLocal = self.config['fields']['local_authz'] |
| 1021 | isTacacs = self.config['fields']['tacacs_plus_authz'] |
| 1022 | |
| 1023 | authz = [] |
| 1024 | if isTacacs: |
| 1025 | authz.append(AUTHZ_TPL % m) |
| 1026 | if isLocal: |
| 1027 | authz.append("@include common-account") |
| 1028 | |
| 1029 | isTacacs = self.config['fields']['tacacs_plus_acct'] |
| 1030 | |
| 1031 | acct = [] |
| 1032 | if isTacacs: |
| 1033 | acct.append(ACCT_TPL % m) |
| 1034 | acct.append("@include common-session") |
| 1035 | |
| 1036 | # enable userid lookups |
| 1037 | self.enableNss() |
| 1038 | |
| 1039 | # write out sshd PAM config as a final step to enable it |
| 1040 | m = dict(authn="\n".join(authn), |
| 1041 | authz="\n".join(authz), |
| 1042 | acct="\n".join(acct)) |
| 1043 | self.writeLocked("/etc/pam.d/sshd", SSHD_TACPLUS_TPL % m) |
| 1044 | |
| 1045 | def set(self, args_list): |
| 1046 | |
| 1047 | self.config = json.loads(args_list[0])[0] |
| 1048 | self.hosts = json.loads(args_list[1]) |
| 1049 | |
| 1050 | self.disablePamDefault() |
| 1051 | |
| 1052 | if not self.isEnabled(): |
| 1053 | try: |
| 1054 | self.disableNss() |
| 1055 | self.disablePam() |
| 1056 | self.result['out'].append('TACACS+ (via PAM) is now disabled\n') |
| 1057 | except Exception: |
| 1058 | traceback.print_exc() |
| 1059 | self.result['err'].append('TACACS+ (via PAM) disable failed\n') |
| 1060 | return self.result |
| 1061 | |
| 1062 | # else, enable the PAM module |
| 1063 | try: |
| 1064 | self.enableNss() |
| 1065 | self.enablePam() |
| 1066 | self.result['out'].append('TACACS+ (via PAM) is now enabled\n') |
| 1067 | except Exception: |
| 1068 | # XXX roth -- maybe back out here and *disable* PAM |
| 1069 | # so that we do not end up with a broken PAM config |
| 1070 | traceback.print_exc() |
| 1071 | self.result['err'].append('TACACS+ (via PAM) enable failed\n') |
| 1072 | |
| 1073 | return self.result |
| 1074 | |
| 1075 | # |
| 1076 | # get_system_version_string |
| 1077 | # |
| 1078 | # Gets the version string of the controller. |
| 1079 | # Reference implementation is in sdncon/rest/views/do_system_version |
| 1080 | # |
| 1081 | def get_system_version_string(): |
| 1082 | version = "SDN OS 1.0 - custom version" |
| 1083 | try: |
| 1084 | f = open("%s/release" % SDN_ROOT, 'r') |
| 1085 | version = f.read() |
| 1086 | f.close() |
| 1087 | except: |
| 1088 | pass |
| 1089 | return version |
| 1090 | |
| 1091 | # |
| 1092 | # rewrite_etc_snmpd_conf |
| 1093 | # |
| 1094 | # API to rewrite the /etc/snmp/snmpd.conf file based on latest config |
| 1095 | # |
| 1096 | def rewrite_etc_snmpd_conf(community, location, contact, ret_result): |
| 1097 | """ |
| 1098 | Return True when the /etc/snmp/snmpd.conf is rewritten. Return False |
| 1099 | otherwise. The file is rewritten only when the intended new contents |
| 1100 | is different from the old contents, this is an attempt to not restart |
| 1101 | the snmp agent unless something really changed. |
| 1102 | """ |
| 1103 | |
| 1104 | changed = False |
| 1105 | new_conf = [] |
| 1106 | # start with default configuration of the file |
| 1107 | new_conf.append("# Default Configuration for the SNMP daemon\n") |
| 1108 | new_conf.append("# Agent address\n") |
| 1109 | new_conf.append("agentAddress udp:161,udp6:[::1]:161\n") |
| 1110 | new_conf.append("# System Object ID\n") |
| 1111 | new_conf.append("sysObjectID %s\n" % (BSN_ENTERPRISE_OID_CONTROLLER)) |
| 1112 | new_conf.append("# System Description\n") |
| 1113 | new_conf.append("sysDescr %s\n"%(get_system_version_string())) |
| 1114 | |
| 1115 | #add community, location, contact information to the file if not there already |
| 1116 | if community != '': |
| 1117 | new_conf.append("rocommunity %s\n" % community) |
| 1118 | if location != '': |
| 1119 | new_conf.append("sysLocation %s\n" % location) |
| 1120 | if contact != '': |
| 1121 | new_conf.append("sysContact %s\n" % contact) |
| 1122 | |
| 1123 | f = open("/etc/snmp/snmpd.conf", "r") |
| 1124 | if (''.join(new_conf) != f.read()): |
| 1125 | f.close() |
| 1126 | f = open("/etc/snmp/snmpd.conf", "w") |
| 1127 | f.write(''.join(new_conf)) |
| 1128 | changed = True |
| 1129 | |
| 1130 | f.close() |
| 1131 | |
| 1132 | return changed |
| 1133 | |
| 1134 | # |
| 1135 | # One of the entry in the snmp server configuration changed |
| 1136 | # |
| 1137 | class SetSnmpServerConfig(OsWrapper): |
| 1138 | def __init__(self, name = "setsnmpserverconfig"): |
| 1139 | OsWrapper.__init__(self, name, [], []) |
| 1140 | |
| 1141 | |
| 1142 | def set(self, args_list): |
| 1143 | # args_list: [server_enable, community, location, contact, enable_changed] |
| 1144 | print "SnmpServerConfig Args List: ", args_list |
| 1145 | server_enable = args_list[0] |
| 1146 | community = args_list[1] |
| 1147 | location = args_list[2] |
| 1148 | contact = args_list[3] |
| 1149 | enable_changed = args_list[4] |
| 1150 | |
| 1151 | ret_result = {'err': [], 'out': []} |
| 1152 | |
| 1153 | try: |
| 1154 | # rewrite /etc/snmp/snmpd.conf file |
| 1155 | need_restart = rewrite_etc_snmpd_conf(community, location, contact, ret_result) |
| 1156 | except Exception, _e: |
| 1157 | need_restart = False |
| 1158 | traceback.print_exc() |
| 1159 | |
| 1160 | if server_enable == 'True' and (need_restart or enable_changed == 'True'): |
| 1161 | self.cmds_lst_for_set = [ |
| 1162 | # set snmpdrun=yes |
| 1163 | {'bin_name' : '/bin/sed', |
| 1164 | 'args_lst' : ['-i', 's/SNMPDRUN=no/SNMPDRUN=yes/', |
| 1165 | '/etc/default/snmpd']}, |
| 1166 | # restart snmpd service |
| 1167 | {'bin_name' : '/usr/sbin/service', |
| 1168 | 'args_lst' : ['snmpd', 'restart']}, |
| 1169 | ] |
| 1170 | return OsWrapper.set_new(self, [], [[], []]) |
| 1171 | |
| 1172 | elif server_enable == 'False' and enable_changed == 'True': |
| 1173 | self.cmds_lst_for_set = [ |
| 1174 | # set snmpdrun=no |
| 1175 | {'bin_name' : '/bin/sed', |
| 1176 | 'args_lst' : ['-i', 's/SNMPDRUN=yes/SNMPDRUN=no/', |
| 1177 | '/etc/default/snmpd']}, |
| 1178 | # stop snmpd service |
| 1179 | {'bin_name' : '/usr/sbin/service', |
| 1180 | 'args_lst' : ['snmpd', 'stop']}, |
| 1181 | ] |
| 1182 | return OsWrapper.set_new(self, [], [[], []]) |
| 1183 | |
| 1184 | return ret_result |
| 1185 | |
| 1186 | |
| 1187 | # |
| 1188 | # The row entry in the snmp server config table was default and deleted |
| 1189 | # |
| 1190 | class UnsetSnmpServerConfig(OsWrapper): |
| 1191 | def __init__(self, name = "unsetsnmpserverconfig"): |
| 1192 | OsWrapper.__init__(self, name, [], []) |
| 1193 | |
| 1194 | def set(self, args_list): |
| 1195 | # args_list: [] |
| 1196 | ret_result = {'err': [], 'out': []} |
| 1197 | |
| 1198 | try: |
| 1199 | # rewrite /etc/snmp/snmpd.conf to default |
| 1200 | rewrite_etc_snmpd_conf('', '', '', ret_result) |
| 1201 | except Exception, _e: |
| 1202 | traceback.print_exc() |
| 1203 | |
| 1204 | # now stop the server if its there |
| 1205 | self.cmds_lst_for_set = [ |
| 1206 | # set snmpdrun=no |
| 1207 | {'bin_name' : '/bin/sed', |
| 1208 | 'args_lst' : ['-i', 's/SNMPDRUN=yes/SNMPDRUN=no/', |
| 1209 | '/etc/default/snmpd']}, |
| 1210 | # stop snmpd service |
| 1211 | {'bin_name' : '/usr/sbin/service', |
| 1212 | 'args_lst' : ['snmpd', 'stop']}, |
| 1213 | ] |
| 1214 | return OsWrapper.set_new(self, [], [[], []]) |
| 1215 | |
| 1216 | class SetImagesUserSSHKey(OsWrapper): |
| 1217 | def __init__(self, name = 'setimagesusersshkey'): |
| 1218 | OsWrapper.__init__(self, name, [], []) |
| 1219 | |
| 1220 | def set(self, args_list): |
| 1221 | sshkey = str(args_list[0]) |
| 1222 | # cat the ssh key to the file |
| 1223 | # set the images user shell to be scponly |
| 1224 | self.cmds_lst_for_set = [ |
| 1225 | {'bin_name' : '/usr/sbin/usermod', |
| 1226 | 'args_lst' : ['-s', '/usr/bin/scponly', 'images']}, |
| 1227 | {'bin_name' : '/bin/echo', |
| 1228 | 'args_lst' : [sshkey], |
| 1229 | 'stdoutfile' : '/home/images/.ssh/authorized_keys'}, |
| 1230 | ] |
| 1231 | return OsWrapper.set_new(self, [], [[], []]) |
| 1232 | |
| 1233 | class ReloadController(OsWrapper): |
| 1234 | def __init__(self, name = 'reloadcontroller'): |
| 1235 | OsWrapper.__init__(self, name, [], []) |
| 1236 | |
| 1237 | def set(self, args_list): |
| 1238 | self.cmds_lst_for_set = [ |
| 1239 | {'bin_name' : '/sbin/reboot', |
| 1240 | 'args_list' : []}, |
| 1241 | ] |
| 1242 | return OsWrapper.set_new(self, [], [[]]) |
| 1243 | |
| 1244 | UPGRADE_IMAGE_FILE_PATH = '/tmp/upgrade-images' |
| 1245 | UPGRADE_IMAGE_MANIFEST = '/tmp/upgrade-image-manifest' |
| 1246 | UPGRADE_PACKAGE_DIRECTORY = '/tmp/upgrade-package/' |
| 1247 | |
| 1248 | class ExtractUpgradePkgManifest(OsWrapper): |
| 1249 | def __init__(self, name = 'extractupgradepkg'): |
| 1250 | OsWrapper.__init__(self, name, [], []) |
| 1251 | |
| 1252 | def set(self, args_list): |
| 1253 | imageName = str(args_list[0]) |
| 1254 | self.cmds_lst_for_set = [ |
| 1255 | {'bin_name' : '/bin/rm', |
| 1256 | 'args_lst' : [UPGRADE_IMAGE_MANIFEST]}, |
| 1257 | {'bin_name' : '/bin/touch', |
| 1258 | 'args_lst' : [UPGRADE_IMAGE_MANIFEST]}, |
| 1259 | {'bin_name' : '/usr/bin/unzip', |
| 1260 | 'args_lst' : ['-p', imageName, 'Manifest'], |
| 1261 | 'stdoutfile' : UPGRADE_IMAGE_MANIFEST}, |
| 1262 | ] |
| 1263 | return OsWrapper.set_new(self, [], [[], [], []], useShell=False, appendStdOut=False) |
| 1264 | |
| 1265 | def get(self, args_list): |
| 1266 | self.cmds_lst_for_get = [ |
| 1267 | {'bin_name' : '/bin/cat', |
| 1268 | 'args_lst': [UPGRADE_IMAGE_MANIFEST]}, |
| 1269 | ] |
| 1270 | return OsWrapper.get_new(self, [], [[]]) |
| 1271 | |
| 1272 | class GetLatestUpgradePkg(OsWrapper): |
| 1273 | def __init__(self, name = 'getlatestupgradepkg'): |
| 1274 | OsWrapper.__init__(self, name, [], []) |
| 1275 | |
| 1276 | # TODO - |
| 1277 | # This is ghetto, it just finds the last zip |
| 1278 | # file in the dir. FIX THIS! |
| 1279 | def get(self, args_list): |
| 1280 | execStr = 'ls -t /home/images/*.pkg | grep pkg | head -1 > ' + UPGRADE_IMAGE_FILE_PATH |
| 1281 | self.cmds_lst_for_get = [ |
| 1282 | {'bin_name' : execStr, |
| 1283 | 'args_lst' : []}, |
| 1284 | ] |
| 1285 | return OsWrapper.get_new(self, [], [[]], useShell=True) |
| 1286 | |
| 1287 | class CatUpgradeImagesFile(OsWrapper): |
| 1288 | def __init__(self, name = 'catupgradeimagesfile'): |
| 1289 | OsWrapper.__init__(self, name, [], []) |
| 1290 | |
| 1291 | def get(self, args_list): |
| 1292 | self.cmds_lst_for_get = [ |
| 1293 | {'bin_name' : '/bin/cat', |
| 1294 | 'args_lst' : [UPGRADE_IMAGE_FILE_PATH]}, |
| 1295 | ] |
| 1296 | return OsWrapper.get_new(self, [], [[]]) |
| 1297 | |
| 1298 | class ExecuteUpgradeStep(OsWrapper): |
| 1299 | def __init__(self, name = 'executeupgradestep'): |
| 1300 | OsWrapper.__init__(self, name, [], []) |
| 1301 | |
| 1302 | def get(self, args_list): |
| 1303 | ret_result = {'err': [], 'out': []} |
| 1304 | |
| 1305 | try: |
| 1306 | manifest = json.loads(exec_os_wrapper("ExtractUpgradePkgManifest", 'get')['out']) |
| 1307 | except ValueError: |
| 1308 | ret_result['err'].append("Corrupted manifest!") |
| 1309 | return ret_result |
| 1310 | |
| 1311 | stepToExec = None |
| 1312 | for step in manifest: |
| 1313 | if step['step'] == int(args_list[0]): |
| 1314 | stepToExec = step['action'] |
| 1315 | break; |
| 1316 | |
| 1317 | if stepToExec == None: |
| 1318 | ret_result['err'].append("Step %s not found in upgrade package manifest!" % |
| 1319 | str(args_list[0])) |
| 1320 | return ret_result |
| 1321 | |
| 1322 | upgradePkg = args_list[1] |
| 1323 | stepScript = tempfile.NamedTemporaryFile(delete=False) |
| 1324 | scriptName = "scripts/%s" % step['action'].strip() |
| 1325 | step = check_output(["unzip", "-p", upgradePkg, scriptName]) |
| 1326 | stepScript.write(step) |
| 1327 | stepScript.flush() |
| 1328 | stepScript.close() |
| 1329 | os.chmod(stepScript.name, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) |
| 1330 | |
| 1331 | try: |
| 1332 | ret = check_output([stepScript.name] + args_list[1:], |
| 1333 | stderr=PIPE) |
| 1334 | ret_result['out'].append(stripped(ret.strip())) |
| 1335 | except CalledProcessError, exception: |
| 1336 | ret_result['err'].append("Error running %s\nreturn code %s\nOutput:\n%s" % |
| 1337 | (exception.cmd, exception.returncode, stripped(exception.output))) |
| 1338 | return ret_result |
| 1339 | |
| 1340 | class CleanupOldUpgradeImages(OsWrapper): |
| 1341 | def __init__(self, name = 'cleanupoldupgradeimages'): |
| 1342 | OsWrapper.__init__(self, name, [], []) |
| 1343 | |
| 1344 | def get(self, args_list): |
| 1345 | # Removes all the .pkg files execpt for the newest one. |
| 1346 | # It handles the case where there is only 1 package, we don't delete it. |
| 1347 | execStr = 'c=`ls /home/images/*.pkg | wc -l`; if [ "$c" -gt 1 ]; then ls -t -r /home/images/*.pkg | head -n -1 | xargs rm; fi' |
| 1348 | self.cmds_lst_for_get = [ |
| 1349 | {'bin_name' : execStr, |
| 1350 | 'args_lst' : []}, |
| 1351 | ] |
| 1352 | return OsWrapper.get_new(self, [], [[]], useShell=True) |
| 1353 | |
| 1354 | class Decommission(OsWrapper): |
| 1355 | def __init__(self, name = "decommission"): |
| 1356 | OsWrapper.__init__(self, name, [], []) |
| 1357 | def set(self, args_list): |
| 1358 | self.cmds_lst_for_set = [ |
| 1359 | {'bin_name' : '/bin/bash', |
| 1360 | 'args_lst' : ["%s/sys/bin/remove-node.sh" % SDN_ROOT, str(args_list[0])]}, |
| 1361 | ] |
| 1362 | return OsWrapper.set_new(self, [], [[]]) |
| 1363 | |
| 1364 | class DecommissionLocal(OsWrapper): |
| 1365 | def __init__(self, name = "decommissionlocal"): |
| 1366 | OsWrapper.__init__(self, name, [], []) |
| 1367 | def set(self, args_list): |
| 1368 | self.cmds_lst_for_set = [ |
| 1369 | {'bin_name' : '/bin/bash', |
| 1370 | 'args_lst' : ["%s/sys/bin/remove-node-local.sh" % SDN_ROOT, str(args_list[0])]}, |
| 1371 | ] |
| 1372 | return OsWrapper.set_new(self, [], [[]]) |
| 1373 | |
| 1374 | class ResetBsc(OsWrapper): |
| 1375 | def __init__(self, name = "resetbsc"): |
| 1376 | OsWrapper.__init__(self, name, [], []) |
| 1377 | def set(self, args_list): |
| 1378 | self.cmds_lst_for_set = [ |
| 1379 | {'bin_name' : '/bin/bash', |
| 1380 | 'args_lst' : ["%s/sys/bin/resetbsc" % SDN_ROOT, '--force']}, |
| 1381 | ] |
| 1382 | return OsWrapper.set_new(self, [], [[]]) |
| 1383 | |
| 1384 | class WriteDataToFile(OsWrapper): |
| 1385 | def __init__(self, name = 'writedatatofile'): |
| 1386 | OsWrapper.__init__(self, name, [], []) |
| 1387 | |
| 1388 | def set(self, args_list): |
| 1389 | data = str(args_list[0]) |
| 1390 | path = str(args_list[1]) |
| 1391 | self.cmds_lst_for_set = [ |
| 1392 | {'bin_name' : '/bin/touch', |
| 1393 | 'args_lst' : [path]}, |
| 1394 | {'bin_name' : '/bin/echo', |
| 1395 | 'args_lst' : [data], |
| 1396 | 'stdoutfile' : path}, |
| 1397 | ] |
| 1398 | return OsWrapper.set_new(self, [], [[], []]) |
| 1399 | |
| 1400 | class DiffConfig(OsWrapper): |
| 1401 | def __init__(self, name = "scpconfig"): |
| 1402 | OsWrapper.__init__(self, name, [], []) |
| 1403 | def set(self, args_list): |
| 1404 | self.cmds_lst_for_set = [ |
| 1405 | {'bin_name' : '/opt/sdnplatform/sys/bin/diff_config.py', |
| 1406 | 'args_lst' : [str(args_list[0]), str(args_list[1])]}, |
| 1407 | ] |
| 1408 | return OsWrapper.set_new(self, [], [[]]) |
| 1409 | |
| 1410 | class RollbackConfig(OsWrapper): |
| 1411 | def __init__(self, name = "upgradeconfig"): |
| 1412 | OsWrapper.__init__(self, name, [], []) |
| 1413 | def set(self, args_list): |
| 1414 | self.cmds_lst_for_set = [ |
| 1415 | {'bin_name' : '/bin/bash', |
| 1416 | 'args_lst' : ["%s/sys/bin/rollback-config.sh" % SDN_ROOT, str(args_list[0])]}, |
| 1417 | ] |
| 1418 | return OsWrapper.set_new(self, [], [[]]) |
| 1419 | |
| 1420 | def stripped(x): |
| 1421 | # remove ascii escape |
| 1422 | return "".join([i for i in x if ord(i) != 27]) |
| 1423 | # |
| 1424 | # exec_os_wrapper |
| 1425 | # |
| 1426 | def exec_os_wrapper(obj_type, oper, args_list = None): |
| 1427 | """ |
| 1428 | Execute the oswrapper.py using sudo(), raising an exception |
| 1429 | for any stderr output from the executed script |
| 1430 | """ |
| 1431 | # Safety check; only run if this file exists |
| 1432 | if not os.path.exists("%s/con" % SDN_ROOT): |
| 1433 | print "exec_os_wrapper: not an installed controller environment" |
| 1434 | return {'out' : '', 'err' : ''} |
| 1435 | if os.path.exists('/etc/not-controller'): |
| 1436 | # XXX should issue some alert here |
| 1437 | print "exec_os_wrapper: /etc/not-controller exists" |
| 1438 | return {'out' : '', 'err' : ''} |
| 1439 | |
| 1440 | oswrapper = os.path.dirname(__file__) + "/oswrapper.py" |
| 1441 | full_cmd_string = ["/usr/bin/sudo", oswrapper, obj_type, oper] |
| 1442 | if args_list: |
| 1443 | full_cmd_string += [str(arg) for arg in args_list] |
| 1444 | |
| 1445 | sub_proc_output = Popen(full_cmd_string, shell=False, |
| 1446 | stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True) |
| 1447 | |
| 1448 | sub_proc_output.wait() |
| 1449 | stderr = sub_proc_output.stderr.read() |
| 1450 | stdout = sub_proc_output.stdout.read() |
| 1451 | returncode = sub_proc_output.returncode |
| 1452 | |
| 1453 | if returncode: |
| 1454 | raise Exception("oswrapper: %s %s: exit code %d: %s" % |
| 1455 | (obj_type, oper, returncode, stderr)) |
| 1456 | |
| 1457 | if len(stderr) != 0 and not stderr.isspace(): |
| 1458 | print " ".join(full_cmd_string), stderr |
| 1459 | |
| 1460 | # ?!? |
| 1461 | return {'out' : stdout, 'err' : stderr} |
| 1462 | |
| 1463 | |
| 1464 | def main(argv): |
| 1465 | obj_type_map = {'ExecuteUfwCommand' : UfwCommand, |
| 1466 | 'SetNtpServer' : SetNtpServer, |
| 1467 | 'SetTimezone' : SetTimezone, |
| 1468 | 'UnsetTimezone' : UnsetTimezone, |
| 1469 | 'NetworkConfig' : NetworkConfig, |
| 1470 | 'SetSyslogServer' : SetSyslogServer, |
| 1471 | 'UnsetSyslogServer' : UnsetSyslogServer, |
| 1472 | 'DateTime' : DateTime, |
| 1473 | 'ControllerId' : SetControllerId, |
| 1474 | 'HAFailback' : HAFailback, |
| 1475 | 'SetHostname' : SetHostname, |
| 1476 | 'SetVrrpVirtualRouterId' : SetVrrpVirtualRouterId, |
| 1477 | 'SetStaticFlowOnlyConfig' : SetStaticFlowOnlyConfig, |
| 1478 | 'SetDefaultConfig' : SetDefaultConfig, |
| 1479 | 'TacacsPlusConfig' : TacacsPlusConfig, |
| 1480 | 'SetSnmpServerConfig' : SetSnmpServerConfig, |
| 1481 | 'UnsetSnmpServerConfig' : UnsetSnmpServerConfig, |
| 1482 | 'SetImagesUserSSHKey' : SetImagesUserSSHKey, |
| 1483 | 'ReloadController' : ReloadController, |
| 1484 | 'ExtractUpgradePkgManifest' : ExtractUpgradePkgManifest, |
| 1485 | 'GetLatestUpgradePkg' : GetLatestUpgradePkg, |
| 1486 | 'CatUpgradeImagesFile' : CatUpgradeImagesFile, |
| 1487 | 'ExecuteUpgradeStep' : ExecuteUpgradeStep, |
| 1488 | 'DirectNetworkConfig' : DirectNetworkConfig, |
| 1489 | 'CleanupOldUpgradeImages' : CleanupOldUpgradeImages, |
| 1490 | 'RestartSDNPlatform' : RestartSDNPlatform, |
| 1491 | 'AbortUpgrade' : AbortUpgrade, |
| 1492 | 'Decommission' : Decommission, |
| 1493 | 'DecommissionLocal' : DecommissionLocal, |
| 1494 | 'RollbackConfig' : RollbackConfig, |
| 1495 | 'DiffConfig' : DiffConfig, |
| 1496 | 'ResetBsc' : ResetBsc, |
| 1497 | 'WriteDataToFile' : WriteDataToFile, |
| 1498 | } |
| 1499 | ret_result = {'err': ["insufficient or invalid args"], 'out': []} |
| 1500 | if len(argv) >= 3: |
| 1501 | if argv[1] in obj_type_map: |
| 1502 | obj_type = obj_type_map[argv[1]] |
| 1503 | x = obj_type() |
| 1504 | if argv[2] == 'set': |
| 1505 | ret_result = x.set(argv[3:]) |
| 1506 | elif argv[2] == 'get': |
| 1507 | ret_result = x.get(argv[3:]) |
| 1508 | |
| 1509 | # The ret_result entries are lists of strings from the output's of |
| 1510 | # various commands. |
| 1511 | print >>sys.stdout, ''.join(ret_result['out']) |
| 1512 | print >>sys.stderr, ''.join(ret_result['err']) |
| 1513 | |
| 1514 | if __name__ == '__main__': |
| 1515 | main(sys.argv) |