mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 08:41:00 +00:00
Compare commits
26 Commits
2.4.1
..
nodesearch
| Author | SHA1 | Date | |
|---|---|---|---|
| 45c13a3d46 | |||
| a04eea6927 | |||
| f46939b7ec | |||
| 17a8ab3211 | |||
| 0fd4c3b2f7 | |||
| 829a5b08c1 | |||
| aa059c6a4d | |||
| 2e03b662ea | |||
| 55a0aab548 | |||
| aaf5aebff7 | |||
| c1abeaff04 | |||
| 37d4543d24 | |||
| d13e286609 | |||
| a7c93627e2 | |||
| 6e5cfe69a8 | |||
| 626f1c16e2 | |||
| 43480c2e3b | |||
| b91693a973 | |||
| da8b4d00d8 | |||
| b7b7fd82eb | |||
| 3789e43f35 | |||
| a325dcb423 | |||
| c8d0009dac | |||
| 79f5dce6dc | |||
| 54f36e259f | |||
| acf67a6c81 |
@@ -50,9 +50,6 @@ argparser.add_option('-c', '--clear', action='store_true',
|
||||
help='Clear attributes')
|
||||
argparser.add_option('-p', '--prompt', action='store_true',
|
||||
help='Prompt for attribute values interactively')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Prompt if trying to set attributes on more '
|
||||
'than specified number of nodes')
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
|
||||
@@ -90,7 +87,6 @@ if len(args) > 1:
|
||||
if oneval != twoval:
|
||||
print('Values did not match.')
|
||||
argassign[arg] = twoval
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
exitcode=client.updateattrib(session,args,nodetype, noderange, options, argassign)
|
||||
try:
|
||||
# setting user output to what the user inputs
|
||||
|
||||
@@ -32,8 +32,6 @@ if path.startswith('/opt'):
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange>")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Number of nodes to affect before prompting for confirmation')
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -45,7 +43,7 @@ session = client.Command()
|
||||
exitcode = 0
|
||||
|
||||
errorNodes = set([])
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
|
||||
success = session.simple_noderange_command(noderange, 'configuration/management_controller/reset', 'reset', key='state', errnodes=errorNodes) # = 0 if successful
|
||||
|
||||
# Determine which nodes were successful and print them
|
||||
|
||||
@@ -42,10 +42,6 @@ argparser.add_option('-p', '--persist', dest='persist', action='store_true',
|
||||
default=False,
|
||||
help='Request the boot device be persistent rather than '
|
||||
'one time')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to boot, '
|
||||
'prompting if over the threshold')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
@@ -70,7 +66,6 @@ else:
|
||||
bootmode = 'uefi'
|
||||
|
||||
errnodes = set([])
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
rc = session.simple_noderange_command(noderange, '/boot/nextdevice', bootdev,
|
||||
bootmode=bootmode,
|
||||
persistent=options.persist,
|
||||
|
||||
@@ -68,10 +68,6 @@ argparser.add_option('-r', '--restoredefault', default=False,
|
||||
help='Restore the configuration of the node '
|
||||
'to factory default for given component. '
|
||||
'Currently only uefi is supported')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to configure, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
cfgpaths = {
|
||||
@@ -209,7 +205,6 @@ else:
|
||||
session = client.Command()
|
||||
rcode = 0
|
||||
if options.restoredefault:
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
if options.restoredefault.lower() in (
|
||||
'sys', 'system', 'uefi', 'bios'):
|
||||
for fr in session.update(
|
||||
@@ -230,7 +225,6 @@ if options.restoredefault:
|
||||
options.restoredefault))
|
||||
sys.exit(1)
|
||||
if setmode:
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
if options.exclude:
|
||||
sys.stderr.write('Cannot use exclude and assign at the same time\n')
|
||||
sys.exit(1)
|
||||
|
||||
@@ -38,10 +38,6 @@ if sys.version_info[0] < 3:
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] noderange [clear]")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to clear if clearing log, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -50,11 +46,11 @@ except IndexError:
|
||||
sys.exit(1)
|
||||
client.check_globbing(noderange)
|
||||
deletemode = False
|
||||
if len(args) > 2:
|
||||
if len(sys.argv) > 3:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
if len(args) == 2:
|
||||
if args[1] == 'clear':
|
||||
if len(sys.argv) == 3:
|
||||
if sys.argv[2] == 'clear':
|
||||
deletemode = True
|
||||
else:
|
||||
argparser.print_help()
|
||||
@@ -92,7 +88,6 @@ def format_event(evt):
|
||||
|
||||
if deletemode:
|
||||
func = session.delete
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
else:
|
||||
func = session.read
|
||||
for rsp in func('/noderange/{0}/events/hardware/log'.format(noderange)):
|
||||
|
||||
@@ -59,10 +59,6 @@ argparser = optparse.OptionParser(
|
||||
"%prog <noderange> [list][update [--backup <file>]]|[<components>]")
|
||||
argparser.add_option('-b', '--backup', action='store_true',
|
||||
help='Target a backup bank rather than primary')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='When updating, prompt if more than the specified '
|
||||
'number of servers will be affected')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
upfile = None
|
||||
try:
|
||||
@@ -99,7 +95,6 @@ def get_update_progress(session, url):
|
||||
|
||||
def update_firmware(session, filename):
|
||||
global exitcode
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
output = sq.ScreenPrinter(noderange, session)
|
||||
nodeurls = {}
|
||||
filename = os.path.abspath(filename)
|
||||
|
||||
@@ -37,10 +37,6 @@ exitcode = 0
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: "
|
||||
"%prog <noderange> [list][install <file>|save <directory>|delete <name>]")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to delete licenses from, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
upfile = None
|
||||
downdir = None
|
||||
@@ -56,7 +52,7 @@ try:
|
||||
delete = args[2]
|
||||
elif args[1] != 'list':
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
sys.exit(1)
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
@@ -142,7 +138,6 @@ try:
|
||||
elif downdir:
|
||||
save_licenses(session, downdir)
|
||||
elif delete:
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
delete_license(session, delete)
|
||||
else:
|
||||
show_licenses(session)
|
||||
|
||||
@@ -37,11 +37,6 @@ argparser = optparse.OptionParser(
|
||||
argparser.add_option('-p', '--showprevious', dest='previous',
|
||||
action='store_true', default=False,
|
||||
help='Show previous power state')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to change power state, '
|
||||
'prompting if over the threshold')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -77,4 +72,4 @@ if options.previous:
|
||||
# add dictionary to session
|
||||
session.add_precede_dict(prev)
|
||||
|
||||
sys.exit(session.simple_noderange_command(noderange, '/power/state', setstate, promptover=options.maxnodes))
|
||||
sys.exit(session.simple_noderange_command(noderange, '/power/state', setstate))
|
||||
|
||||
@@ -35,10 +35,6 @@ import confluent.client as client
|
||||
argparser = optparse.OptionParser(
|
||||
usage='''\n %prog noderange
|
||||
\n ''')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to delete, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
if len(args) != 1:
|
||||
argparser.print_help()
|
||||
@@ -47,7 +43,6 @@ noderange = args[0]
|
||||
client.check_globbing(noderange)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
for r in session.delete('/noderange/{0}'.format(noderange)):
|
||||
if 'error' in r:
|
||||
sys.stderr.write(r['error'] + '\n')
|
||||
|
||||
@@ -32,10 +32,6 @@ if path.startswith('/opt'):
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange>")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to reseat, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -47,7 +43,7 @@ session = client.Command()
|
||||
exitcode = 0
|
||||
|
||||
errorNodes = set([])
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
|
||||
success = session.simple_noderange_command(noderange, 'power/reseat', 'reseat', key='reseat', errnodes=errorNodes) # = 0 if successful
|
||||
|
||||
# Determine which nodes were successful and print them
|
||||
|
||||
@@ -42,10 +42,6 @@ def run():
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog location noderange:location",
|
||||
)
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to run rsync to, '
|
||||
'prompting if over the threshold')
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
help='Number of nodes to concurrently rsync')
|
||||
# among other things, FD_SETSIZE limits. Besides, spawning too many
|
||||
@@ -68,7 +64,7 @@ def run():
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
c.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(noderange),
|
||||
{'expression': cmdstr}):
|
||||
if 'error' in exp:
|
||||
@@ -104,7 +100,7 @@ def run():
|
||||
if desc['type'] == 'stdout':
|
||||
if node not in pernodeout:
|
||||
pernodeout[node] = ''
|
||||
pernodeout[node] += client.stringify(data)
|
||||
pernodeout[node] += stringify(data)
|
||||
if '\n' in pernodeout[node]:
|
||||
currout, pernodeout[node] = pernodeout[node].split('\n', 1)
|
||||
if currout:
|
||||
|
||||
@@ -46,10 +46,6 @@ def run():
|
||||
help='Number of commands to run at a time')
|
||||
argparser.add_option('-n', '--nonodeprefix', action='store_true',
|
||||
help='Do not prefix output with node names')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to run the command with, '
|
||||
'prompting if over the threshold')
|
||||
# among other things, FD_SETSIZE limits. Besides, spawning too many
|
||||
# processes can be unkind for the unaware on memory pressure and such...
|
||||
argparser.disable_interspersed_args()
|
||||
@@ -67,7 +63,7 @@ def run():
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
c.stop_if_noderange_over(args[0], options.maxnodes)
|
||||
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
|
||||
{'expression': cmdstr}):
|
||||
if 'error' in exp:
|
||||
|
||||
@@ -43,10 +43,7 @@ argparser.add_option('-p', '--persist', dest='persist', action='store_true',
|
||||
argparser.add_option('-u', '--uefi', dest='uefi', action='store_true',
|
||||
default=True,
|
||||
help='Request UEFI style boot (rather than BIOS)')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to modify next boot device, '
|
||||
'prompting if over the threshold')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
try:
|
||||
@@ -66,7 +63,6 @@ if options.biosmode:
|
||||
bootmode = 'bios'
|
||||
else:
|
||||
bootmode = 'uefi'
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
sys.exit(session.simple_noderange_command(noderange, '/boot/nextdevice', bootdev,
|
||||
bootmode=bootmode,
|
||||
persistent=options.persist))
|
||||
|
||||
@@ -46,10 +46,6 @@ def run():
|
||||
help='Number of commands to run at a time')
|
||||
argparser.add_option('-n', '--nonodeprefix', action='store_true',
|
||||
help='Do not prefix output with node names')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to run remote ssh command to, '
|
||||
'prompting if over the threshold')
|
||||
# among other things, FD_SETSIZE limits. Besides, spawning too many
|
||||
# processes can be unkind for the unaware on memory pressure and such...
|
||||
argparser.disable_interspersed_args()
|
||||
@@ -59,7 +55,7 @@ def run():
|
||||
sys.exit(1)
|
||||
client.check_globbing(args[0])
|
||||
concurrentprocs = options.count
|
||||
c = client.Command()
|
||||
c = client.Command()
|
||||
cmdstr = " ".join(args[1:])
|
||||
|
||||
currprocs = 0
|
||||
@@ -68,7 +64,7 @@ def run():
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
|
||||
c.stop_if_noderange_over(args[0], options.maxnodes)
|
||||
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
|
||||
{'expression': cmdstr}):
|
||||
if 'error' in exp:
|
||||
|
||||
@@ -63,16 +63,10 @@ def _print_cfg(scfg):
|
||||
sys.stderr.write(e['error'] + '\n')
|
||||
exitcode = e.get('errorcode', 1)
|
||||
for node in e.get('databynode', {}):
|
||||
curr = e['databynode'][node]
|
||||
if 'error' in curr:
|
||||
if 'no available drives' in curr['error']:
|
||||
curr['error'] += ' (drives must be in unconfigured state to be available, they must not be in jbod or online state)'
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, curr['error']))
|
||||
exitcode = curr.get('errorcode', 1)
|
||||
continue
|
||||
if node not in storagebynode:
|
||||
storagebynode[node] = {'disks': [], 'arrays': [],
|
||||
'volumes': []}
|
||||
curr = e['databynode'][node]
|
||||
storagebynode[node][curr['type'] + 's'].append(curr)
|
||||
for node in storagebynode:
|
||||
for disk in sorted(storagebynode[node]['disks'],
|
||||
@@ -114,7 +108,6 @@ def createstorage(noderange, options, args):
|
||||
sys.stderr.write('-r and -d are required arguments to create array\n')
|
||||
sys.exit(1)
|
||||
session = client.Command()
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
names = options.name
|
||||
if names is None:
|
||||
names = ''.join(args)
|
||||
@@ -139,7 +132,6 @@ def deletestorage(noderange, options, args):
|
||||
else:
|
||||
names = options.name
|
||||
session = client.Command()
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
for rsp in session.delete(
|
||||
'/noderange/{0}/configuration/storage/volumes/{1}'.format(
|
||||
noderange, names)):
|
||||
@@ -170,7 +162,6 @@ def setdisk(noderange, options, args):
|
||||
sys.stderr.write('diskset requires valid state as argument (hotspare, jbod, unconfigured)\n')
|
||||
sys.exit(1)
|
||||
session = client.Command()
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
scfg = session.update('/noderange/{0}/configuration/storage/disks/{1}'.format(noderange, names), {'state': args[0]})
|
||||
_print_cfg(scfg)
|
||||
|
||||
@@ -211,10 +202,6 @@ def main():
|
||||
help='Comma separated list of stripsizes to use when creating volumes. '
|
||||
'This value is in kilobytes. The default behavior is to allow the '
|
||||
'storage controller to decide.')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to configure storage on, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
if len(args) == 1:
|
||||
args.append('show')
|
||||
|
||||
@@ -64,7 +64,7 @@ def printerror(res, node=None):
|
||||
|
||||
|
||||
|
||||
def download_servicedata(noderange, media, options):
|
||||
def download_servicedata(noderange, media):
|
||||
global exitcode
|
||||
session = client.Command()
|
||||
output = sq.ScreenPrinter(noderange, session)
|
||||
@@ -73,7 +73,6 @@ def download_servicedata(noderange, media, options):
|
||||
upargs = {'filename': filename}
|
||||
noderrs = {}
|
||||
nodeurls = {}
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
for res in session.create(resource, upargs):
|
||||
if 'created' not in res:
|
||||
for nodename in res.get('databynode', ()):
|
||||
@@ -122,10 +121,6 @@ def main():
|
||||
'management server (the confluent server if running remote, '
|
||||
'and the collective.manager if in collective)\n'
|
||||
'\n\nSee `man %prog` for more info.\n')
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to download diagnostic data from, '
|
||||
'prompting if over the threshold')
|
||||
(options, args) = argparser.parse_args()
|
||||
media = None
|
||||
try:
|
||||
@@ -147,6 +142,6 @@ def main():
|
||||
except KeyError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
handler(noderange, media, options)
|
||||
handler(noderange, media)
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -39,10 +39,6 @@ _attraliases = {
|
||||
'bmcpass': 'secret.hardwaremanagementpassword',
|
||||
}
|
||||
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
def stringify(instr):
|
||||
# Normalize unicode and bytes to 'str', correcting for
|
||||
@@ -223,7 +219,7 @@ class Command(object):
|
||||
return rc
|
||||
|
||||
def simple_noderange_command(self, noderange, resource, input=None,
|
||||
key=None, errnodes=None, promptover=None, **kwargs):
|
||||
key=None, errnodes=None, **kwargs):
|
||||
try:
|
||||
self._currnoderange = noderange
|
||||
rc = 0
|
||||
@@ -239,7 +235,6 @@ class Command(object):
|
||||
noderange, resource)):
|
||||
rc = self.handle_results(ikey, rc, res, errnodes)
|
||||
else:
|
||||
self.stop_if_noderange_over(noderange, promptover)
|
||||
kwargs[ikey] = input
|
||||
for res in self.update('/noderange/{0}/{1}'.format(
|
||||
noderange, resource), kwargs):
|
||||
@@ -249,33 +244,6 @@ class Command(object):
|
||||
except KeyboardInterrupt:
|
||||
cprint('')
|
||||
return 0
|
||||
|
||||
def stop_if_noderange_over(self, noderange, maxnodes):
|
||||
if maxnodes is None:
|
||||
return
|
||||
nsize = self.get_noderange_size(noderange)
|
||||
if nsize > maxnodes:
|
||||
if nsize == 1:
|
||||
nodename = list(self.read(
|
||||
'/noderange/{0}/nodes/'.format(noderange)))[0].get('item', {}).get('href', None)
|
||||
nodename = nodename[:-1]
|
||||
p = input('Command is about to affect node {0}, continue (y/n)? '.format(nodename))
|
||||
else:
|
||||
p = input('Command is about to affect {0} nodes, continue (y/n)? '.format(nsize))
|
||||
if p.lower() != 'y':
|
||||
sys.stderr.write('Aborting at user request\n')
|
||||
sys.exit(1)
|
||||
raise Exception("Aborting at user request")
|
||||
|
||||
|
||||
def get_noderange_size(self, noderange):
|
||||
numnodes = 0
|
||||
for node in self.read('/noderange/{0}/nodes/'.format(noderange)):
|
||||
if node.get('item', {}).get('href', None):
|
||||
numnodes += 1
|
||||
else:
|
||||
raise Exception("Error trying to size noderange {0}".format(noderange))
|
||||
return numnodes
|
||||
|
||||
def simple_nodegroups_command(self, noderange, resource, input=None, key=None, **kwargs):
|
||||
try:
|
||||
@@ -376,8 +344,12 @@ class Command(object):
|
||||
if fingerprint == khf[hostid]:
|
||||
return
|
||||
else:
|
||||
replace = input(
|
||||
"MISMATCHED CERTIFICATE DATA, ACCEPT NEW? (y/n):")
|
||||
try:
|
||||
replace = raw_input(
|
||||
"MISMATCHED CERTIFICATE DATA, ACCEPT NEW? (y/n):")
|
||||
except NameError:
|
||||
replace = input(
|
||||
"MISMATCHED CERTIFICATE DATA, ACCEPT NEW? (y/n):")
|
||||
if replace not in ('y', 'Y'):
|
||||
raise Exception("BAD CERTIFICATE")
|
||||
cprint('Adding new key for %s:%s' % (server, port))
|
||||
@@ -429,10 +401,6 @@ def printattributes(session, requestargs, showtype, nodetype, noderange, options
|
||||
path = '/{0}/{1}/attributes/{2}'.format(nodetype, noderange, showtype)
|
||||
return print_attrib_path(path, session, requestargs, options)
|
||||
|
||||
def _sort_attrib(k):
|
||||
if isinstance(k[1], dict) and k[1].get('sortid', None) is not None:
|
||||
return k[1]['sortid']
|
||||
return k[0]
|
||||
|
||||
def print_attrib_path(path, session, requestargs, options, rename=None, attrprefix=None):
|
||||
exitcode = 0
|
||||
@@ -443,7 +411,9 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
|
||||
exitcode = 1
|
||||
continue
|
||||
for node in sorted(res['databynode']):
|
||||
for attr, val in sorted(res['databynode'][node].items(), key=_sort_attrib):
|
||||
for attr, val in sorted(
|
||||
res['databynode'][node].items(),
|
||||
key=lambda k: k[1].get('sortid', k[0]) if isinstance(k[1], dict) else k[0]):
|
||||
if attr == 'error':
|
||||
sys.stderr.write('{0}: Error: {1}\n'.format(node, val))
|
||||
continue
|
||||
|
||||
@@ -46,8 +46,9 @@ alias nodelicense='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export
|
||||
_confluent_get_args()
|
||||
{
|
||||
CMPARGS=($COMP_LINE)
|
||||
NUMARGS=$((COMP_CWORD+1))
|
||||
if [ "${COMP_WORDS[COMP_CWORD]}" == '' ]; then
|
||||
NUMARGS=${#CMPARGS[@]}
|
||||
if [ "${COMP_WORDS[-1]}" == '' ]; then
|
||||
NUMARGS=$((NUMARGS+1))
|
||||
CMPARGS+=("")
|
||||
fi
|
||||
GENNED=""
|
||||
@@ -74,7 +75,7 @@ function _confluent_generic_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS -ge 3 ] && [ ! -z "$GENNED" ]; then
|
||||
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[-1]}))
|
||||
fi
|
||||
if [ $NUMARGS -lt 3 ]; then
|
||||
_confluent_nr_completion
|
||||
@@ -110,7 +111,7 @@ _confluent_nodemedia_completion()
|
||||
return
|
||||
fi
|
||||
if [ $NUMARGS -ge 3 ] && [ ! -z "$GENNED" ]; then
|
||||
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[-1]}))
|
||||
return;
|
||||
fi
|
||||
if [ $NUMARGS -lt 3 ]; then
|
||||
@@ -123,7 +124,7 @@ _confluent_nodefirmware_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
COMPREPLY=($(compgen -W "list update" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -W "list update" -- ${COMP_WORDS[-1]}))
|
||||
return;
|
||||
fi
|
||||
if [ $NUMARGS -gt 3 ] && [ ${CMPARGS[2]} == 'update' ]; then
|
||||
@@ -141,7 +142,7 @@ _confluent_nodeshell_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
COMPREPLY=($(compgen -c -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -c -- ${COMP_WORDS[-1]}))
|
||||
return
|
||||
fi
|
||||
if [ $NUMARGS -gt 3 ]; then
|
||||
@@ -159,7 +160,7 @@ _confluent_nodelicense_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
COMPREPLY=($(compgen -W "install list save delete" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -W "install list save delete" -- ${COMP_WORDS[-1]}))
|
||||
return;
|
||||
fi
|
||||
if [ $NUMARGS == 4 ] && [ ${CMPARGS[2]} == 'install' ]; then
|
||||
@@ -182,7 +183,7 @@ _confluent_nodesupport_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
COMPREPLY=($(compgen -W "servicedata" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
COMPREPLY=($(compgen -W "servicedata" -- ${COMP_WORDS[-1]}))
|
||||
return;
|
||||
fi
|
||||
if [ $NUMARGS == 4 ] && [ ${CMPARGS[2]} == 'servicedata' ]; then
|
||||
@@ -209,36 +210,41 @@ _confluent_nn_completion()
|
||||
if [ $NUMARGS -gt 2 ]; then
|
||||
return;
|
||||
fi
|
||||
INPUT=${COMP_WORDS[COMP_CWORD]}
|
||||
INPUT=${COMP_WORDS[-1]}
|
||||
INPUT=${INPUT##*,-}
|
||||
INPUT=${INPUT##*,}
|
||||
INPUT=${INPUT##*@}
|
||||
PREFIX=""
|
||||
if [ "$INPUT" != "${COMP_WORDS[COMP_CWORD]}" ]; then
|
||||
PREFIX=${COMP_WORDS[COMP_CWORD]}
|
||||
if [ "$INPUT" != "${COMP_WORDS[-1]}" ]; then
|
||||
PREFIX=${COMP_WORDS[-1]}
|
||||
PREFIX=$(echo $PREFIX | sed -e 's/,[^,@-]*$/,/' -e 's/,-[^,@]*$/,-/' -e 's/@[^,@]*/@/')
|
||||
fi
|
||||
|
||||
COMPREPLY=($(compgen -W "$(nodelist | sed -e s/^/$PREFIX/)" -- "${COMP_WORDS[COMP_CWORD]}"))
|
||||
COMPREPLY=($(compgen -W "$(nodelist | sed -e s/^/$PREFIX/)" -- "${COMP_WORDS[-1]}"))
|
||||
}
|
||||
_confluent_nr_completion()
|
||||
{
|
||||
CMPARGS=($COMP_LINE)
|
||||
NUMARGS=${#CMPARGS[@]}
|
||||
if [ "${COMP_WORDS[-1]}" == '' ]; then
|
||||
NUMARGS=$((NUMARGS+1))
|
||||
fi
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS -gt 2 ]; then
|
||||
return;
|
||||
fi
|
||||
INPUT=${COMP_WORDS[COMP_CWORD]}
|
||||
INPUT=${COMP_WORDS[-1]}
|
||||
INPUT=${INPUT##*,-}
|
||||
INPUT=${INPUT##*,}
|
||||
INPUT=${INPUT##*@}
|
||||
PREFIX=""
|
||||
if [ "$INPUT" != "${COMP_WORDS[COMP_CWORD]}" ]; then
|
||||
PREFIX=${COMP_WORDS[COMP_CWORD]}
|
||||
if [ "$INPUT" != "${COMP_WORDS[-1]}" ]; then
|
||||
PREFIX=${COMP_WORDS[-1]}
|
||||
PREFIX=$(echo $PREFIX | sed -e 's/,[^,@-]*$/,/' -e 's/,-[^,@]*$/,-/' -e 's/@[^,@]*/@/')
|
||||
fi
|
||||
|
||||
COMPREPLY=($(compgen -W "$(confetty show /nodegroups|sed -e 's/\///' -e s/^/$PREFIX/;nodelist | sed -e s/^/$PREFIX/)" -- "${COMP_WORDS[COMP_CWORD]}"))
|
||||
#COMPREPLY=($(compgen -W "$(confetty show /nodegroups|sed -e 's/\///' -e s/^/$PREFIX/;nodelist | sed -e s/^/$PREFIX/)" -- "${COMP_WORDS[-1]}"))
|
||||
COMPREPLY=($(compgen -W "$(confetty show /nodegroups|sed -e 's/\///' -e s/^/$PREFIX/;nodelist | sed -e s/^/$PREFIX/)" -- "${COMP_WORDS[-1]}"))
|
||||
}
|
||||
_confluent_ng_completion()
|
||||
{
|
||||
@@ -246,17 +252,17 @@ _confluent_ng_completion()
|
||||
if [ $NUMARGS -gt 2 ]; then
|
||||
return;
|
||||
fi
|
||||
INPUT=${COMP_WORDS[COMP_CWORD]}
|
||||
INPUT=${COMP_WORDS[-1]}
|
||||
INPUT=${INPUT##*,-}
|
||||
INPUT=${INPUT##*,}
|
||||
INPUT=${INPUT##*@}
|
||||
PREFIX=""
|
||||
if [ "$INPUT" != "${COMP_WORDS[COMP_CWORD]}" ]; then
|
||||
PREFIX=${COMP_WORDS[COMP_CWORD]}
|
||||
if [ "$INPUT" != "${COMP_WORDS[-1]}" ]; then
|
||||
PREFIX=${COMP_WORDS[-1]}
|
||||
PREFIX=$(echo $PREFIX | sed -e 's/,[^,@-]*$/,/' -e 's/,-[^,@]*$/,-/' -e 's/@[^,@]*/@/')
|
||||
fi
|
||||
|
||||
COMPREPLY=($(compgen -W "$(confetty show /nodegroups|sed -e 's/\///' -e s/^/$PREFIX/)" -- "${COMP_WORDS[COMP_CWORD]}"))
|
||||
COMPREPLY=($(compgen -W "$(confetty show /nodegroups|sed -e 's/\///' -e s/^/$PREFIX/)" -- "${COMP_WORDS[-1]}"))
|
||||
}
|
||||
complete -F _confluent_nodeattrib_completion nodeattrib
|
||||
complete -F _confluent_nodeattrib_completion nodegroupattrib
|
||||
|
||||
@@ -13,11 +13,7 @@ nodefirmware(8) -- Report firmware information on confluent nodes
|
||||
will retrieve all firmware, but can be directed to fetch specific firmware by
|
||||
calling out the name of the firmware (e.g. uefi or xcc) or request reading only
|
||||
core firmware firmware by using the word 'core', which is generally a quicker
|
||||
operation. Different hardwaremanagement.method indicated plugins may have
|
||||
different capabilities available. For example, the 'core' distinction may
|
||||
not be relevant to redfish. Additionally, the Lenovo XCC makes certain
|
||||
information available over IPMI that is not otherwise available (for example
|
||||
the FPGA version where applicable).
|
||||
operation.
|
||||
|
||||
In the update form, it accepts a single file and attempts to update it using
|
||||
the out of band facilities. Firmware updates can end in one of three states:
|
||||
|
||||
@@ -7,7 +7,7 @@ nodesupport(8) -- Utilities for interacting with vendor support
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`nodesupport` provides capabilities associated with interacting with support.
|
||||
`nodesupport` provides capabilities associated with interactiong with support.
|
||||
Currently it only has the `servicedata` subcommand. `servicedata` takes
|
||||
an argument that is either a directory name (that can be used for a single node
|
||||
or multiple nodes) or a file name (only to be used with single node noderange).
|
||||
@@ -16,9 +16,6 @@ connects to the managed system, so it will download to the remote system if runn
|
||||
remotely and will download to the collective.manager indicated system if
|
||||
running in collective mode.
|
||||
|
||||
Note that due to vendor filename requirements, any filename may have vendor
|
||||
specific suffixes added to any file produced.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Download support data from a single node to a specific filename
|
||||
|
||||
@@ -3,12 +3,8 @@ cd `dirname $0`
|
||||
PKGNAME=$(basename $(pwd))
|
||||
DPKGNAME=$(basename $(pwd) | sed -e s/_/-/)
|
||||
OPKGNAME=$(basename $(pwd) | sed -e s/_/-/)
|
||||
PYEXEC=python3
|
||||
DSCARGS="--with-python3=True --with-python2=False"
|
||||
if grep wheezy /etc/os-release; then
|
||||
DPKGNAME=python-$DPKGNAME
|
||||
PYEXEC=python
|
||||
DSCARGS="--with-python3=False --with-python2=True"
|
||||
fi
|
||||
cd ..
|
||||
mkdir -p /tmp/confluent # $DPKGNAME
|
||||
@@ -28,15 +24,15 @@ install-scripts=/opt/confluent/bin
|
||||
package=$DPKGNAME
|
||||
EOF
|
||||
|
||||
$PYEXEC setup.py sdist > /dev/null 2>&1
|
||||
py2dsc $DSCARGS dist/*.tar.gz
|
||||
python setup.py sdist > /dev/null 2>&1
|
||||
py2dsc dist/*.tar.gz
|
||||
shopt -s extglob
|
||||
cd deb_dist/!(*.orig)/
|
||||
if [ "$OPKGNAME" = "confluent-server" ]; then
|
||||
if grep wheezy /etc/os-release; then
|
||||
sed -i 's/^\(Depends:.*\)/\1, python-confluent-client, python-lxml, python-eficompressor, python-pycryptodomex, python-dateutil, python-pyopenssl/' debian/control
|
||||
else
|
||||
sed -i 's/^\(Depends:.*\)/\1, confluent-client, python3-lxml, python3-eficompressor, python3-pycryptodome, python3-websocket/' debian/control
|
||||
sed -i 's/^\(Depends:.*\)/\1, confluent-client, python-lxml, python-eficompressor, python-pycryptodome, python-dateutil/' debian/control
|
||||
fi
|
||||
if grep wheezy /etc/os-release; then
|
||||
echo 'confluent_client python-confluent-client' >> debian/pydist-overrides
|
||||
|
||||
@@ -58,9 +58,9 @@ _allowedbyrole = {
|
||||
'/node*/configuration/*',
|
||||
],
|
||||
'update': [
|
||||
'/discovery/*',
|
||||
'/discovery/*',
|
||||
'/networking/macs/rescan',
|
||||
'/node*/power/state',
|
||||
'/node*/power/state',
|
||||
'/node*/power/reseat',
|
||||
'/node*/attributes/*',
|
||||
'/node*/media/*tach',
|
||||
|
||||
@@ -160,11 +160,11 @@ def connect_to_leader(cert=None, name=None, leader=None):
|
||||
raise
|
||||
currentleader = leader
|
||||
#spawn this as a thread...
|
||||
follower = eventlet.spawn(follow_leader, remote, pvers, leader)
|
||||
follower = eventlet.spawn(follow_leader, remote, pvers)
|
||||
return True
|
||||
|
||||
|
||||
def follow_leader(remote, proto, leader):
|
||||
def follow_leader(remote, proto):
|
||||
global currentleader
|
||||
cleanexit = False
|
||||
try:
|
||||
@@ -176,8 +176,8 @@ def follow_leader(remote, proto, leader):
|
||||
log.log({'info': 'Previous following cleanly closed',
|
||||
'subsystem': 'collective'})
|
||||
return
|
||||
log.log({'info': 'Current leader ({0}) has disappeared, restarting '
|
||||
'collective membership'.format(leader), 'subsystem': 'collective'})
|
||||
log.log({'info': 'Current leader has disappeared, restarting '
|
||||
'collective membership', 'subsystem': 'collective'})
|
||||
# The leader has folded, time to startup again...
|
||||
cfm.stop_following()
|
||||
currentleader = None
|
||||
|
||||
@@ -97,6 +97,15 @@ node = {
|
||||
'description': ('Classification of node as server or switch'),
|
||||
'validvalues': ('switch', 'server'),
|
||||
},
|
||||
'api.key': {
|
||||
'description': ('Crypt of api key for self api requests by node'),
|
||||
},
|
||||
'api.armed': {
|
||||
'description': ('Indicates whether an insecure api key request is allowed. '
|
||||
'The format is an expiration time in ISO8601 format. When '
|
||||
'the indicated time passes or the first time a node claims '
|
||||
'the key, key grants will not be allowed.'),
|
||||
},
|
||||
#'id': {
|
||||
# 'description': ('Numeric identifier for node')
|
||||
#},
|
||||
@@ -271,7 +280,7 @@ node = {
|
||||
'console.method': {
|
||||
'description': ('Indicate the method used to access the console of '
|
||||
'the managed node.'),
|
||||
'validvalues': ('ssh', 'ipmi', 'tsmsol'),
|
||||
'validvalues': ('ssh', 'ipmi'),
|
||||
},
|
||||
# 'virtualization.host': {
|
||||
# 'description': ('Hypervisor where this node does/should reside'),
|
||||
|
||||
@@ -328,11 +328,6 @@ def exec_on_leader(function, *args):
|
||||
|
||||
|
||||
def exec_on_followers(fnname, *args):
|
||||
pushes = eventlet.GreenPool()
|
||||
# Check health of collective prior to attempting
|
||||
for _ in pushes.starmap(
|
||||
_push_rpc, [(cfgstreams[s], b'') for s in cfgstreams]):
|
||||
pass
|
||||
if len(cfgstreams) < (len(_cfgstore['collective']) // 2):
|
||||
# the leader counts in addition to registered streams
|
||||
raise exc.DegradedCollective()
|
||||
@@ -391,15 +386,9 @@ def init_masterkey(password=None, autogen=True):
|
||||
|
||||
def _push_rpc(stream, payload):
|
||||
with _rpclock:
|
||||
try:
|
||||
stream.sendall(struct.pack('!Q', len(payload)))
|
||||
if len(payload):
|
||||
stream.sendall(payload)
|
||||
return True
|
||||
except Exception:
|
||||
logException()
|
||||
del cfgstreams[stream]
|
||||
stream.close()
|
||||
stream.sendall(struct.pack('!Q', len(payload)))
|
||||
if len(payload):
|
||||
stream.sendall(payload)
|
||||
|
||||
|
||||
def decrypt_value(cryptvalue,
|
||||
@@ -589,18 +578,11 @@ def relay_slaved_requests(name, listener, vers):
|
||||
lh = StreamHandler(listener)
|
||||
_hasquorum = len(cfgstreams) >= (
|
||||
len(_cfgstore['collective']) // 2)
|
||||
_newquorum = None
|
||||
while _hasquorum != _newquorum:
|
||||
if _newquorum is not None:
|
||||
_hasquorum = _newquorum
|
||||
payload = cPickle.dumps({'quorum': _hasquorum}, protocol=lowestver)
|
||||
for _ in pushes.starmap(
|
||||
_push_rpc,
|
||||
[(cfgstreams[s], payload) for s in cfgstreams]):
|
||||
pass
|
||||
_newquorum = len(cfgstreams) >= (
|
||||
len(_cfgstore['collective']) // 2)
|
||||
_hasquorum = _newquorum
|
||||
payload = cPickle.dumps({'quorum': _hasquorum}, protocol=lowestver)
|
||||
for _ in pushes.starmap(
|
||||
_push_rpc,
|
||||
[(cfgstreams[s], payload) for s in cfgstreams]):
|
||||
pass
|
||||
if _hasquorum and _pending_collective_updates:
|
||||
apply_pending_collective_updates()
|
||||
msg = lh.get_next_msg()
|
||||
@@ -622,10 +604,8 @@ def relay_slaved_requests(name, listener, vers):
|
||||
except Exception as e:
|
||||
exc = e
|
||||
if 'xid' in rpc:
|
||||
res = _push_rpc(listener, cPickle.dumps({'xid': rpc['xid'],
|
||||
_push_rpc(listener, cPickle.dumps({'xid': rpc['xid'],
|
||||
'exc': exc}, protocol=vers))
|
||||
if not res:
|
||||
break
|
||||
try:
|
||||
msg = lh.get_next_msg()
|
||||
except Exception:
|
||||
@@ -670,9 +650,7 @@ class StreamHandler(object):
|
||||
if confluent.util.monotonic_time() > self.expiry:
|
||||
return None
|
||||
if confluent.util.monotonic_time() > self.keepalive:
|
||||
res = _push_rpc(self.sock, b'') # nulls are a keepalive
|
||||
if not res:
|
||||
return None
|
||||
_push_rpc(self.sock, b'') # nulls are a keepalive
|
||||
self.keepalive = confluent.util.monotonic_time() + 20
|
||||
self.expiry = confluent.util.monotonic_time() + 60
|
||||
msg = self.sock.recv(8)
|
||||
@@ -789,9 +767,7 @@ def follow_channel(channel, proto=2):
|
||||
_pendingchangesets[rpc['xid']].send()
|
||||
if 'quorum' in rpc:
|
||||
_hasquorum = rpc['quorum']
|
||||
res = _push_rpc(channel, b'') # use null as ACK
|
||||
if not res:
|
||||
break
|
||||
_push_rpc(channel, b'') # use null as ACK
|
||||
msg = lh.get_next_msg()
|
||||
finally:
|
||||
# mark the connection as broken
|
||||
@@ -1161,8 +1137,6 @@ class ConfigManager(object):
|
||||
attribute, match = expression.split('=')
|
||||
else:
|
||||
raise Exception('Invalid Expression')
|
||||
if attribute.startswith('secret.'):
|
||||
raise Exception('Filter by secret attributes is not supported')
|
||||
for node in nodes:
|
||||
try:
|
||||
currvals = [self._cfgstore['nodes'][node][attribute]['value']]
|
||||
@@ -1652,7 +1626,6 @@ class ConfigManager(object):
|
||||
if group in self._cfgstore['nodes'][node]['groups']:
|
||||
self._cfgstore['nodes'][node]['groups'].remove(group)
|
||||
self._node_removed_from_group(node, group, changeset)
|
||||
_mark_dirtykey('nodes', node, self.tenant)
|
||||
for node in nodes:
|
||||
if node not in self._cfgstore['nodes']:
|
||||
self._cfgstore['nodes'][node] = {'groups': [group]}
|
||||
|
||||
@@ -362,17 +362,13 @@ class ConsoleHandler(object):
|
||||
if self.reconnect:
|
||||
self.reconnect.cancel()
|
||||
self.reconnect = None
|
||||
strerror = ('The console.method attribute for this node is '
|
||||
'not configured,\r\nset it to a valid value for console '
|
||||
'function')
|
||||
try:
|
||||
self._console = list(plugin.handle_path(
|
||||
self._plugin_path.format(self.node),
|
||||
"create", self.cfgmgr))[0]
|
||||
except (exc.NotImplementedException, exc.NotFoundException):
|
||||
self._console = None
|
||||
except Exception as e:
|
||||
strerror = str(e)
|
||||
except:
|
||||
if _tracelog:
|
||||
_tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event,
|
||||
event=log.Events.stacktrace)
|
||||
@@ -385,9 +381,13 @@ class ConsoleHandler(object):
|
||||
self._send_rcpts({'connectstate': self.connectstate,
|
||||
'error': self.error})
|
||||
self.feedbuffer(
|
||||
'\x1bc\x1b[2J\x1b[1;1H[{0}]'.format(strerror))
|
||||
'\x1bc\x1b[2J\x1b[1;1H[The console.method attribute for this node is '
|
||||
'not configured,\r\nset it to a valid value for console '
|
||||
'function]')
|
||||
self._send_rcpts(
|
||||
'\x1bc\x1b[2J\x1b[1;1H[{0}]'.format(strerror))
|
||||
'\x1bc\x1b[2J\x1b[1;1H[The console.method attribute for this node is '
|
||||
'not configured,\r\nset it to a valid value for console '
|
||||
'function]')
|
||||
self.clearerror = True
|
||||
return
|
||||
if self.clearerror:
|
||||
|
||||
@@ -72,7 +72,7 @@ import struct
|
||||
import sys
|
||||
|
||||
pluginmap = {}
|
||||
dispatch_plugins = (b'ipmi', u'ipmi', b'redfish', u'redfish', b'tsmsol', u'tsmsol')
|
||||
dispatch_plugins = (b'ipmi', u'ipmi')
|
||||
|
||||
|
||||
def seek_element(currplace, currkey):
|
||||
@@ -224,7 +224,7 @@ def _init_core():
|
||||
'advanced': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
'storage': {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2019 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import confluent.config.configmanager as cfm
|
||||
import datetime
|
||||
import eventlet
|
||||
import eventlet.green.socket as socket
|
||||
import eventlet.greenpool
|
||||
import os
|
||||
|
||||
class CredServer(object):
|
||||
def __init__(self, bindhost='::', bindport=301, ttl=1):
|
||||
self.srv = socket.socket(socket.AF_INET6)
|
||||
self.srv.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl)
|
||||
self.srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.srv.bind((bindhost, bindport))
|
||||
self.srv.listen(32)
|
||||
self.gpool = eventlet.greenpool.GreenPool(256)
|
||||
self.cfm = cfm.ConfigManager(None)
|
||||
self.runtime = eventlet.spawn(self.listen)
|
||||
|
||||
def listen(self):
|
||||
while True:
|
||||
client, info = self.srv.accept()
|
||||
if info[1] > 1023:
|
||||
client.close()
|
||||
continue
|
||||
self.gpool.spawn_n(self.handle_client, client)
|
||||
|
||||
def handle_client(self, client):
|
||||
client.send('\xc2\xd1-\xa8\x80\xd8j\xba')
|
||||
tlv = bytearray(client.recv(2))
|
||||
if tlv[0] != 1:
|
||||
client.close()
|
||||
return
|
||||
nodename = client.recv(tlv[1])
|
||||
tlv = bytearray(client.recv(2))
|
||||
apiarmed = self.cfm.get_node_attributes(nodename, 'api.armed')
|
||||
apiarmed = apiarmed.get(nodename, {}).get('api.armed', {}).get('value', None)
|
||||
if not apiarmed:
|
||||
client.close()
|
||||
return
|
||||
now = datetime.datetime.utcnow()
|
||||
expiry = datetime.datetime.strptime(apiarmed, "%Y-%m-%dT%H:%M:%SZ")
|
||||
if now > expiry:
|
||||
self.cfm.set_node_attributes({nodename: {'api.armed': ''}})
|
||||
client.close()
|
||||
return
|
||||
client.send(b'\x02\x20')
|
||||
rttoken = os.urandom(32)
|
||||
client.send(rttoken)
|
||||
client.send('\x00\x00')
|
||||
tlv = bytearray(client.recv(2))
|
||||
if tlv[0] != 3:
|
||||
client.close()
|
||||
return
|
||||
echotoken = client.recv(tlv[1])
|
||||
if echotoken != rttoken:
|
||||
client.close()
|
||||
return
|
||||
tlv = bytearray(client.recv(2))
|
||||
if tlv[0] != 4:
|
||||
client.close()
|
||||
return
|
||||
echotoken = client.recv(tlv[1])
|
||||
self.cfm.set_node_attributes({nodename: {'api.key': echotoken, 'api.armed': ''}})
|
||||
client.recv(2) # drain end of message
|
||||
client.send('\x05\x00') # report success
|
||||
client.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
a = CredServer()
|
||||
while True:
|
||||
eventlet.sleep(86400)
|
||||
@@ -65,7 +65,7 @@ import base64
|
||||
import confluent.config.configmanager as cfm
|
||||
import confluent.collective.manager as collective
|
||||
import confluent.discovery.protocols.pxe as pxe
|
||||
#import confluent.discovery.protocols.ssdp as ssdp
|
||||
import confluent.discovery.protocols.ssdp as ssdp
|
||||
import confluent.discovery.protocols.slp as slp
|
||||
import confluent.discovery.handlers.imm as imm
|
||||
import confluent.discovery.handlers.cpstorage as cpstorage
|
||||
@@ -871,6 +871,8 @@ def get_nodename_from_chained_smms(cfg, handler, info):
|
||||
nodename = newnodename
|
||||
return nodename
|
||||
|
||||
def get_node_by_uuid(uuid):
|
||||
return nodes_by_uuid.get(uuid, None)
|
||||
|
||||
def get_nodename_from_enclosures(cfg, info):
|
||||
nodename = None
|
||||
@@ -949,9 +951,9 @@ def eval_node(cfg, handler, info, nodename, manual=False):
|
||||
# raise exc.InvalidArgumentException(errorstr)
|
||||
# log.log({'error': errorstr})
|
||||
if encuuid in pending_by_uuid:
|
||||
pending_by_uuid[encuuid].append(info)
|
||||
pending_by_uuid[encuuid].add(info)
|
||||
else:
|
||||
pending_by_uuid[encuuid] = [info]
|
||||
pending_by_uuid[encuuid] = set([info])
|
||||
return
|
||||
# We found the real smm, replace the list with the actual smm
|
||||
# to continue
|
||||
@@ -1094,10 +1096,6 @@ def discover_node(cfg, handler, info, nodename, manual):
|
||||
info['discostatus'] = 'discovered'
|
||||
for i in pending_by_uuid.get(curruuid, []):
|
||||
eventlet.spawn_n(_recheck_single_unknown_info, cfg, i)
|
||||
try:
|
||||
del pending_by_uuid[curruuid]
|
||||
except KeyError:
|
||||
pass
|
||||
return True
|
||||
log.log({'info': 'Detected {0}, but discovery.policy is not set to a '
|
||||
'value allowing discovery (open or permissive)'.format(
|
||||
@@ -1232,8 +1230,7 @@ def start_detection():
|
||||
if rechecker is None:
|
||||
rechecktime = util.monotonic_time() + 900
|
||||
rechecker = eventlet.spawn_after(900, _periodic_recheck, cfg)
|
||||
|
||||
# eventlet.spawn_n(ssdp.snoop, safe_detected)
|
||||
eventlet.spawn_n(ssdp.snoop, None, None, ssdp, get_node_by_uuid)
|
||||
|
||||
def stop_autosense():
|
||||
for watcher in list(autosensors):
|
||||
|
||||
@@ -68,21 +68,17 @@ class NodeHandler(object):
|
||||
def _savecert(self, certificate):
|
||||
self._fp = certificate
|
||||
return True
|
||||
|
||||
|
||||
def get_node_credentials(self, nodename, creds, defuser, defpass):
|
||||
user = creds.get(nodename, {}).get(
|
||||
'secret.hardwaremanagementuser', {}).get('value', None)
|
||||
havecustomcreds = False
|
||||
if user and not isinstance(user, str):
|
||||
user = user.decode('utf8')
|
||||
if user is not None and user != defuser:
|
||||
havecustomcreds = True
|
||||
else:
|
||||
user = defuser
|
||||
passwd = creds.get(nodename, {}).get(
|
||||
'secret.hardwaremanagementpassword', {}).get('value', None)
|
||||
if passwd and not isinstance(passwd, str):
|
||||
passwd = passwd.decode('utf8')
|
||||
if passwd is not None and passwd != defpass:
|
||||
havecustomcreds = True
|
||||
else:
|
||||
|
||||
@@ -98,7 +98,7 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
setdata += ',v4Gateway:{0}'.format(gateway)
|
||||
wc.request('POST', '/data', setdata)
|
||||
rsp = wc.getresponse()
|
||||
rspdata = util.stringify(rsp.read())
|
||||
rspdata = rsp.read()
|
||||
if '<statusCode>0' not in rspdata:
|
||||
raise Exception("Error configuring SMM Network")
|
||||
return
|
||||
@@ -145,7 +145,7 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
authdata['password'] = password
|
||||
wc.request('POST', '/data/login', urlencode(authdata), headers)
|
||||
rsp = wc.getresponse()
|
||||
rspdata = util.stringify(rsp.read())
|
||||
rspdata = rsp.read()
|
||||
if 'authResult>0' in rspdata:
|
||||
tokens = fromstring(rspdata)
|
||||
st2 = tokens.findall('st2')[0].text
|
||||
@@ -181,10 +181,6 @@ class NodeHandler(bmchandler.NodeHandler):
|
||||
'secret.hardwaremanagementuser', {}).get('value', 'USERID')
|
||||
passwd = creds.get(nodename, {}).get(
|
||||
'secret.hardwaremanagementpassword', {}).get('value', 'PASSW0RD')
|
||||
if not isinstance(username, str):
|
||||
username = username.decode('utf8')
|
||||
if not isinstance(passwd, str):
|
||||
passwd = passwd.decode('utf8')
|
||||
if passwd == 'PASSW0RD' and self.ruleset:
|
||||
raise Exception('Cannot support default password and setting password rules at same time')
|
||||
if passwd == 'PASSW0RD':
|
||||
|
||||
@@ -22,7 +22,6 @@ try:
|
||||
from urllib import urlencode
|
||||
except ImportError:
|
||||
from urllib.parse import urlencode
|
||||
|
||||
getaddrinfo = eventlet.support.greendns.getaddrinfo
|
||||
|
||||
webclient = eventlet.import_patched('pyghmi.util.webclient')
|
||||
@@ -44,13 +43,6 @@ class NodeHandler(generic.NodeHandler):
|
||||
self.atdefault = True
|
||||
super(NodeHandler, self).__init__(info, configmanager)
|
||||
|
||||
def scan(self):
|
||||
c = webclient.SecureHTTPConnection(self.ipaddr, 443, verifycallback=self.validate_cert)
|
||||
i = c.grab_json_response('/redfish/v1/')
|
||||
uuid = i.get('UUID', None)
|
||||
if uuid:
|
||||
self.info['uuid'] = uuid
|
||||
|
||||
def validate_cert(self, certificate):
|
||||
# broadly speaking, merely checks consistency moment to moment,
|
||||
# but if https_cert gets stricter, this check means something
|
||||
@@ -62,17 +54,9 @@ class NodeHandler(generic.NodeHandler):
|
||||
'username': self.DEFAULT_USER,
|
||||
'password': self.DEFAULT_PASS,
|
||||
}
|
||||
wc = webclient.SecureHTTPConnection(self.ipaddr, 443, verifycallback=self.validate_cert)
|
||||
wc.set_header('Content-Type', 'application/json')
|
||||
authmode = 0
|
||||
if not self.trieddefault:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', authdata)
|
||||
if status == 403:
|
||||
wc.set_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||
authmode = 1
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
else:
|
||||
authmode = 2
|
||||
wc = webclient.SecureHTTPConnection(self.ipaddr, 443, verifycallback=self.validate_cert)
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
if status > 400:
|
||||
rsp = util.stringify(rsp)
|
||||
self.trieddefault = True
|
||||
@@ -84,15 +68,9 @@ class NodeHandler(generic.NodeHandler):
|
||||
'default_password': self.DEFAULT_PASS,
|
||||
'username': self.DEFAULT_USER
|
||||
}
|
||||
if authmode == 2:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/reset-pass', passchange)
|
||||
else:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/reset-pass', urlencode(passchange))
|
||||
rsp, status = wc.grab_json_response_with_status('/api/reset-pass', urlencode(passchange))
|
||||
authdata['password'] = self.targpass
|
||||
if authmode == 2:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', authdata)
|
||||
else:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
self.csrftok = rsp['CSRFToken']
|
||||
self.channel = rsp['channel']
|
||||
self.curruser = self.DEFAULT_USER
|
||||
@@ -107,23 +85,15 @@ class NodeHandler(generic.NodeHandler):
|
||||
if self.curruser:
|
||||
authdata['username'] = self.curruser
|
||||
authdata['password'] = self.currpass
|
||||
if authmode != 1:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', authdata)
|
||||
if authmode == 1 or status == 403:
|
||||
wc.set_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
if status != 200:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
if rsp.status != 200:
|
||||
return None
|
||||
self.csrftok = rsp['CSRFToken']
|
||||
self.channel = rsp['channel']
|
||||
return wc
|
||||
authdata['username'] = self.targuser
|
||||
authdata['password'] = self.targpass
|
||||
if authmode != 1:
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', authdata)
|
||||
if authmode == 1 or status == 403:
|
||||
wc.set_header('Content-Type', 'application/x-www-form-urlencoded')
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
rsp, status = wc.grab_json_response_with_status('/api/session', urlencode(authdata))
|
||||
if status != 200:
|
||||
return None
|
||||
self.curruser = self.targuser
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
import base64
|
||||
import codecs
|
||||
import confluent.discovery.handlers.imm as immhandler
|
||||
import confluent.exceptions as exc
|
||||
import confluent.netutil as netutil
|
||||
import confluent.util as util
|
||||
import errno
|
||||
@@ -96,8 +95,7 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
ipmicmd.xraw_command(netfn=0x3a, command=0xf1, data=(1,))
|
||||
except pygexc.IpmiException as e:
|
||||
if (e.ipmicode != 193 and 'Unauthorized name' not in str(e) and
|
||||
'Incorrect password' not in str(e) and
|
||||
str(e) != 'Session no longer connected'):
|
||||
'Incorrect password' not in str(e)):
|
||||
# raise an issue if anything other than to be expected
|
||||
if disableipmi:
|
||||
_, _ = wc.grab_json_response_with_status(
|
||||
@@ -172,12 +170,7 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
pwdchanged = True
|
||||
if '_csrf_token' in wc.cookies:
|
||||
wc.set_header('X-XSRF-TOKEN', wc.cookies['_csrf_token'])
|
||||
if pwdchanged:
|
||||
# Remove the minimum change interval, to allow sane
|
||||
# password changes after provisional changes
|
||||
self.set_password_policy('')
|
||||
return (wc, pwdchanged)
|
||||
return (None, None)
|
||||
|
||||
@property
|
||||
def wc(self):
|
||||
@@ -211,7 +204,7 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
# however the target *will* demand a new password... if it's currently
|
||||
# PASSW0RD
|
||||
# use TempW0rd42 to avoid divulging a real password on the line
|
||||
# This is replacing one well known password (PASSW0RD) with another
|
||||
# This is replacing one well known password (PASSW0RD) with another
|
||||
# (TempW0rd42)
|
||||
passwd = 'TempW0rd42'
|
||||
wc, pwdchanged = self.get_webclient('USERID', 'PASSW0RD', passwd)
|
||||
@@ -233,9 +226,9 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
if wc:
|
||||
return wc
|
||||
|
||||
def set_password_policy(self, strruleset):
|
||||
def set_password_policy(self):
|
||||
ruleset = {'USER_GlobalMinPassChgInt': '0'}
|
||||
for rule in strruleset.split(','):
|
||||
for rule in self.ruleset.split(','):
|
||||
if '=' not in rule:
|
||||
continue
|
||||
name, value = rule.split('=')
|
||||
@@ -357,18 +350,15 @@ class NodeHandler(immhandler.NodeHandler):
|
||||
# between hypothetical secure path and today.
|
||||
dpp = self.configmanager.get_node_attributes(
|
||||
nodename, 'discovery.passwordrules')
|
||||
strruleset = dpp.get(nodename, {}).get(
|
||||
self.ruleset = dpp.get(nodename, {}).get(
|
||||
'discovery.passwordrules', {}).get('value', '')
|
||||
wc = self.wc
|
||||
creds = self.configmanager.get_node_attributes(
|
||||
self.nodename, ['secret.hardwaremanagementuser',
|
||||
'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
user, passwd, isdefault = self.get_node_credentials(nodename, creds, 'USERID', 'PASSW0RD')
|
||||
self.set_password_policy(strruleset)
|
||||
self.set_password_policy()
|
||||
if self._atdefaultcreds:
|
||||
if isdefault and self.tmppasswd:
|
||||
raise Exception(
|
||||
'Request to use default credentials, but refused by target after it has been changed to {0}'.format(self.tmppasswd))
|
||||
if not isdefault:
|
||||
self._setup_xcc_account(user, passwd, wc)
|
||||
self._convert_sha256account(user, passwd, wc)
|
||||
|
||||
@@ -84,8 +84,6 @@ def _parse_SrvRply(parsed):
|
||||
:return:
|
||||
"""
|
||||
payload = parsed['payload']
|
||||
if len(payload) < 4:
|
||||
return
|
||||
ecode, ucount = struct.unpack('!HH', bytes(payload[0:4]))
|
||||
if ecode:
|
||||
parsed['errorcode'] = ecode
|
||||
@@ -236,20 +234,13 @@ def _find_srvtype(net, net4, srvtype, addresses, xid):
|
||||
|
||||
|
||||
def _grab_rsps(socks, rsps, interval, xidmap):
|
||||
r = None
|
||||
res = select.select(socks, (), (), interval)
|
||||
if res:
|
||||
r = res[0]
|
||||
r, _, _ = select.select(socks, (), (), interval)
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
neighutil.refresh_neigh()
|
||||
_parse_slp_packet(rsp, peer, rsps, xidmap)
|
||||
res = select.select(socks, (), (), interval)
|
||||
if not res:
|
||||
r = None
|
||||
else:
|
||||
r = res[0]
|
||||
r, _, _ = select.select(socks, (), (), interval)
|
||||
|
||||
|
||||
|
||||
@@ -569,7 +560,7 @@ def scan(srvtypes=_slp_services, addresses=None, localonly=False):
|
||||
# now to analyze and flesh out the responses
|
||||
for id in rsps:
|
||||
if 'service:ipmi' in rsps[id]['services']:
|
||||
if 'service:ipmi://Athena:623' in rsps[id].get('urls', ''):
|
||||
if 'service:ipmi://Athena:623' in rsps[id]['urls']:
|
||||
rsps[id]['services'] = ['service:thinkagile-storage']
|
||||
else:
|
||||
continue
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
|
||||
import confluent.neighutil as neighutil
|
||||
import confluent.util as util
|
||||
import confluent.log as log
|
||||
import eventlet.green.select as select
|
||||
import eventlet.green.socket as socket
|
||||
import struct
|
||||
import traceback
|
||||
|
||||
mcastv4addr = '239.255.255.250'
|
||||
mcastv6addr = 'ff02::c'
|
||||
@@ -51,7 +53,7 @@ def scan(services, target=None):
|
||||
yield rply
|
||||
|
||||
|
||||
def snoop(handler, byehandler=None):
|
||||
def snoop(handler, byehandler=None, protocol=None, uuidlookup=None):
|
||||
"""Watch for SSDP notify messages
|
||||
|
||||
The handler shall be called on any service coming online.
|
||||
@@ -67,6 +69,7 @@ def snoop(handler, byehandler=None):
|
||||
# Normally, I like using v6/v4 agnostic socket. However, since we are
|
||||
# dabbling in multicast wizardry here, such sockets can cause big problems,
|
||||
# so we will have two distinct sockets
|
||||
tracelog = log.Logger('trace')
|
||||
known_peers = set([])
|
||||
net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
|
||||
@@ -90,50 +93,73 @@ def snoop(handler, byehandler=None):
|
||||
net6.bind(('', 1900))
|
||||
peerbymacaddress = {}
|
||||
while True:
|
||||
newmacs = set([])
|
||||
machandlers = {}
|
||||
r, _, _ = select.select((net4, net6), (), (), 60)
|
||||
neighutil.update_neigh()
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
rsp = rsp.split('\r\n')
|
||||
method, _, _ = rsp[0].split(' ', 2)
|
||||
if method == 'NOTIFY':
|
||||
ip = peer[0].partition('%')[0]
|
||||
if ip not in neighutil.neightable:
|
||||
continue
|
||||
if peer in known_peers:
|
||||
continue
|
||||
mac = neighutil.neightable[ip]
|
||||
known_peers.add(peer)
|
||||
newmacs.add(mac)
|
||||
if mac in peerbymacaddress:
|
||||
peerbymacaddress[mac]['peers'].append(peer)
|
||||
else:
|
||||
peerbymacaddress[mac] = {
|
||||
'hwaddr': mac,
|
||||
'peers': [peer],
|
||||
}
|
||||
peerdata = peerbymacaddress[mac]
|
||||
try:
|
||||
newmacs = set([])
|
||||
machandlers = {}
|
||||
r, _, _ = select.select((net4, net6), (), (), 60)
|
||||
neighutil.update_neigh()
|
||||
while r:
|
||||
for s in r:
|
||||
(rsp, peer) = s.recvfrom(9000)
|
||||
rsp = rsp.split('\r\n')
|
||||
method, _, _ = rsp[0].split(' ', 2)
|
||||
if method == 'NOTIFY':
|
||||
ip = peer[0].partition('%')[0]
|
||||
if ip not in neighutil.neightable:
|
||||
continue
|
||||
if peer in known_peers:
|
||||
continue
|
||||
mac = neighutil.neightable[ip]
|
||||
known_peers.add(peer)
|
||||
newmacs.add(mac)
|
||||
if mac in peerbymacaddress:
|
||||
peerbymacaddress[mac]['peers'].append(peer)
|
||||
else:
|
||||
peerbymacaddress[mac] = {
|
||||
'hwaddr': mac,
|
||||
'peers': [peer],
|
||||
}
|
||||
peerdata = peerbymacaddress[mac]
|
||||
for headline in rsp[1:]:
|
||||
if not headline:
|
||||
continue
|
||||
header, _, value = headline.partition(':')
|
||||
header = header.strip()
|
||||
value = value.strip()
|
||||
if header == 'NT':
|
||||
peerdata['service'] = value
|
||||
elif header == 'NTS':
|
||||
if value == 'ssdp:byebye':
|
||||
machandlers[mac] = byehandler
|
||||
elif value == 'ssdp:alive':
|
||||
machandlers[mac] = None # handler
|
||||
elif method == 'M-SEARCH':
|
||||
if not uuidlookup:
|
||||
continue
|
||||
#ip = peer[0].partition('%')[0]
|
||||
for headline in rsp[1:]:
|
||||
if not headline:
|
||||
continue
|
||||
header, _, value = headline.partition(':')
|
||||
header = header.strip()
|
||||
value = value.strip()
|
||||
if header == 'NT':
|
||||
peerdata['service'] = value
|
||||
elif header == 'NTS':
|
||||
if value == 'ssdp:byebye':
|
||||
machandlers[mac] = byehandler
|
||||
elif value == 'ssdp:alive':
|
||||
machandlers[mac] = handler
|
||||
r, _, _ = select.select((net4, net6), (), (), 0.1)
|
||||
for mac in newmacs:
|
||||
thehandler = machandlers.get(mac, None)
|
||||
if thehandler:
|
||||
thehandler(peerbymacaddress[mac])
|
||||
headline = headline.partition(':')
|
||||
if len(headline) < 3:
|
||||
continue
|
||||
if headline[0] == 'ST' and headline[-1].startswith(' urn:xcat.org:service:confluent:'):
|
||||
for query in headline[-1].split('/'):
|
||||
if query.startswith('uuid='):
|
||||
curruuid = query.split('=', 1)[1].lower()
|
||||
node = uuidlookup(curruuid)
|
||||
if not node:
|
||||
break
|
||||
reply = 'HTTP/1.1 200 OK\r\nNODENAME: {0}'.format(node)
|
||||
s.sendto(reply, peer)
|
||||
r, _, _ = select.select((net4, net6), (), (), 0.2)
|
||||
for mac in newmacs:
|
||||
thehandler = machandlers.get(mac, None)
|
||||
if thehandler:
|
||||
thehandler(peerbymacaddress[mac])
|
||||
except Exception:
|
||||
tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event,
|
||||
event=log.Events.stacktrace)
|
||||
|
||||
|
||||
def _find_service(service, target):
|
||||
|
||||
@@ -107,14 +107,11 @@ class PubkeyInvalid(ConfluentException):
|
||||
self.fingerprint = fingerprint
|
||||
self.attrname = attribname
|
||||
self.message = text
|
||||
certtxt = base64.b64encode(certificate)
|
||||
if not isinstance(certtxt, str):
|
||||
certtxt = certtxt.decode('utf8')
|
||||
bodydata = {'message': text,
|
||||
'event': event,
|
||||
'fingerprint': fingerprint,
|
||||
'fingerprintfield': attribname,
|
||||
'certificate': certtxt}
|
||||
'certificate': base64.b64encode(certificate)}
|
||||
self.errorbody = json.dumps(bodydata)
|
||||
|
||||
def get_error_body(self):
|
||||
|
||||
@@ -43,16 +43,13 @@ def execupdate(handler, filename, updateobj, type, owner, node):
|
||||
'detail': errstr})
|
||||
return
|
||||
if type == 'ffdc' and os.path.isdir(filename):
|
||||
filename += '/' + node
|
||||
filename += '/' + node + '.svcdata'
|
||||
try:
|
||||
if type == 'firmware':
|
||||
completion = handler(filename, progress=updateobj.handle_progress,
|
||||
bank=updateobj.bank)
|
||||
else:
|
||||
completion = handler(filename, progress=updateobj.handle_progress)
|
||||
if type == 'ffdc' and completion:
|
||||
filename = completion
|
||||
completion = None
|
||||
if completion is None:
|
||||
completion = 'complete'
|
||||
if owner:
|
||||
|
||||
@@ -76,7 +76,7 @@ def _htmlify_structure(indict):
|
||||
if datum is None:
|
||||
nd.append('')
|
||||
else:
|
||||
nd.append(util.stringify(datum))
|
||||
nd.append(datum)
|
||||
ret += ",".join(nd)
|
||||
else:
|
||||
for v in indict:
|
||||
@@ -138,7 +138,6 @@ class ConfluentMessage(object):
|
||||
snippet = ""
|
||||
for key in pairs:
|
||||
val = pairs[key]
|
||||
key = util.stringify(key)
|
||||
value = self.defaultvalue
|
||||
if isinstance(val, dict) and 'type' in val:
|
||||
valtype = val['type']
|
||||
@@ -339,7 +338,6 @@ class ConfluentChoiceMessage(ConfluentMessage):
|
||||
snippet = ''
|
||||
for key in pairdata:
|
||||
val = pairdata[key]
|
||||
key = util.stringify(key)
|
||||
snippet += key + ':<select name="%s">' % key
|
||||
valid_values = self.valid_values
|
||||
if key in self.valid_paramset:
|
||||
@@ -1379,8 +1377,6 @@ class InputAlertDestination(ConfluentMessage):
|
||||
self.alertcfg = {}
|
||||
if multinode: # keys are node names
|
||||
for node in inputdata:
|
||||
if not isinstance(inputdata[node], dict):
|
||||
break
|
||||
self.alertcfg[node] = inputdata[node]
|
||||
for key in inputdata[node]:
|
||||
if key not in self.valid_alert_params:
|
||||
@@ -1393,8 +1389,7 @@ class InputAlertDestination(ConfluentMessage):
|
||||
else:
|
||||
self.alertcfg[node][key] = \
|
||||
self.valid_alert_params[key](inputdata[node][key])
|
||||
else:
|
||||
return
|
||||
else:
|
||||
for key in inputdata:
|
||||
if key not in self.valid_alert_params:
|
||||
raise exc.InvalidArgumentException(
|
||||
@@ -1492,15 +1487,12 @@ class Volume(ConfluentMessage):
|
||||
|
||||
class Disk(ConfluentMessage):
|
||||
valid_states = set([
|
||||
'fault',
|
||||
'jbod',
|
||||
'unconfigured',
|
||||
'hotspare',
|
||||
'rebuilding',
|
||||
'online',
|
||||
])
|
||||
state_aliases = {
|
||||
'unconfigured bad': 'fault',
|
||||
'unconfigured good': 'unconfigured',
|
||||
'global hot spare': 'hotspare',
|
||||
'dedicated hot spare': 'hotspare',
|
||||
|
||||
@@ -125,8 +125,7 @@ def _dump_neighbordatum(info):
|
||||
|
||||
def b64tohex(b64str):
|
||||
bd = base64.b64decode(b64str)
|
||||
bd = bytearray(bd)
|
||||
return ''.join(['{0:02x}'.format(x) for x in bd])
|
||||
return ''.join(['{0:02x}'.format(ord(x)) for x in bd])
|
||||
|
||||
def get_fingerprint(switch, port, configmanager, portmatch):
|
||||
update_switch_data(switch, configmanager)
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2019 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
# This plugin provides an ssh implementation comforming to the 'console'
|
||||
# specification. consoleserver or shellserver would be equally likely
|
||||
# to use this.
|
||||
|
||||
import confluent.exceptions as cexc
|
||||
import confluent.interface.console as conapi
|
||||
import confluent.log as log
|
||||
import confluent.util as util
|
||||
import pyghmi.exceptions as pygexc
|
||||
import pyghmi.redfish.command as rcmd
|
||||
import eventlet
|
||||
import eventlet.green.ssl as ssl
|
||||
try:
|
||||
websocket = eventlet.import_patched('websocket')
|
||||
wso = websocket.WebSocket
|
||||
except Exception:
|
||||
wso = object
|
||||
|
||||
def get_conn_params(node, configdata):
|
||||
if 'secret.hardwaremanagementuser' in configdata:
|
||||
username = configdata['secret.hardwaremanagementuser']['value']
|
||||
else:
|
||||
username = 'USERID'
|
||||
if 'secret.hardwaremanagementpassword' in configdata:
|
||||
passphrase = configdata['secret.hardwaremanagementpassword']['value']
|
||||
else:
|
||||
passphrase = 'PASSW0RD' # for lack of a better guess
|
||||
if 'hardwaremanagement.manager' in configdata:
|
||||
bmc = configdata['hardwaremanagement.manager']['value']
|
||||
else:
|
||||
bmc = node
|
||||
return {
|
||||
'username': username,
|
||||
'passphrase': passphrase,
|
||||
'bmc': bmc,
|
||||
}
|
||||
_configattributes = ('secret.hardwaremanagementuser',
|
||||
'secret.hardwaremanagementpassword',
|
||||
'hardwaremanagement.manager')
|
||||
|
||||
class WrappedWebSocket(wso):
|
||||
|
||||
def set_verify_callback(self, callback):
|
||||
self._certverify = callback
|
||||
|
||||
def connect(self, url, **options):
|
||||
add_tls = url.startswith('wss://')
|
||||
if add_tls:
|
||||
hostname, port, resource, _ = websocket._url.parse_url(url)
|
||||
if hostname[0] != '[' and ':' in hostname:
|
||||
hostname = '[{0}]'.format(hostname)
|
||||
if resource[0] != '/':
|
||||
resource = '/{0}'.format(resource)
|
||||
url = 'ws://{0}:443{1}'.format(hostname,resource)
|
||||
else:
|
||||
return super(WrappedWebSocket, self).connect(url, **options)
|
||||
self.sock_opt.timeout = options.get('timeout', self.sock_opt.timeout)
|
||||
self.sock, addrs = websocket._http.connect(url, self.sock_opt, websocket._http.proxy_info(**options),
|
||||
options.pop('socket', None))
|
||||
self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE)
|
||||
# The above is supersedeed by the _certverify, which provides
|
||||
# known-hosts style cert validaiton
|
||||
bincert = self.sock.getpeercert(binary_form=True)
|
||||
if not self._certverify(bincert):
|
||||
raise pygexc.UnrecognizedCertificate('Unknown certificate', bincert)
|
||||
try:
|
||||
self.handshake_response = websocket._handshake.handshake(self.sock, *addrs, **options)
|
||||
if self.handshake_response.status in websocket._handshake.SUPPORTED_REDIRECT_STATUSES:
|
||||
options['redirect_limit'] = options.pop('redirect_limit', 3) - 1
|
||||
if options['redirect_limit'] < 0:
|
||||
raise Exception('Redirect limit hit')
|
||||
url = self.handshake_response.headers['location']
|
||||
self.sock.close()
|
||||
return self.connect(url, **options)
|
||||
self.connected = True
|
||||
except:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
raise
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TsmConsole(conapi.Console):
|
||||
|
||||
def __init__(self, node, config):
|
||||
self.node = node
|
||||
self.ws = None
|
||||
configdata = config.get_node_attributes([node], _configattributes, decrypt=True)
|
||||
connparams = get_conn_params(node, configdata[node])
|
||||
self.username = connparams['username']
|
||||
self.password = connparams['passphrase']
|
||||
self.bmc = connparams['bmc']
|
||||
self.origbmc = connparams['bmc']
|
||||
if ':' in self.bmc:
|
||||
self.bmc = '[{0}]'.format(self.bmc)
|
||||
self.datacallback = None
|
||||
self.nodeconfig = config
|
||||
self.connected = False
|
||||
|
||||
|
||||
def recvdata(self):
|
||||
while self.connected:
|
||||
pendingdata = self.ws.recv()
|
||||
if pendingdata == '':
|
||||
self.datacallback(conapi.ConsoleEvent.Disconnect)
|
||||
return
|
||||
self.datacallback(pendingdata)
|
||||
|
||||
def connect(self, callback):
|
||||
self.datacallback = callback
|
||||
rc = rcmd.Command(self.origbmc, self.username,
|
||||
self.password,
|
||||
verifycallback=lambda x: True)
|
||||
wc = rc.oem.wc
|
||||
bmc = self.bmc
|
||||
if '%' in self.bmc:
|
||||
prefix = self.bmc.split('%')[0]
|
||||
bmc = prefix + ']'
|
||||
self.ws = WrappedWebSocket(host=bmc)
|
||||
kv = util.TLSCertVerifier(
|
||||
self.nodeconfig, self.node, 'pubkeys.tls_hardwaremanager').verify_cert
|
||||
self.ws.set_verify_callback(kv)
|
||||
self.ws.connect('wss://{0}/sol?CSRFTOKEN={1}'.format(self.bmc, rc.oem.csrftok), host=bmc, cookie='QSESSIONID={0}'.format(wc.cookies['QSESSIONID']))
|
||||
self.connected = True
|
||||
eventlet.spawn_n(self.recvdata)
|
||||
return
|
||||
|
||||
def write(self, data):
|
||||
self.ws.send(data)
|
||||
|
||||
def close(self):
|
||||
if self.ws:
|
||||
self.ws.close()
|
||||
self.connected = False
|
||||
self.datacallback = None
|
||||
|
||||
def create(nodes, element, configmanager, inputdata):
|
||||
if len(nodes) == 1:
|
||||
return TsmConsole(nodes[0], configmanager)
|
||||
@@ -78,18 +78,6 @@ def retrieve(nodes, element, configmanager, inputdata):
|
||||
for node in nodes:
|
||||
workers.add(eventlet.spawn(retrieve_health, configmanager, creds,
|
||||
node, results))
|
||||
elif element[:3] == ['inventory', 'hardware', 'all']:
|
||||
creds = configmanager.get_node_attributes(
|
||||
nodes, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
for node in nodes:
|
||||
workers.add(eventlet.spawn(retrieve_inventory, configmanager,
|
||||
creds, node, results, element))
|
||||
elif element[:3] == ['inventory', 'firmware', 'all']:
|
||||
creds = configmanager.get_node_attributes(
|
||||
nodes, ['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'], decrypt=True)
|
||||
for node in nodes:
|
||||
workers.add(eventlet.spawn(retrieve_firmware, configmanager,
|
||||
creds, node, results, element))
|
||||
else:
|
||||
for node in nodes:
|
||||
yield msg.ConfluentNodeError(node, 'Not Implemented')
|
||||
@@ -117,44 +105,6 @@ def retrieve(nodes, element, configmanager, inputdata):
|
||||
pass
|
||||
|
||||
|
||||
def retrieve_inventory(configmanager, creds, node, results, element):
|
||||
if len(element) == 3:
|
||||
results.put(msg.ChildCollection('all'))
|
||||
results.put(msg.ChildCollection('system'))
|
||||
return
|
||||
wc = cnos_login(node, configmanager, creds)
|
||||
sysinfo = wc.grab_json_response('/nos/api/sysinfo/inventory')
|
||||
invinfo = {
|
||||
'inventory': [{
|
||||
'name': 'System',
|
||||
'present': True,
|
||||
'information': {
|
||||
'Product name': sysinfo['Model'],
|
||||
'Serial Number': sysinfo['Electronic Serial Number'],
|
||||
'Board Serial Number': sysinfo['Serial Number'],
|
||||
'Manufacturer': 'Lenovo',
|
||||
'Model': sysinfo['Machine Type Model'],
|
||||
'FRU Number': sysinfo['FRU'].strip(),
|
||||
}
|
||||
}]
|
||||
}
|
||||
results.put(msg.KeyValueData(invinfo, node))
|
||||
|
||||
|
||||
def retrieve_firmware(configmanager, creds, node, results, element):
|
||||
if len(element) == 3:
|
||||
results.put(msg.ChildCollection('all'))
|
||||
return
|
||||
wc = cnos_login(node, configmanager, creds)
|
||||
sysinfo = wc.grab_json_response('/nos/api/sysinfo/inventory')
|
||||
items = [{
|
||||
'Software': {'version': sysinfo['Software Revision']},
|
||||
},
|
||||
{
|
||||
'BIOS': {'version': sysinfo['BIOS Revision']},
|
||||
}]
|
||||
results.put(msg.Firmware(items, node))
|
||||
|
||||
def retrieve_health(configmanager, creds, node, results):
|
||||
wc = cnos_login(node, configmanager, creds)
|
||||
hinfo = wc.grab_json_response('/nos/api/sysinfo/globalhealthstatus')
|
||||
|
||||
@@ -42,11 +42,6 @@ import traceback
|
||||
if not hasattr(ssl, 'SSLEOFError'):
|
||||
ssl.SSLEOFError = None
|
||||
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
pci_cache = {}
|
||||
|
||||
def get_dns_txt(qstring):
|
||||
@@ -136,7 +131,7 @@ def hex2bin(hexstring):
|
||||
if len(hexvals) < 2:
|
||||
hexvals = hexstring.split(' ')
|
||||
if len(hexvals) < 2:
|
||||
hexvals = [hexstring[i:i+2] for i in range(0, len(hexstring), 2)]
|
||||
hexvals = [hexstring[i:i+2] for i in xrange(0, len(hexstring), 2)]
|
||||
bytedata = [int(i, 16) for i in hexvals]
|
||||
return bytearray(bytedata)
|
||||
|
||||
@@ -283,7 +278,6 @@ def _donothing(data):
|
||||
|
||||
class IpmiConsole(conapi.Console):
|
||||
configattributes = frozenset(_configattributes)
|
||||
bmctonodemapping = {}
|
||||
|
||||
def __init__(self, node, config):
|
||||
self.error = None
|
||||
@@ -301,21 +295,10 @@ class IpmiConsole(conapi.Console):
|
||||
self.bmc = connparams['bmc']
|
||||
self.port = connparams['port']
|
||||
self.connected = False
|
||||
# ok, is self.bmc unique among nodes already
|
||||
# Cannot actually create console until 'connect', when we get callback
|
||||
if (self.bmc in self.bmctonodemapping and
|
||||
self.bmctonodemapping[self.bmc] != node):
|
||||
raise Exception(
|
||||
"Duplicate hardwaremanagement.manager attribute for {0} and {1}".format(
|
||||
node, self.bmctonodemapping[self.bmc]))
|
||||
self.bmctonodemapping[self.bmc] = node
|
||||
|
||||
def __del__(self):
|
||||
self.solconnection = None
|
||||
try:
|
||||
del self.bmctonodemapping[self.bmc]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def handle_data(self, data):
|
||||
if type(data) == dict:
|
||||
@@ -601,13 +584,9 @@ class IpmiHandler(object):
|
||||
self.output.put(msg.CreatedResource(
|
||||
'nodes/{0}/media/uploads/{1}'.format(self.node, u.name)))
|
||||
|
||||
def get_diags(self, savefile, progress):
|
||||
return self.ipmicmd.get_diagnostic_data(
|
||||
savefile, progress=progress, autosuffix=True)
|
||||
|
||||
def handle_servicedata_fetch(self):
|
||||
u = firmwaremanager.Updater(
|
||||
self.node, self.get_diags,
|
||||
self.node, self.ipmicmd.get_diagnostic_data,
|
||||
self.inputdata.nodefile(self.node), self.tenant, type='ffdc',
|
||||
owner=self.current_user)
|
||||
self.output.put(msg.CreatedResource(
|
||||
@@ -679,7 +658,7 @@ class IpmiHandler(object):
|
||||
if len(self.element) == 4:
|
||||
# A list of destinations
|
||||
maxdest = self.ipmicmd.get_alert_destination_count()
|
||||
for alertidx in range(0, maxdest + 1):
|
||||
for alertidx in xrange(0, maxdest + 1):
|
||||
self.output.put(msg.ChildCollection(alertidx))
|
||||
return
|
||||
elif len(self.element) == 5:
|
||||
|
||||
@@ -175,15 +175,12 @@ class IpmiCommandWrapper(ipmicommand.Command):
|
||||
raise exc.TargetEndpointUnreachable(se.strerror)
|
||||
else:
|
||||
raise exc.TargetEndpointUnreachable(str(se))
|
||||
if isinstance(se, socket.timeout):
|
||||
if isinstance(se, socket.timeout) or (len(se) > 1 and se[1] == 'EHOSTUNREACH'):
|
||||
raise exc.TargetEndpointUnreachable('timeout')
|
||||
raise
|
||||
except pygexc.PyghmiException as pe:
|
||||
if 'Access Denied' in str(pe):
|
||||
raise exc.TargetEndpointBadCredentials()
|
||||
if 'Redfish not ready' in str(pe):
|
||||
raise exc.TargetEndpointUnreachable('Redfish not yet ready')
|
||||
raise
|
||||
|
||||
def close_confluent(self):
|
||||
if self._attribwatcher:
|
||||
@@ -465,13 +462,9 @@ class IpmiHandler(object):
|
||||
self.output.put(msg.CreatedResource(
|
||||
'nodes/{0}/media/uploads/{1}'.format(self.node, u.name)))
|
||||
|
||||
def get_diags(self, savefile, progress):
|
||||
return self.ipmicmd.get_diagnostic_data(
|
||||
savefile, progress=progress, autosuffix=True)
|
||||
|
||||
def handle_servicedata_fetch(self):
|
||||
u = firmwaremanager.Updater(
|
||||
self.node, self.get_diags,
|
||||
self.node, self.ipmicmd.get_diagnostic_data,
|
||||
self.inputdata.nodefile(self.node), self.tenant, type='ffdc',
|
||||
owner=self.current_user)
|
||||
self.output.put(msg.CreatedResource(
|
||||
|
||||
@@ -13,9 +13,9 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
|
||||
Prefix: %{_prefix}
|
||||
BuildArch: noarch
|
||||
%if "%{dist}" == ".el8"
|
||||
Requires: python3-pyghmi >= 1.0.34, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-pyte, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-monotonic, python3-websocket-client
|
||||
Requires: python3-pyghmi >= 1.0.34, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-pyte, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-monotonic
|
||||
%else
|
||||
Requires: python-pyghmi >= 1.0.34, python-eventlet, python-greenlet, python-pycryptodomex >= 3.4.7, confluent_client, python-pyparsing, python-paramiko, python-dns, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-pyte, python-lxml, python-eficompressor, python-setuptools, python-dateutil, python2-websocket-client
|
||||
Requires: python-pyghmi >= 1.0.34, python-eventlet, python-greenlet, python-pycryptodomex >= 3.4.7, confluent_client, python-pyparsing, python-paramiko, python-dns, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-pyte, python-lxml, python-eficompressor, python-setuptools, python-dateutil
|
||||
%endif
|
||||
Vendor: Jarrod Johnson <jjohnson2@lenovo.com>
|
||||
Url: http://xcat.sf.net/
|
||||
@@ -42,10 +42,7 @@ python2 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUI
|
||||
for file in $(grep confluent/__init__.py INSTALLED_FILES.bare); do
|
||||
rm $RPM_BUILD_ROOT/$file
|
||||
done
|
||||
grep -v confluent/__init__.py INSTALLED_FILES.bare | grep -v etc/init.d/confluent > INSTALLED_FILES
|
||||
rm $RPM_BUILD_ROOT/etc/init.d/confluent
|
||||
rmdir $RPM_BUILD_ROOT/etc/init.d
|
||||
rmdir $RPM_BUILD_ROOT/etc
|
||||
grep -v confluent/__init__.py INSTALLED_FILES.bare > INSTALLED_FILES
|
||||
cat INSTALLED_FILES
|
||||
|
||||
%triggerin -- python-pyghmi
|
||||
|
||||
@@ -14,7 +14,6 @@ setup(
|
||||
'confluent/discovery/handlers/',
|
||||
'confluent/networking/',
|
||||
'confluent/plugins/hardwaremanagement/',
|
||||
'confluent/plugins/console/',
|
||||
'confluent/plugins/shell/',
|
||||
'confluent/collective/',
|
||||
'confluent/plugins/configuration/'],
|
||||
|
||||
+85
-44
@@ -2,84 +2,125 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <crypt.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define OUI_ETHERTYPE 0x88b7
|
||||
#define MAXPACKET 1024
|
||||
#define CHDR "\xa4\x8c\xdb\x30\x01"
|
||||
|
||||
int get_interface_index(int sock, char *interface) {
|
||||
struct ifreq req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
strncpy(req.ifr_name, interface, IFNAMSIZ);
|
||||
if (ioctl(sock, SIOCGIFINDEX, &req) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return req.ifr_ifindex;
|
||||
}
|
||||
static const char cryptalpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./";
|
||||
|
||||
unsigned char* genpasswd() {
|
||||
unsigned char* genpasswd(int len) {
|
||||
unsigned char * passwd;
|
||||
int urandom;
|
||||
passwd = calloc(33, sizeof(char));
|
||||
passwd = calloc(len + 1, sizeof(char));
|
||||
urandom = open("/dev/urandom", O_RDONLY);
|
||||
read(urandom, passwd, 32);
|
||||
read(urandom, passwd, len);
|
||||
close(urandom);
|
||||
for (urandom = 0; urandom < 32; urandom++) {
|
||||
passwd[urandom] = 0x30 + (passwd[urandom] >> 2);
|
||||
for (urandom = 0; urandom < len; urandom++) {
|
||||
passwd[urandom] = cryptalpha[passwd[urandom] >> 2];
|
||||
}
|
||||
return passwd;
|
||||
|
||||
}
|
||||
|
||||
int parse_macaddr(char* macaddr) {
|
||||
unsigned char *curr;
|
||||
unsigned char idx;
|
||||
curr = strtok(macaddr, ":-");
|
||||
idx = 0;
|
||||
|
||||
while (curr != NULL) {
|
||||
macaddr[idx++] = strtoul(curr, NULL, 16);
|
||||
curr = strtok(NULL, ":-");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
int sock;
|
||||
int iface;
|
||||
unsigned char currlen, currtype;
|
||||
unsigned char* passwd;
|
||||
unsigned char* cryptedpass;
|
||||
unsigned char* macaddr;
|
||||
|
||||
struct timeval timeout;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *addrs;
|
||||
struct addrinfo *curr;
|
||||
struct sockaddr_in net4bind;
|
||||
struct sockaddr_in net6bind;
|
||||
unsigned char buffer[MAXPACKET];
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
memset(&net4bind, 0, sizeof(struct sockaddr_in));
|
||||
memset(&net6bind, 0, sizeof(struct sockaddr_in));
|
||||
memset(&buffer, 0, MAXPACKET);
|
||||
memset(&timeout, 0, sizeof(struct timeval));
|
||||
timeout.tv_sec = 10;
|
||||
net4bind.sin_port = htons(302);
|
||||
net6bind.sin_port = htons(302);
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
passwd = genpasswd();
|
||||
passwd = genpasswd(32);
|
||||
memset(buffer, 0, MAXPACKET);
|
||||
strncpy(buffer, "$5$", 3);
|
||||
cryptedpass = genpasswd(8);
|
||||
strncpy(buffer + 3, cryptedpass, 8);
|
||||
free(cryptedpass);
|
||||
cryptedpass = crypt(passwd, buffer);
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "Missing interface name and target MAC\n");
|
||||
fprintf(stderr, "Missing node name and manager\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("%s\n", argv[2]);
|
||||
parse_macaddr(argv[2]);
|
||||
printf("%s\n", argv[2]);
|
||||
sock = socket(AF_PACKET, SOCK_DGRAM, htons(OUI_ETHERTYPE));
|
||||
if (sock < 0) {
|
||||
fprintf(stderr, "Unable to open socket (run as root?)\n");
|
||||
sock = getaddrinfo(argv[2], "301", &hints, &addrs);
|
||||
if (sock != 0) {
|
||||
fprintf(stderr, "Error trying to resolve %s\n", argv[2]);
|
||||
exit(1);
|
||||
}
|
||||
iface = get_interface_index(sock, argv[1]);
|
||||
if (iface < 0) {
|
||||
fprintf(stderr, "Unable to find specified interface '%s'\n", argv[1]);
|
||||
for (curr = addrs; curr != NULL; curr = curr->ai_next) {
|
||||
sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
|
||||
if (sock < 0) continue;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int));
|
||||
if (curr->ai_family == AF_INET) {
|
||||
bind(sock, (struct sockaddr*)&net4bind, sizeof(struct sockaddr_in));
|
||||
} else if (curr->ai_family == AF_INET6) {
|
||||
bind(sock, (struct sockaddr*)&net6bind, sizeof(struct sockaddr_in6));
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if (connect(sock, curr->ai_addr, curr->ai_addrlen) == 0) break;
|
||||
}
|
||||
if (curr == NULL) {
|
||||
fprintf(stderr, "Unable to reach %s\n", argv[2]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
freeaddrinfo(addrs);
|
||||
read(sock, buffer, 8);
|
||||
if (memcmp(buffer, "\xc2\xd1-\xa8\x80\xd8j\xba", 8) != 0) {
|
||||
fprintf(stderr, "Unrecognized server\n");
|
||||
exit(1);
|
||||
}
|
||||
dprintf(sock, "\x01%c%s", strlen(argv[1]), argv[1]);
|
||||
write(sock, "\x00\x00", 2);
|
||||
memset(buffer, 0, MAXPACKET);
|
||||
read(sock, buffer, 2);
|
||||
while (buffer[0] != 255) {
|
||||
currtype = buffer[0];
|
||||
currlen = buffer[1];
|
||||
memset(buffer, 0, MAXPACKET);
|
||||
if (currlen) {
|
||||
read(sock, buffer, currlen); // Max is 255, well under MAX_PACKET
|
||||
}
|
||||
if (currtype == 2) {
|
||||
dprintf(sock, "\x03%c", currlen);
|
||||
write(sock, buffer, currlen);
|
||||
dprintf(sock, "\x04%c%s", strlen(cryptedpass), cryptedpass);
|
||||
write(sock, "\x00\x00", 2);
|
||||
} else if (currtype == 5) {
|
||||
printf(passwd);
|
||||
printf("\n");
|
||||
exit(0);
|
||||
}
|
||||
buffer[0] = 255;
|
||||
read(sock, buffer, 2);
|
||||
}
|
||||
fprintf(stderr, "Password was not accepted\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
+46
-6
@@ -68,7 +68,11 @@ int main(int argc, char* argv[]) {
|
||||
struct sockaddr_in6 addr, dst;
|
||||
struct sockaddr_in addr4, dst4;
|
||||
char msg[1024];
|
||||
char *nodenameidx;
|
||||
char nodename[1024];
|
||||
char lastnodename[1024];
|
||||
char lastmsg[1024];
|
||||
char last6msg[1024];
|
||||
int ifidx, offset;
|
||||
fd_set rfds;
|
||||
struct timeval tv;
|
||||
@@ -80,6 +84,10 @@ int main(int argc, char* argv[]) {
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
memset(&dst4, 0, sizeof(dst4));
|
||||
memset(nodename, 0, 1024);
|
||||
memset(lastnodename, 0, 1024);
|
||||
memset(lastmsg, 0, 1024);
|
||||
memset(last6msg, 0, 1024);
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_addr = in6addr_any;
|
||||
addr.sin6_port = htons(190);
|
||||
@@ -141,20 +149,52 @@ int main(int argc, char* argv[]) {
|
||||
if (ifidx == -1) perror("Unable to select");
|
||||
if (ifidx) {
|
||||
if (FD_ISSET(n4, &rfds)) {
|
||||
recvfrom(n4, msg, 1024, 0, (struct sockaddr *)&dst4, &dst4size);
|
||||
memset(msg, 0, 1024);
|
||||
/* Deny packet access to the last 24 bytes to assure null */
|
||||
recvfrom(n4, msg, 1000, 0, (struct sockaddr *)&dst4, &dst4size);
|
||||
if (nodenameidx = strstr(msg, "NODENAME: ")) {
|
||||
nodenameidx += 10;
|
||||
strncpy(nodename, nodenameidx, 1024);
|
||||
nodenameidx = strstr(nodenameidx, "\r");
|
||||
if (nodenameidx) { nodenameidx[0] = 0; }
|
||||
if (strncmp(lastnodename, nodename, 1024) != 0) {
|
||||
printf("NODENAME: %s\n", nodename);
|
||||
strncpy(lastnodename, nodename, 1024);
|
||||
}
|
||||
}
|
||||
memset(msg, 0, 1024);
|
||||
inet_ntop(dst4.sin_family, &dst4.sin_addr, msg, dst4size);
|
||||
/* Take measure from printing out the same ip twice in a row */
|
||||
if (strncmp(lastmsg, msg, 1024) != 0) {
|
||||
printf("%s\n", msg);
|
||||
sendto(n4, "PING", 4, 0, (const struct sockaddr *)&dst4, dst4size);
|
||||
printf("MANAGER: %s\n", msg);
|
||||
strncpy(lastmsg, msg, 1024);
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(ns, &rfds)) {
|
||||
recvfrom(ns, msg, 1024, 0, (struct sockaddr *)&dst, &dstsize);
|
||||
memset(msg, 0, 1024);
|
||||
/* Deny packet access to the last 24 bytes to assure null */
|
||||
recvfrom(ns, msg, 1000, 0, (struct sockaddr *)&dst, &dstsize);
|
||||
if (nodenameidx = strstr(msg, "NODENAME: ")) {
|
||||
nodenameidx += 10;
|
||||
strncpy(nodename, nodenameidx, 1024);
|
||||
nodenameidx = strstr(nodenameidx, "\r");
|
||||
if (nodenameidx) { nodenameidx[0] = 0; }
|
||||
if (strncmp(lastnodename, nodename, 1024) != 0) {
|
||||
printf("NODENAME: %s\n", nodename);
|
||||
strncpy(lastnodename, nodename, 1024);
|
||||
}
|
||||
}
|
||||
memset(msg, 0, 1024);
|
||||
inet_ntop(dst.sin6_family, &dst.sin6_addr, msg, dstsize);
|
||||
if (strncmp(lastmsg, msg, 1024) != 0) {
|
||||
printf("%s\n", msg);
|
||||
strncpy(lastmsg, msg, 1024);
|
||||
if (strncmp(last6msg, msg, 1024) != 0) {
|
||||
sendto(ns, "PING", 4, 0, (const struct sockaddr *)&dst, dstsize);
|
||||
printf("MANAGER: %s", msg);
|
||||
if (strncmp(msg, "fe80::", 6) == 0) {
|
||||
printf("%%%u", dst.sin6_scope_id);
|
||||
}
|
||||
printf("\n");
|
||||
strncpy(last6msg, msg, 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user