mirror of
https://github.com/xcat2/confluent.git
synced 2026-09-29 00:31:09 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 123a002b6c | |||
| 2721f2bd7b | |||
| 43f26d8cbb |
+1
-7
@@ -1,7 +1 @@
|
||||
*.pyc
|
||||
.*.
|
||||
confluent_client/man/man*
|
||||
.vscode
|
||||
.*.sw*
|
||||
.sw*
|
||||
.idea/*
|
||||
.idea
|
||||
|
||||
@@ -175,3 +175,28 @@
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Confluent
|
||||
|
||||
 [](https://github.com/xcat2/confluent/blob/master/LICENSE)
|
||||
|
||||
Confluent is a software package to handle essential bootstrap and operation of scale-out server configurations.
|
||||
It supports stateful and stateless deployments for various operating systems.
|
||||
|
||||
Check [this page](https://hpc.lenovo.com/users/documentation/whatisconfluent.html
|
||||
) for a more detailed list of features.
|
||||
|
||||
Confluent is the modern successor of [xCAT](https://github.com/xcat2/xcat-core).
|
||||
If you're coming from xCAT, check out [this comparison](https://hpc.lenovo.com/users/documentation/confluentvxcat.html).
|
||||
|
||||
# Documentation
|
||||
|
||||
Confluent documentation is hosted on hpc.lenovo.com: https://hpc.lenovo.com/users/documentation/
|
||||
|
||||
# Download
|
||||
|
||||
Get the latest version from: https://hpc.lenovo.com/users/downloads/
|
||||
|
||||
Check release notes on: https://hpc.lenovo.com/users/news/
|
||||
|
||||
# Open Source License
|
||||
|
||||
Confluent is made available under the Apache 2.0 license: https://opensource.org/license/apache-2-0
|
||||
|
||||
# Developers
|
||||
|
||||
Want to help? Submit a [Pull Request](https://github.com/xcat2/confluent/pulls).
|
||||
@@ -1,77 +0,0 @@
|
||||
Project: https://hpc.lenovo.com/users/
|
||||
Source: https://github.com/lenovo/confluent
|
||||
Upstream-Name: confluent-client
|
||||
|
||||
All file of the Confluent-client software is distributed under the terms of an Apache-2.0 license as indicated below:
|
||||
|
||||
Files: *
|
||||
Copyright: 2014-2019 Lenovo
|
||||
License: Apache-2.0
|
||||
|
||||
File: client.py:
|
||||
Copyright: 2014 IBM Corporation
|
||||
2015-2019 Lenovo
|
||||
License: Apache-2.0
|
||||
|
||||
File: sortutil.py,
|
||||
tlvdata.py
|
||||
Copyright: 2014 IBM Corporation
|
||||
2015-2016 Lenovo
|
||||
License: Apache-2.0
|
||||
|
||||
File: tlv.py
|
||||
Copyright: 2014 IBM Corporation
|
||||
License: Apache-2.0
|
||||
|
||||
License: Apache-2.0
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
import os
|
||||
import sys
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
try:
|
||||
sys.path.remove(path)
|
||||
except Exception:
|
||||
pass
|
||||
path = os.path.realpath(os.path.join(path, '..', 'confluent_server'))
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.config.attributes as attr
|
||||
import shutil
|
||||
|
||||
shutil.copyfile('doc/man/nodeattrib.ronn.tmpl', 'doc/man/nodeattrib.ronn')
|
||||
shutil.copyfile('doc/man/nodegroupattrib.ronn.tmpl', 'doc/man/nodegroupattrib.ronn')
|
||||
with open('doc/man/nodeattrib.ronn', 'a') as outf:
|
||||
for field in sorted(attr.node):
|
||||
outf.write('\n* `{0}`:\n {1}\n'.format(field, attr.node[field]['description']))
|
||||
with open('doc/man/nodegroupattrib.ronn', 'a') as outf:
|
||||
for field in sorted(attr.node):
|
||||
outf.write('\n* `{0}`:\n {1}\n'.format(field, attr.node[field]['description']))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import select
|
||||
import sys
|
||||
|
||||
@@ -44,8 +43,6 @@ argparser.add_option('-d', '--diff', action='store_true',
|
||||
'output group and others')
|
||||
argparser.add_option('-w', '--watch', action='store_true',
|
||||
help='Show intermediate results while running')
|
||||
argparser.add_option('-g', '--groupcount', action='store_true',
|
||||
help='Show count of output groups rather than the actual output')
|
||||
argparser.add_option('-s', '--skipcommon', action='store_true',
|
||||
help='Do not print most common result, only non modal '
|
||||
'groups, useful when combined with -d')
|
||||
@@ -72,9 +69,6 @@ def print_current():
|
||||
if options.diff:
|
||||
grouped.print_deviants(skipmodal=options.skipcommon, count=options.count,
|
||||
reverse=options.reverse, basenode=options.base)
|
||||
elif options.groupcount:
|
||||
grouped.generate_byoutput()
|
||||
print(len(grouped.byoutput))
|
||||
else:
|
||||
grouped.print_all(skipmodal=options.skipcommon,
|
||||
count=options.count,
|
||||
@@ -85,28 +79,19 @@ fullline = sys.stdin.readline()
|
||||
printpending = True
|
||||
clearpending = False
|
||||
holdoff = 0
|
||||
padded = None
|
||||
while fullline:
|
||||
for line in fullline.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
if ':' not in line:
|
||||
if ': ' not in line:
|
||||
line = 'UNKNOWN: ' + line
|
||||
if options.log:
|
||||
node, output = line.split(':', 1)
|
||||
if padded is None:
|
||||
if output.startswith(' '):
|
||||
padded = True
|
||||
else:
|
||||
padded = False
|
||||
if padded:
|
||||
output = re.sub(r'^ ', '', output)
|
||||
node, output = line.split(': ', 1)
|
||||
currlog = options.log.format(node=node, nodename=node)
|
||||
with open(currlog, mode='a') as log:
|
||||
log.write(output + '\n')
|
||||
continue
|
||||
node, output = line.split(':', 1)
|
||||
grouped.add_line(node, output)
|
||||
grouped.add_line(*line.split(': ', 1))
|
||||
if options.watch:
|
||||
if not holdoff:
|
||||
holdoff = os.times()[4] + 0.250
|
||||
|
||||
+102
-152
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2014 IBM Corporation
|
||||
@@ -76,11 +76,6 @@ import confluent.termhandler as termhandler
|
||||
import confluent.tlvdata as tlvdata
|
||||
import confluent.client as client
|
||||
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
unicode = str
|
||||
|
||||
conserversequence = '\x05c' # ctrl-e, c
|
||||
clearpowermessage = False
|
||||
|
||||
@@ -94,10 +89,6 @@ except NameError:
|
||||
netserver = None
|
||||
laststate = {}
|
||||
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
class BailOut(Exception):
|
||||
def __init__(self, errorcode=0):
|
||||
@@ -204,9 +195,9 @@ def prompt():
|
||||
if os.environ.get('TERM', '') not in ('linux'):
|
||||
sys.stdout.write('\x1b]0;confetty: %s\x07' % target)
|
||||
try:
|
||||
return input(target + ' -> ')
|
||||
return raw_input(target + ' -> ')
|
||||
except KeyboardInterrupt:
|
||||
print("")
|
||||
print ""
|
||||
return ""
|
||||
except EOFError: # ctrl-d
|
||||
print("exit")
|
||||
@@ -292,8 +283,6 @@ def rcompleter(text, state):
|
||||
|
||||
|
||||
def parse_command(command):
|
||||
if isinstance(command, list):
|
||||
return command
|
||||
try:
|
||||
args = shlex.split(command, posix=True)
|
||||
except ValueError as ve:
|
||||
@@ -306,11 +295,8 @@ currchildren = None
|
||||
|
||||
|
||||
def print_result(res):
|
||||
global exitcode
|
||||
if 'errorcode' in res or 'error' in res:
|
||||
print(res['error'])
|
||||
if 'errorcode' in res:
|
||||
exitcode |= res['errorcode']
|
||||
print res['error']
|
||||
return
|
||||
if 'databynode' in res:
|
||||
print_result(res['databynode'])
|
||||
@@ -323,9 +309,9 @@ def print_result(res):
|
||||
attrstr = '%s=%s' % (key, recurse_format(res[key]))
|
||||
elif not isinstance(res[key], dict):
|
||||
try:
|
||||
print('{0}: {1}'.format(key, res[key]))
|
||||
print '{0}: {1}'.format(key, res[key])
|
||||
except UnicodeEncodeError:
|
||||
print('{0}: {1}'.format(key, repr(res[key])))
|
||||
print '{0}: {1}'.format(key, repr(res[key]))
|
||||
continue
|
||||
elif 'value' in res[key] and res[key]['value'] is not None:
|
||||
attrstr = '%s="%s"' % (key, res[key]['value'])
|
||||
@@ -338,7 +324,7 @@ def print_result(res):
|
||||
else:
|
||||
sys.stdout.write('{0}: '.format(key))
|
||||
if isinstance(res[key], str) or isinstance(res[key], unicode):
|
||||
print(res[key])
|
||||
print res[key]
|
||||
else:
|
||||
print_result(res[key])
|
||||
continue
|
||||
@@ -373,7 +359,7 @@ def do_command(command, server):
|
||||
if argv[0] == 'exit':
|
||||
if os.environ.get('TERM', '') not in ('linux'):
|
||||
sys.stdout.write('\x1b]0;\x07')
|
||||
raise BailOut()
|
||||
raise Bailout()
|
||||
elif argv[0] in ('help', '?'):
|
||||
return print_help()
|
||||
elif argv[0] == 'cd':
|
||||
@@ -437,10 +423,10 @@ def do_command(command, server):
|
||||
for res in session.read(targpath):
|
||||
if 'item' in res: # a link relation
|
||||
if type(res['item']) == dict:
|
||||
print(res['item']["href"])
|
||||
print res['item']["href"]
|
||||
else:
|
||||
for item in res['item']:
|
||||
print(item["href"])
|
||||
print item["href"]
|
||||
else: # generic attributes to list
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
@@ -518,13 +504,6 @@ def makecall(callout, args):
|
||||
if 'errorcode' in response:
|
||||
exitcode = response['errorcode']
|
||||
sys.stderr.write('Error: ' + response['error'] + '\n')
|
||||
if 'databynode' in response:
|
||||
lresponse = response['databynode']
|
||||
for node in lresponse:
|
||||
if 'errorcode' in lresponse[node]:
|
||||
exitcode = lresponse[node]['errorcode']
|
||||
if 'error' in lresponse[node]:
|
||||
sys.stderr.write('{0}: Error - {1}\n'.format(node, lresponse[node]['error']))
|
||||
|
||||
|
||||
def clearvalues(resource, attribs):
|
||||
@@ -635,7 +614,7 @@ def quitconfetty(code=0, fullexit=False, fixterm=True):
|
||||
global didconsole
|
||||
if fixterm or didconsole:
|
||||
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
||||
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl & ~os.O_NONBLOCK)
|
||||
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
|
||||
if oldtcattr is not None:
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
|
||||
# Request default color scheme, to undo potential weirdness of terminal
|
||||
@@ -663,28 +642,6 @@ def get_session_node(shellargs):
|
||||
return None
|
||||
|
||||
|
||||
def run_inline_command(path, arg, completion, **kwargs):
|
||||
tlvdata.send(session.connection, {'operation': 'pause',
|
||||
'path': currconsole})
|
||||
buffdata = ''
|
||||
while select.select((session.connection,), (), (), 0)[0]:
|
||||
buffdata += consume_termdata(session.connection, bufferonly=True)
|
||||
rc = session.simple_noderange_command(consolename, path, arg, **kwargs)
|
||||
tlvdata.send(session.connection, {'operation': 'resume',
|
||||
'path': currconsole})
|
||||
sys.stdout.write(completion)
|
||||
sys.stdout.flush()
|
||||
if buffdata:
|
||||
try:
|
||||
sys.stdout.write(buffdata)
|
||||
except UnicodeEncodeError:
|
||||
sys.stdout.buffer.write(buffdata.encode('utf8'))
|
||||
except IOError: # Some times circumstances are bad
|
||||
# resort to byte at a time...
|
||||
for d in buffdata:
|
||||
sys.stdout.write(d)
|
||||
return rc
|
||||
|
||||
def conserver_command(filehandle, localcommand):
|
||||
# x - conserver has that as 'show baud', I am inclined to replace that with
|
||||
# 'request exclusive'
|
||||
@@ -720,13 +677,13 @@ def conserver_command(filehandle, localcommand):
|
||||
localcommand = get_command_bytes(filehandle, localcommand, cmdlen)
|
||||
|
||||
if localcommand[1] == 'o': # off
|
||||
sys.stdout.write("powering off...")
|
||||
sys.stdout.flush()
|
||||
run_inline_command('/power/state', 'off', 'complete]')
|
||||
print("powering off...")
|
||||
session.simple_noderange_command(consolename, '/power/state', 'off')
|
||||
print("complete]\r")
|
||||
elif localcommand[1] == 's': # shutdown
|
||||
sys.stdout.write("shutting down...")
|
||||
sys.stdout.flush()
|
||||
run_inline_command('/power/state', 'shutdown', 'complete]')
|
||||
print("shutting down...")
|
||||
session.simple_noderange_command(consolename, '/power/state', 'shutdown')
|
||||
print("complete]\r")
|
||||
elif localcommand[1] == 'b': # boot
|
||||
cmdlen += 1
|
||||
localcommand = get_command_bytes(filehandle, localcommand, cmdlen)
|
||||
@@ -737,46 +694,59 @@ def conserver_command(filehandle, localcommand):
|
||||
bootmode = 'uefi'
|
||||
bootdev = 'setup'
|
||||
|
||||
rc = run_inline_command('/boot/nextdevice', bootdev, '', bootmode=bootmode)
|
||||
rc = session.simple_noderange_command(consolename, '/boot/nextdevice', bootdev, bootmode=bootmode)
|
||||
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
rc = run_inline_command('/power/state', 'boot', 'complete]')
|
||||
if rc:
|
||||
print("Error]")
|
||||
|
||||
elif localcommand[2] == 'n': # boot to network
|
||||
sys.stdout.write("booting to network...")
|
||||
sys.stdout.flush()
|
||||
bootmode = 'uefi'
|
||||
bootdev = 'network'
|
||||
rc = run_inline_command('/boot/nextdevice', bootdev, '', bootmode=bootmode)
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
rc = run_inline_command('/power/state', 'boot', 'complete]')
|
||||
rc = session.simple_noderange_command(consolename, '/power/state', 'boot')
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
print("complete]\r")
|
||||
|
||||
elif localcommand[2] == 'n': # boot to network
|
||||
print("booting to network...")
|
||||
|
||||
bootmode = 'uefi'
|
||||
bootdev = 'network'
|
||||
|
||||
rc = session.simple_noderange_command(consolename, '/boot/nextdevice', bootdev, bootmode=bootmode)
|
||||
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
rc = session.simple_noderange_command(consolename, '/power/state', 'boot')
|
||||
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
print("complete]\r")
|
||||
|
||||
elif localcommand[2] == '\x0d': # boot to default
|
||||
print("booting to default...")
|
||||
|
||||
bootmode = 'uefi'
|
||||
bootdev = 'default'
|
||||
|
||||
rc = run_inline_command('/boot/nextdevice', bootdev, '', bootmode=bootmode)
|
||||
rc = session.simple_noderange_command(consolename, '/boot/nextdevice', bootdev, bootmode=bootmode)
|
||||
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
rc = run_inline_command('/power/state', 'boot', 'complete]')
|
||||
rc = session.simple_noderange_command(consolename, '/power/state', 'boot')
|
||||
|
||||
if rc:
|
||||
print("Error]\r")
|
||||
else:
|
||||
print("complete]\r")
|
||||
|
||||
else:
|
||||
print("Unknown boot state.]\r")
|
||||
|
||||
else:
|
||||
print("Unknown power state.]\r")
|
||||
|
||||
check_power_state()
|
||||
elif localcommand[0] == 'r':
|
||||
sys.stdout.write('\x1b7\x1b[999;999H\x1b[6n')
|
||||
@@ -874,7 +844,7 @@ def server_connect():
|
||||
passphrase = os.environ['CONFLUENT_PASSPHRASE']
|
||||
session.authenticate(username, passphrase)
|
||||
while not session.authenticated:
|
||||
username = input("Name: ")
|
||||
username = raw_input("Name: ")
|
||||
passphrase = getpass.getpass("Passphrase: ")
|
||||
session.authenticate(username, passphrase)
|
||||
|
||||
@@ -894,7 +864,7 @@ def main():
|
||||
global inconsole
|
||||
try:
|
||||
server_connect()
|
||||
except (EOFError, KeyboardInterrupt) as _:
|
||||
except EOFError, KeyboardInterrupt:
|
||||
raise BailOut(0)
|
||||
except socket.gaierror:
|
||||
sys.stderr.write('Could not connect to confluent\n')
|
||||
@@ -923,7 +893,8 @@ def main():
|
||||
do_command("start /nodes/%s/console/session" % session_node, netserver)
|
||||
doexit = True
|
||||
elif shellargs:
|
||||
do_command(shellargs, netserver)
|
||||
command = " ".join(shellargs)
|
||||
do_command(command, netserver)
|
||||
quitconfetty(fullexit=True, fixterm=False)
|
||||
|
||||
powerstate = None
|
||||
@@ -938,7 +909,56 @@ def main():
|
||||
rdylist = ()
|
||||
for fh in rdylist:
|
||||
if fh == session.connection:
|
||||
consume_termdata(fh)
|
||||
# this only should get called in the
|
||||
# case of a console session
|
||||
# each command should slurp up all relevant
|
||||
# recv potential
|
||||
#fh.read()
|
||||
try:
|
||||
data = tlvdata.recv(fh)
|
||||
except Exception:
|
||||
data = None
|
||||
if type(data) == dict:
|
||||
updatestatus(data)
|
||||
continue
|
||||
if data is not None:
|
||||
if clearpowermessage:
|
||||
sys.stdout.write("\x1b[2J\x1b[;H")
|
||||
clearpowermessage = False
|
||||
try:
|
||||
sys.stdout.write(data)
|
||||
except IOError: # Some times circumstances are bad
|
||||
# resort to byte at a time...
|
||||
for d in data:
|
||||
sys.stdout.write(d)
|
||||
now = time.time()
|
||||
if ('showtime' not in laststate or
|
||||
(now // 60) != laststate['showtime'] // 60):
|
||||
# don't bother churning if minute does not change
|
||||
laststate['showtime'] = now
|
||||
updatestatus()
|
||||
sys.stdout.flush()
|
||||
else:
|
||||
deadline = 5
|
||||
connected = False
|
||||
while not connected and deadline > 0:
|
||||
try:
|
||||
server_connect()
|
||||
connected = True
|
||||
except (socket.gaierror, socket.error):
|
||||
pass
|
||||
if not connected:
|
||||
time.sleep(1)
|
||||
deadline -=1
|
||||
if connected:
|
||||
do_command(
|
||||
"start /nodes/%s/console/session skipreplay=True" % consolename,
|
||||
netserver)
|
||||
else:
|
||||
doexit = True
|
||||
inconsole = False
|
||||
sys.stdout.write("\r\n[remote disconnected]\r\n")
|
||||
break
|
||||
else:
|
||||
try:
|
||||
myinput = fh.read()
|
||||
@@ -948,9 +968,8 @@ def main():
|
||||
except IOError:
|
||||
pass
|
||||
if powerstate is None or powertime < time.time() - 10: # Check powerstate every 10 seconds
|
||||
if powerstate == None:
|
||||
powerstate = True
|
||||
powertime = time.time()
|
||||
powerstate = True
|
||||
check_power_state()
|
||||
else:
|
||||
currcommand = prompt()
|
||||
@@ -965,65 +984,6 @@ def main():
|
||||
sys.stdout.write('Lost connection to server')
|
||||
quitconfetty(fullexit=True)
|
||||
|
||||
def consume_termdata(fh, bufferonly=False):
|
||||
global clearpowermessage
|
||||
try:
|
||||
data = tlvdata.recv(fh)
|
||||
except Exception:
|
||||
data = None
|
||||
if type(data) == dict:
|
||||
updatestatus(data)
|
||||
return ''
|
||||
if data is not None:
|
||||
data = client.stringify(data)
|
||||
if clearpowermessage:
|
||||
sys.stdout.write("\x1b[2J\x1b[;H")
|
||||
clearpowermessage = False
|
||||
if bufferonly:
|
||||
return data
|
||||
try:
|
||||
sys.stdout.write(data)
|
||||
except UnicodeEncodeError:
|
||||
sys.stdout.buffer.write(data.encode('utf8'))
|
||||
except IOError: # Some times circumstances are bad
|
||||
# resort to byte at a time...
|
||||
for d in data:
|
||||
sys.stdout.write(d)
|
||||
now = time.time()
|
||||
if ('showtime' not in laststate or
|
||||
(now // 60) != laststate['showtime'] // 60):
|
||||
# don't bother churning if minute does not change
|
||||
laststate['showtime'] = now
|
||||
updatestatus()
|
||||
try:
|
||||
sys.stdout.flush()
|
||||
except Exception:
|
||||
# EWOULDBLOCK causes this to raise, ignore
|
||||
# this scenario comfortable that it
|
||||
# will come out soon enough
|
||||
pass
|
||||
else:
|
||||
deadline = 5
|
||||
connected = False
|
||||
while not connected and deadline > 0:
|
||||
try:
|
||||
server_connect()
|
||||
connected = True
|
||||
except Exception:
|
||||
pass
|
||||
if not connected:
|
||||
time.sleep(1)
|
||||
deadline -=1
|
||||
if connected:
|
||||
do_command(
|
||||
"start /nodes/%s/console/session skipreplay=True" % consolename,
|
||||
netserver)
|
||||
else:
|
||||
doexit = True
|
||||
inconsole = False
|
||||
sys.stdout.write("\r\n[remote disconnected]\r\n")
|
||||
return ''
|
||||
|
||||
if __name__ == '__main__':
|
||||
errcode = 0
|
||||
deadline = 0
|
||||
@@ -1033,18 +993,8 @@ if __name__ == '__main__':
|
||||
main()
|
||||
except BailOut as e:
|
||||
errcode = e.errorcode
|
||||
except Exception as e:
|
||||
import traceback
|
||||
excinfo = traceback.print_exc()
|
||||
try:
|
||||
quitconfetty()
|
||||
except Exception:
|
||||
pass
|
||||
print(excinfo)
|
||||
finally:
|
||||
if deadline and os.times()[4] < deadline:
|
||||
sys.stderr.write('[Exited early, hit enter to continue]')
|
||||
sys.stdin.readline()
|
||||
if errcode == 0:
|
||||
errcode = exitcode
|
||||
sys.exit(errcode)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
import optparse
|
||||
import signal
|
||||
import sys
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
import sys
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
|
||||
def partitionhostsline(line):
|
||||
comment = ''
|
||||
if '#' in line:
|
||||
cmdidx = line.index('#')
|
||||
comment = line[cmdidx:]
|
||||
line = line[:cmdidx].strip()
|
||||
if not line.strip():
|
||||
return '', [], comment
|
||||
ipaddr, names = line.split(None, 1)
|
||||
names = names.split()
|
||||
return ipaddr, names, comment
|
||||
|
||||
class HostMerger(object):
|
||||
def __init__(self):
|
||||
self.byip = {}
|
||||
self.byname = {}
|
||||
self.byname6 = {}
|
||||
self.sourcelines = []
|
||||
self.targlines = []
|
||||
|
||||
def read_source(self, sourcefile):
|
||||
with open(sourcefile, 'r') as hfile:
|
||||
self.sourcelines = hfile.read().split('\n')
|
||||
while not self.sourcelines[-1]:
|
||||
self.sourcelines = self.sourcelines[:-1]
|
||||
for x in range(len(self.sourcelines)):
|
||||
line = self.sourcelines[x]
|
||||
currip, names, comment = partitionhostsline(line)
|
||||
if currip:
|
||||
self.byip[currip] = x
|
||||
byname = self.byname
|
||||
if ':' in currip:
|
||||
byname = self.byname6
|
||||
for name in names:
|
||||
byname[name] = x
|
||||
|
||||
def add_entry(self, ip, names):
|
||||
targ = self.byname
|
||||
if ':' in ip:
|
||||
targ = self.byname6
|
||||
line = '{:<39} {}'.format(ip, names)
|
||||
x = len(self.sourcelines)
|
||||
self.sourcelines.append(line)
|
||||
for name in names.split():
|
||||
if not name:
|
||||
continue
|
||||
targ[name] = x
|
||||
self.byip[ip] = x
|
||||
|
||||
|
||||
def read_target(self, targetfile):
|
||||
if not os.path.exists(targetfile):
|
||||
return
|
||||
with open(targetfile, 'r') as hfile:
|
||||
lines = hfile.read().split('\n')
|
||||
while lines and not lines[-1]:
|
||||
lines = lines[:-1]
|
||||
for y in range(len(lines)):
|
||||
line = lines[y]
|
||||
currip, names, comment = partitionhostsline(line)
|
||||
byname = self.byname
|
||||
if ':' in currip:
|
||||
byname = self.byname6
|
||||
if currip in self.byip:
|
||||
x = self.byip[currip]
|
||||
if self.sourcelines[x] is None:
|
||||
# have already consumed this entry
|
||||
continue
|
||||
self.targlines.append(self.sourcelines[x])
|
||||
self.sourcelines[x] = None
|
||||
continue
|
||||
for name in names:
|
||||
if name in byname:
|
||||
x = byname[name]
|
||||
if self.sourcelines[x] is None:
|
||||
break
|
||||
self.targlines.append(self.sourcelines[x])
|
||||
self.sourcelines[x] = None
|
||||
break
|
||||
else:
|
||||
self.targlines.append(line)
|
||||
|
||||
def write_out(self, targetfile):
|
||||
while self.targlines and not self.targlines[-1]:
|
||||
self.targlines = self.targlines[:-1]
|
||||
while self.sourcelines and not self.sourcelines[-1]:
|
||||
self.sourcelines = self.sourcelines[:-1]
|
||||
if not self.sourcelines:
|
||||
break
|
||||
with open(targetfile, 'w') as hosts:
|
||||
for line in self.targlines:
|
||||
hosts.write(line + '\n')
|
||||
for line in self.sourcelines:
|
||||
if line is not None:
|
||||
hosts.write(line + '\n')
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="Create/amend /etc/hosts file for given noderange")
|
||||
ap.add_argument('noderange', help='Noderange to generate/update /etc/hosts for')
|
||||
ap.add_argument('-a', '--attrib', help='Pull ip addresses and hostnames from attribute database', action='store_true')
|
||||
ap.add_argument('-i', '--ip', help='Expression to generate addresses (e.g. 172.16.1.{n1} or fd2b:246f:8a50::{n1:x})')
|
||||
ap.add_argument('-n', '--name', help='Expression for name to add ({node}-compute, etc). If unspecified, "{node} {node}.{dns.domain}" will be used', action='append')
|
||||
args = ap.parse_args()
|
||||
c = client.Command()
|
||||
if args.name:
|
||||
names = ' '.join(args.name)
|
||||
else:
|
||||
names = '{node} {node}.{dns.domain}'
|
||||
merger = HostMerger()
|
||||
if not args.ip and not args.attrib:
|
||||
sys.stderr.write('-a or -i is currently required\n')
|
||||
sys.exit(1)
|
||||
if args.attrib:
|
||||
ip4bynode = {}
|
||||
ip6bynode = {}
|
||||
namesbynode = {}
|
||||
domainsbynode = {}
|
||||
for ent in c.read('/noderange/{0}/attributes/current'.format(args.noderange)):
|
||||
ent = ent.get('databynode', {})
|
||||
for node in ent:
|
||||
for attrib in ent[node]:
|
||||
val = ent[node][attrib]
|
||||
if not isinstance(val, dict):
|
||||
continue
|
||||
val = val.get('value', None)
|
||||
if attrib == 'dns.domain':
|
||||
domainsbynode[node] = val
|
||||
if attrib.startswith('net.'):
|
||||
nameparts = attrib.split('.')
|
||||
if len(nameparts) == 2:
|
||||
currnet = None
|
||||
else:
|
||||
currnet = '.'.join(nameparts[1:-1])
|
||||
for currdict in (ip4bynode, ip6bynode, namesbynode):
|
||||
if node not in currdict:
|
||||
currdict[node] = {}
|
||||
if attrib.endswith('.ipv4_address') and val:
|
||||
ip4bynode[node][currnet] = val.split('/', 1)[0]
|
||||
elif attrib.endswith('.ipv6_address') and val:
|
||||
ip6bynode[node][currnet] = val.split('/', 1)[0]
|
||||
elif attrib.endswith('.hostname'):
|
||||
namesbynode[node][currnet] = re.split(r'\s+|,', val)
|
||||
for node in ip4bynode:
|
||||
mydomain = domainsbynode.get(node, None)
|
||||
for ipdb in (ip4bynode, ip6bynode):
|
||||
for currnet in ipdb[node]:
|
||||
if args.name:
|
||||
sys.stderr.write('Custom name and attribute addresses not currently supported together\n')
|
||||
sys.exit(1)
|
||||
else:
|
||||
names = list(namesbynode.get(node, {}).get(currnet, [node]))
|
||||
if mydomain:
|
||||
for name in names:
|
||||
if mydomain in names:
|
||||
break
|
||||
else:
|
||||
for name in list(names):
|
||||
names.append('{0}.{1}'.format(name, mydomain))
|
||||
names = ' '.join(names)
|
||||
merger.add_entry(ipdb[node][currnet], names)
|
||||
merger.write_out('/etc/whatnowhosts')
|
||||
else:
|
||||
namesbynode = {}
|
||||
ipbynode = {}
|
||||
expurl = '/noderange/{0}/attributes/expression'.format(args.noderange)
|
||||
expression = names
|
||||
exitcode = 0
|
||||
exitcode |= expand_expression(c, namesbynode, expurl, names)
|
||||
exitcode |= expand_expression(c, ipbynode, expurl, args.ip)
|
||||
if exitcode:
|
||||
sys.exit(exitcode)
|
||||
for node in ipbynode:
|
||||
merger.add_entry(ipbynode[node], namesbynode[node])
|
||||
if os.path.exists('/etc/hosts'):
|
||||
merger.read_target('/etc/hosts')
|
||||
os.rename('/etc/hosts', '/etc/hosts.confluentbkup')
|
||||
merger.write_out('/etc/hosts')
|
||||
|
||||
|
||||
|
||||
def expand_expression(c, namesbynode, expurl, expression):
|
||||
exitcode = 0
|
||||
for exp in c.create(expurl, {'expression': expression}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
namesbynode[node] = ex[node]['value']
|
||||
return exitcode
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
import csv
|
||||
import optparse
|
||||
import signal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
import csv
|
||||
import optparse
|
||||
import signal
|
||||
@@ -26,7 +26,7 @@ def lookupdata(data, key):
|
||||
|
||||
def main():
|
||||
argparser = optparse.OptionParser(
|
||||
usage='''\n %prog noderange -o xcatnodes.def
|
||||
usage='''\n %prog noderange -o ansible.hosts
|
||||
\n ''')
|
||||
argparser.add_option('-o', '--output',
|
||||
help='xCAT stanza file')
|
||||
@@ -79,8 +79,6 @@ def collect_attribute_data(databynode, node):
|
||||
xcatattrs['groups'] = ','.join(data.get('groups', []))
|
||||
xcatattrs['bmc'] = data.get('hardwaremanagement.manager', {}).get(
|
||||
'value', None)
|
||||
if xcatattrs['bmc']:
|
||||
xcatattrs['bmc'] = xcatattrs['bmc'].split('/', 1)[0]
|
||||
xcatattrs['mpa'] = data.get('enclosure.manager', {}).get(
|
||||
'value', None)
|
||||
xcatattrs['slotid'] = data.get('enclosure.bay', {}).get(
|
||||
|
||||
@@ -1,61 +1,19 @@
|
||||
#!/usr/bin/python
|
||||
#!/bin/sh
|
||||
# This will take a given directory and make a 'big floppy image'
|
||||
# out of it, suitable for nodemedia upload.
|
||||
|
||||
import fcntl
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def create_image(directory, image, label=None):
|
||||
ents = 0
|
||||
datasz = 512
|
||||
for dir in os.walk(sys.argv[1]):
|
||||
ents += 1
|
||||
for filen in dir[2]:
|
||||
ents += 1
|
||||
filename = os.path.join(dir[0], filen)
|
||||
currsz = os.path.getsize(filename)
|
||||
# assuming up to 65k cluster
|
||||
currsz = (currsz // 512 +1) * 512
|
||||
datasz += currsz
|
||||
datasz += ents * 32768
|
||||
datasz = datasz // 16384 + 1
|
||||
with open(image, 'wb') as imgfile:
|
||||
imgfile.seek(datasz * 16384 - 1)
|
||||
imgfile.write(b'\x00')
|
||||
if label:
|
||||
subprocess.check_call(['mformat', '-i', image, '-v', label,
|
||||
'-r', '16', '-d', '1', '-t', str(datasz),
|
||||
'-s', '16','-h', '2', '::'])
|
||||
else:
|
||||
subprocess.check_call(['mformat', '-i', image, '-r', '16', '-d', '1', '-t',
|
||||
str(datasz), '-s', '16','-h', '2', '::'])
|
||||
# Some clustered filesystems will have the lock from mformat
|
||||
# linger after close (mformat doesn't unlock)
|
||||
# do a blocking wait for shared lock and then explicitly
|
||||
# unlock between calls to mtools
|
||||
with open(image, 'rb') as imgfile:
|
||||
fcntl.flock(imgfile.fileno(), fcntl.LOCK_SH)
|
||||
fcntl.flock(imgfile.fileno(), fcntl.LOCK_UN)
|
||||
cpycmd = ['mcopy', '-i', image, '-s']
|
||||
cpycmd.extend(glob.glob('{0}/*'.format(directory)))
|
||||
cpycmd.append('::')
|
||||
subprocess.check_call(cpycmd)
|
||||
# though not necessary for us, make sure dir2img doesn't have a lingering
|
||||
# flock from mcopy for any subsequent commands
|
||||
with open(image, 'rb') as imgfile:
|
||||
fcntl.flock(imgfile.fileno(), fcntl.LOCK_SH)
|
||||
fcntl.flock(imgfile.fileno(), fcntl.LOCK_UN)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
sys.stderr.write("Usage: {0} <directory> <imagefile>".format(
|
||||
sys.argv[0]))
|
||||
sys.exit(1)
|
||||
label = None
|
||||
if len(sys.argv) > 3:
|
||||
label = sys.argv[3]
|
||||
create_image(sys.argv[1], sys.argv[2], label)
|
||||
if [ -z "$1" -o -z "$2" ]; then
|
||||
echo "Usage: $0 <directory> <imagefile>"
|
||||
exit 1
|
||||
fi
|
||||
# Get the needed payload side
|
||||
SIZE=$(du -sB 512 $1|awk '{print $1}')
|
||||
ENTRIES=$(find $1 |wc -l)
|
||||
# Also, each file and directory has overhead, pad size by 32kb per file,
|
||||
# which should be overkill but other values proved to be inadequate
|
||||
# A deeper understanding of VFAT would probably allow for more precise value
|
||||
SIZE=$((SIZE + ENTRIES * 64))
|
||||
dd if=/dev/zero of=$2 bs=512 count=$SIZE
|
||||
# Make a big single sided floppy with many many tracks, saves on complex math
|
||||
mformat -i $2 -d 1 -t $SIZE -s 1 -h 1 ::
|
||||
mcopy -i $2 -s $1/* ::
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 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.
|
||||
|
||||
from collections import deque
|
||||
import optparse
|
||||
import os
|
||||
import select
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
devnull = None
|
||||
|
||||
def run():
|
||||
global devnull
|
||||
devnull = open(os.devnull, 'rb')
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] noderange commandexpression",
|
||||
epilog="Expressions are the same as in attributes, e.g. "
|
||||
"'ipmitool -H {hardwaremanagement.manager}' will be expanded.")
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
help='Number of commands to run at a time')
|
||||
argparser.add_option('-k', '--security', action='store_true',
|
||||
help='Update SSH setup')
|
||||
argparser.add_option('-F', '--sync', action='store_true',
|
||||
help='Run the syncfiles associated with the currently completed OS profile on the noderange')
|
||||
argparser.add_option('-P', '--scripts',
|
||||
help='Re-run specified scripts, with full path under scripts, e.g. post.d/first,firstboot.d/second')
|
||||
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()
|
||||
(options, args) = argparser.parse_args()
|
||||
if len(args) < 1:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
client.check_globbing(args[0])
|
||||
concurrentprocs = options.count
|
||||
c = client.Command()
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
|
||||
c.stop_if_noderange_over(args[0], options.maxnodes)
|
||||
nodemap = {}
|
||||
cmdparms = []
|
||||
nodes = []
|
||||
for res in c.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode |= res.get('errorcode', 1)
|
||||
break
|
||||
node = res['item']['href'][:-1]
|
||||
nodes.append(node)
|
||||
|
||||
cmdstorun = []
|
||||
if options.security:
|
||||
cmdstorun.append(['run_remote', 'setupssh'])
|
||||
if options.sync:
|
||||
cmdstorun.append(['run_remote_python', 'syncfileclient'])
|
||||
if options.scripts:
|
||||
for script in options.scripts.split(','):
|
||||
cmdstorun.append(['run_remote', script])
|
||||
if not cmdstorun:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
idxbynode = {}
|
||||
cmdvbase = ['bash', '/etc/confluent/functions']
|
||||
for sshnode in nodes:
|
||||
idxbynode[sshnode] = 1
|
||||
cmdv = ['ssh', sshnode] + cmdvbase + cmdstorun[0]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(sshnode, cmdv, all, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((sshnode, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
desc = pipedesc[r]
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
data = r.readline()
|
||||
if data:
|
||||
if desc['type'] == 'stdout':
|
||||
if node not in pernodeout:
|
||||
pernodeout[node] = []
|
||||
pernodeout[node].append(data)
|
||||
else:
|
||||
data = client.stringify(data)
|
||||
sys.stderr.write('{0}: {1}'.format(node, data))
|
||||
sys.stderr.flush()
|
||||
else:
|
||||
pop = desc['popen']
|
||||
ret = pop.poll()
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout':
|
||||
if idxbynode[node] < len(cmdstorun):
|
||||
cmdv = ['ssh', sshnode] + cmdvbase + cmdstorun[idxbynode[node]]
|
||||
idxbynode[node] += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
elif pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
line = line.lstrip('\x08')
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
all.add(nopen.stdout)
|
||||
all.add(nopen.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -22,7 +22,6 @@ import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import shlex
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
@@ -51,11 +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')
|
||||
argparser.add_option('-s', '--set', dest='set', metavar='settings.batch',
|
||||
default=False, help='set attributes using a batch file')
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
|
||||
@@ -93,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
|
||||
@@ -108,32 +101,11 @@ if len(args) > 1:
|
||||
requestargs=args[1:]
|
||||
except:
|
||||
pass
|
||||
elif options.clear or options.environment or options.prompt:
|
||||
sys.stderr.write('Attribute names required with specified options\n')
|
||||
argparser.print_help()
|
||||
exitcode = 400
|
||||
|
||||
elif options.set:
|
||||
arglist = [noderange]
|
||||
showtype='current'
|
||||
argfile = open(options.set, 'r')
|
||||
argset = argfile.readline()
|
||||
while argset:
|
||||
try:
|
||||
argset = argset[:argset.index('#')]
|
||||
except ValueError:
|
||||
pass
|
||||
argset = argset.strip()
|
||||
if argset:
|
||||
arglist += shlex.split(argset)
|
||||
argset = argfile.readline()
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
exitcode=client.updateattrib(session,arglist,nodetype, noderange, options, None)
|
||||
if exitcode != 0:
|
||||
sys.exit(exitcode)
|
||||
|
||||
# Lists all attributes
|
||||
|
||||
if len(args) > 0:
|
||||
# setting output to all so it can search since if we do have something to search, we want to show all outputs even if it is blank.
|
||||
if requestargs is None:
|
||||
@@ -154,6 +126,6 @@ else:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
print(res['item']['href'].replace('/', ''))
|
||||
print res['item']['href'].replace('/', '')
|
||||
|
||||
sys.exit(exitcode)
|
||||
sys.exit(exitcode)
|
||||
@@ -1,121 +0,0 @@
|
||||
#!/usr/libexec/platform-python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 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.
|
||||
|
||||
__author__ = 'tkucherera'
|
||||
|
||||
from getpass import getpass
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange> <username> <new_password>")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Number of nodes to affect before prompting for confirmation')
|
||||
argparser.add_option('-p', '--prompt', action='store_true',
|
||||
help='Prompt for password values interactively')
|
||||
argparser.add_option('-e', '--environment', action='store_true',
|
||||
help='Set passwod, but from environment variable of '
|
||||
'same name')
|
||||
|
||||
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
|
||||
try:
|
||||
noderange = args[0]
|
||||
username = args[1]
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
client.check_globbing(noderange)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
|
||||
if options.prompt:
|
||||
oneval = 1
|
||||
twoval = 2
|
||||
while oneval != twoval:
|
||||
oneval = getpass('Enter pass for {0}: '.format(username))
|
||||
twoval = getpass('Confirm pass for {0}: '.format(username))
|
||||
if oneval != twoval:
|
||||
print('Values did not match.')
|
||||
new_password = twoval
|
||||
|
||||
elif len(args) == 3:
|
||||
if options.environment:
|
||||
key = args[2]
|
||||
new_password = os.environ.get(key, os.environ[key.upper()])
|
||||
else:
|
||||
new_password = args[2]
|
||||
else:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
errorNodes = set([])
|
||||
uid_dict = {}
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
|
||||
for rsp in session.read('/noderange/{0}/configuration/management_controller/users/all'.format(noderange)):
|
||||
databynode = rsp["databynode"]
|
||||
for node in databynode:
|
||||
if 'error' in rsp['databynode'][node]:
|
||||
print(node, ':', rsp['databynode'][node]['error'])
|
||||
errorNodes.add(node)
|
||||
continue
|
||||
for user in rsp['databynode'][node]['users']:
|
||||
if user['username'] == username:
|
||||
if not user['uid'] in uid_dict:
|
||||
uid_dict[user['uid']] = node
|
||||
continue
|
||||
uid_dict[user['uid']] = uid_dict[user['uid']] + ',{}'.format(node)
|
||||
break
|
||||
|
||||
if not uid_dict:
|
||||
print("Error: Could not reach target node's bmc user")
|
||||
sys.exit(1)
|
||||
|
||||
for uid in uid_dict:
|
||||
success = session.simple_noderange_command(uid_dict[uid], 'configuration/management_controller/users/{0}'.format(uid), new_password, key='password', errnodes=errorNodes) # = 0 if successful
|
||||
|
||||
allNodes = set([])
|
||||
|
||||
for node in session.read('/noderange/{0}/nodes/'.format(noderange)):
|
||||
if 'error' in node and success != 0:
|
||||
sys.exit(success)
|
||||
allNodes.add(node['item']['href'].replace("/", ""))
|
||||
|
||||
goodNodes = allNodes - errorNodes
|
||||
|
||||
for node in goodNodes:
|
||||
print(node + ": Password Change Successful")
|
||||
|
||||
|
||||
sys.exit(success)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -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
|
||||
@@ -53,14 +51,12 @@ success = session.simple_noderange_command(noderange, 'configuration/management_
|
||||
allNodes = set([])
|
||||
|
||||
for node in session.read('/noderange/{0}/nodes/'.format(noderange)):
|
||||
if 'error' in node and success != 0:
|
||||
sys.exit(success)
|
||||
allNodes.add(node['item']['href'].replace("/", ""))
|
||||
|
||||
goodNodes = allNodes - errorNodes
|
||||
|
||||
for node in goodNodes:
|
||||
print(node + ": BMC Reset Successful")
|
||||
print node + ": BMC Reset Successful"
|
||||
|
||||
|
||||
sys.exit(success)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015 Lenovo
|
||||
@@ -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()
|
||||
|
||||
@@ -58,8 +54,8 @@ except IndexError:
|
||||
sys.exit(1)
|
||||
client.check_globbing(noderange)
|
||||
bootdev = None
|
||||
if len(args) > 1:
|
||||
bootdev = args[1]
|
||||
if len(sys.argv) > 2:
|
||||
bootdev = sys.argv[2]
|
||||
if bootdev in ('net', 'pxe'):
|
||||
bootdev = 'network'
|
||||
session = client.Command()
|
||||
@@ -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,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -43,7 +43,7 @@ def bailout(msg, code=1):
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog [options] <noderange> [setting|setting=value]")
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog [options] noderange [option|option=value]")
|
||||
argparser.add_option('-c', '--comparedefault', dest='comparedefault',
|
||||
action='store_true', default=False,
|
||||
help='Compare given settings to default or list settings '
|
||||
@@ -54,12 +54,6 @@ argparser.add_option('-d', '--detail', dest='detail',
|
||||
action='store_true', default=False,
|
||||
help='Provide verbose information as available, such as '
|
||||
'help text and possible valid values')
|
||||
argparser.add_option('-e', '--extra', dest='extra',
|
||||
action='store_true', default=False,
|
||||
help='Access extra configuration. Extra configuration is generally '
|
||||
'reserved for unpopular or redundant options that may be slow to '
|
||||
'read. Notably the IMM category on Lenovo settings is considered '
|
||||
'to be extra configuration')
|
||||
argparser.add_option('-x', '--exclude', dest='exclude',
|
||||
action='store_true', default=False,
|
||||
help='Treat positional arguments as items to not '
|
||||
@@ -74,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 = {
|
||||
@@ -90,12 +80,6 @@ cfgpaths = {
|
||||
'bmc.ipv4_gateway': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'ipv4_gateway'),
|
||||
'bmc.static_ipv6_addresses': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'static_v6_addresses'),
|
||||
'bmc.static_ipv6_gateway': (
|
||||
'configuration/management_controller/net_interfaces/management',
|
||||
'static_v6_gateway'),
|
||||
'bmc.hostname': (
|
||||
'configuration/management_controller/hostname', 'hostname'),
|
||||
}
|
||||
@@ -114,9 +98,6 @@ setmode = None
|
||||
assignment = {}
|
||||
queryparms = {}
|
||||
printsys = []
|
||||
printbmc = []
|
||||
printextbmc = []
|
||||
printallbmc = False
|
||||
setsys = {}
|
||||
forceset = False
|
||||
needval = None
|
||||
@@ -140,10 +121,9 @@ def _assign_value():
|
||||
assignment[key] = value
|
||||
|
||||
|
||||
def parse_config_line(arguments, single=False):
|
||||
global setmode, printallbmc, forceset, key, value, needval, candidate, path, attrib
|
||||
for pidx in range(0, len(arguments)):
|
||||
param = arguments[pidx]
|
||||
def parse_config_line(arguments):
|
||||
global setmode, forceset, key, value, needval, candidate, path, attrib
|
||||
for param in arguments:
|
||||
if param == 'show':
|
||||
continue # forgive muscle memory of pasu users
|
||||
if param == 'set':
|
||||
@@ -153,19 +133,14 @@ def parse_config_line(arguments, single=False):
|
||||
if needval:
|
||||
key = needval
|
||||
needval = None
|
||||
if single:
|
||||
value = ' '.join(arguments[pidx:])
|
||||
_assign_value()
|
||||
break
|
||||
else:
|
||||
value = param
|
||||
value = param
|
||||
_assign_value()
|
||||
continue
|
||||
if '=' in param or param[-1] == ':' or forceset:
|
||||
if setmode is None:
|
||||
setmode = True
|
||||
if setmode != True:
|
||||
bailout('Cannot do set and query in same command: Query detected but "{0}" appears to be set'.format(param))
|
||||
bailout('Cannot do set and query in same command')
|
||||
if '=' in param:
|
||||
key, _, value = param.partition('=')
|
||||
_assign_value()
|
||||
@@ -177,10 +152,8 @@ def parse_config_line(arguments, single=False):
|
||||
if setmode is None:
|
||||
setmode = False
|
||||
if setmode != False:
|
||||
bailout('Cannot do set and query in same command: Set mode detected but "{0}" appears to be a query'.format(param))
|
||||
bailout('Cannot do set and query in same command')
|
||||
if '.' not in param:
|
||||
if param == 'bmc':
|
||||
printallbmc = True
|
||||
matchedparms = False
|
||||
for candidate in cfgpaths:
|
||||
if candidate.startswith('{0}.'.format(param)):
|
||||
@@ -196,19 +169,10 @@ def parse_config_line(arguments, single=False):
|
||||
del queryparms[path]
|
||||
except KeyError:
|
||||
pass
|
||||
if param.lower() == 'imm':
|
||||
printextbmc.append(param)
|
||||
options.extra = True
|
||||
elif not matchedparms:
|
||||
if not matchedparms:
|
||||
printsys.append(param)
|
||||
elif param not in cfgpaths:
|
||||
if param.startswith('bmc.'):
|
||||
printbmc.append(param.replace('bmc.', ''))
|
||||
elif param.lower().startswith('imm'):
|
||||
options.extra = True
|
||||
printextbmc.append(param)
|
||||
else:
|
||||
printsys.append(param)
|
||||
printsys.append(param)
|
||||
else:
|
||||
path, attrib = cfgpaths[param]
|
||||
path = '/noderange/{0}/{1}'.format(noderange, path)
|
||||
@@ -227,35 +191,25 @@ if options.batch:
|
||||
pass
|
||||
argset = argset.strip()
|
||||
if argset:
|
||||
parse_config_line(shlex.split(argset), single=True)
|
||||
parse_config_line(shlex.split(argset))
|
||||
argset = argfile.readline()
|
||||
else:
|
||||
parse_config_line(args[1:])
|
||||
session = client.Command()
|
||||
rcode = 0
|
||||
if options.restoredefault:
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
if options.restoredefault.lower() in (
|
||||
if options.restoredefault and options.restoredefault.lower() in (
|
||||
'sys', 'system', 'uefi', 'bios'):
|
||||
for fr in session.update(
|
||||
'/noderange/{0}/configuration/system/clear'.format(noderange),
|
||||
{'clear': True}):
|
||||
rcode |= client.printerror(fr)
|
||||
sys.exit(rcode)
|
||||
elif options.restoredefault.lower() in (
|
||||
'bmc', 'imm', 'xcc'):
|
||||
for fr in session.update(
|
||||
'/noderange/{0}/configuration/management_controller/clear'.format(noderange),
|
||||
{'clear': True}):
|
||||
rcode |= client.printerror(fr)
|
||||
sys.exit(rcode)
|
||||
else:
|
||||
sys.stderr.write(
|
||||
'Unrecognized component to restore defaults: {0}\n'.format(
|
||||
options.restoredefault))
|
||||
sys.exit(1)
|
||||
for fr in session.update(
|
||||
'/noderange/{0}/configuration/system/clear'.format(noderange),
|
||||
{'clear': True}):
|
||||
rcode |= client.printerror(fr)
|
||||
sys.exit(rcode)
|
||||
elif options.restoredefault:
|
||||
sys.stderr.write(
|
||||
'Unrecognized component to restore defaults: {0}\n'.format(
|
||||
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)
|
||||
@@ -263,12 +217,8 @@ if setmode:
|
||||
attrnamebypath = {}
|
||||
for key in assignment:
|
||||
if key not in cfgpaths:
|
||||
if key.startswith('bmc.'):
|
||||
path = 'configuration/management_controller/extended/all'
|
||||
attrib = key.replace('bmc.', '')
|
||||
else:
|
||||
path = 'configuration/system/all'
|
||||
attrib = key
|
||||
path = 'configuration/system/all'
|
||||
attrib = key
|
||||
else:
|
||||
path, attrib = cfgpaths[key]
|
||||
if path not in updatebypath:
|
||||
@@ -284,6 +234,7 @@ if setmode:
|
||||
rcode |= client.printerror(fr)
|
||||
for node in fr.get('databynode', []):
|
||||
r = fr['databynode'][node]
|
||||
rcode |= client.printerror(r, node)
|
||||
if 'value' not in r:
|
||||
continue
|
||||
keyval = r['value']
|
||||
@@ -295,22 +246,10 @@ else:
|
||||
for path in queryparms:
|
||||
if options.comparedefault:
|
||||
continue
|
||||
rcode |= client.print_attrib_path(path, session, list(queryparms[path]),
|
||||
rc = client.print_attrib_path(path, session, list(queryparms[path]),
|
||||
NullOpt(), queryparms[path])
|
||||
if printsys == 'all' or printextbmc or printbmc or printallbmc:
|
||||
if printbmc or not printextbmc:
|
||||
rcode |= client.print_attrib_path(
|
||||
'/noderange/{0}/configuration/management_controller/extended/all'.format(noderange),
|
||||
session, printbmc, options, attrprefix='bmc.')
|
||||
if options.extra:
|
||||
if options.advanced:
|
||||
rcode |= client.print_attrib_path(
|
||||
'/noderange/{0}/configuration/management_controller/extended/extra_advanced'.format(noderange),
|
||||
session, printextbmc, options)
|
||||
else:
|
||||
rcode |= client.print_attrib_path(
|
||||
'/noderange/{0}/configuration/management_controller/extended/extra'.format(noderange),
|
||||
session, printextbmc, options)
|
||||
if rc:
|
||||
sys.exit(rc)
|
||||
if printsys or options.exclude:
|
||||
if printsys == 'all':
|
||||
printsys = []
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/libexec/platform-python
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015 Lenovo
|
||||
@@ -23,268 +23,22 @@ path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
import confluent.logreader as logreader
|
||||
import time
|
||||
import socket
|
||||
import re
|
||||
|
||||
confettypath = os.path.join(os.path.dirname(sys.argv[0]), 'confetty')
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] <noderange> [kill][-- [passthroughoptions]]",
|
||||
usage="Usage: %prog [options] node",
|
||||
epilog="Command sequences are available while connected to a console, hit "
|
||||
"ctrl-'e', then release ctrl, then 'c', then '?' for a full list. "
|
||||
"For example, ctrl-'e', then 'c', then '.' will exit the current "
|
||||
"console")
|
||||
argparser.add_option('-t', '--tile', action='store_true', default=False,
|
||||
help='Tile console windows in the terminal')
|
||||
argparser.add_option('-l', '--log', action='store_true', default=False,
|
||||
help='Enter log replay mode instead of showing a live console')
|
||||
|
||||
argparser.add_option('-T', '--Timestamp', action='store_true', default=False,
|
||||
help= 'Dump log in stdout with timestamps')
|
||||
|
||||
argparser.add_option('-w','--windowed', action='store_true', default=False,
|
||||
help='Open terminal windows for each node. The '
|
||||
'environment variable NODECONSOLE_WINDOWED_COMMAND '
|
||||
'should be set, which should be a text string corresponding '
|
||||
'to a command that can be used to open a windowed console,'
|
||||
' omitting the "nodeconsole <noderange>" part of the '
|
||||
'command, for example, to open a set of consoles for a '
|
||||
'range of nodes in separate xterm windows, set '
|
||||
'NODECONSOLE_WINDOWED_COMMAND to "xterm -e". To open a '
|
||||
'set of consoles for a range of nodes in separate '
|
||||
'GNOME Terminal windows with a size of 100 columns and '
|
||||
'31 rows, set NODECONSOLE_WINDOWED_COMMAND '
|
||||
'to "gnome-terminal --geometry 100x31 --" or in a WSL '
|
||||
'environment, to open a set of consoles for a range of '
|
||||
'nodes in separate Windows Terminal windows, with the '
|
||||
'title set for each node, set NODECONSOLE_WINDOWED_COMMAND'
|
||||
' to "wt.exe wsl.exe -d AlmaLinux-8 '
|
||||
'--shell-type login". If the NODECONSOLE_WINDOWED_COMMAND '
|
||||
'environment variable isn\'t set, xterm will be used by'
|
||||
'default.')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
pass_through_args = []
|
||||
killcon = False
|
||||
try:
|
||||
noderange = args[0]
|
||||
if len(args) > 1:
|
||||
if args[1] == 'kill':
|
||||
killcon = True
|
||||
pass_through_args = args[1:]
|
||||
args = args[:1]
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
if len(args) != 1:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
if options.log:
|
||||
logname = args[0]
|
||||
if not os.path.exists(logname) and logname[0] != '/':
|
||||
logname = os.path.join('/var/log/confluent/consoles', logname)
|
||||
if not os.path.exists(logname):
|
||||
sys.stderr.write('Unable to locate {0} on local system\n'.format(logname))
|
||||
sys.exit(1)
|
||||
logreader.replay_to_console(logname)
|
||||
sys.exit(0)
|
||||
|
||||
if options.Timestamp:
|
||||
logname = args[0]
|
||||
if not os.path.exists(logname) and logname[0] != '/':
|
||||
logname = os.path.join('/var/log/confluent/consoles', logname)
|
||||
if not os.path.exists(logname):
|
||||
sys.stderr.write('Unable to locate {0} on local system\n'.format(logname))
|
||||
sys.exit(1)
|
||||
logreader.dump_to_console(logname)
|
||||
sys.exit(0)
|
||||
|
||||
def kill(noderange):
|
||||
sess = client.Command()
|
||||
envstring=os.environ.get('NODECONSOLE_WINDOWED_COMMAND')
|
||||
if not envstring:
|
||||
envstring = 'xterm'
|
||||
|
||||
nodes = []
|
||||
for res in sess.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
node = res.get('item', {}).get('href', '/').replace('/', '')
|
||||
if not node:
|
||||
sys.stderr.write(res.get('error', repr(res)) + '\n')
|
||||
sys.exit(1)
|
||||
nodes.append(node)
|
||||
|
||||
for node in nodes:
|
||||
s=socket.socket(socket.AF_UNIX)
|
||||
winid=None
|
||||
try:
|
||||
win=subprocess.Popen(['xwininfo', '-tree', '-root'], stdout=subprocess.PIPE)
|
||||
wintr=win.communicate()[0]
|
||||
for line in wintr.decode('utf-8').split('\n'):
|
||||
if 'console: {0}'.format(node) in line or 'confetty' in line:
|
||||
win_obj = [ele for ele in line.split(' ') if ele.strip()]
|
||||
winid = win_obj[0]
|
||||
except:
|
||||
print("Error: cannot retrieve window id of node {}".format(node))
|
||||
|
||||
if winid:
|
||||
ps_data=subprocess.Popen(['xkill', '-id', winid ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
def handle_geometry(envlist, sizegeometry, side_pad=0, top_pad=0, first=False):
|
||||
if '-geometry' in envlist:
|
||||
g_index = envlist.index('-geometry')
|
||||
elif '-g' in envlist:
|
||||
g_index = envlist.index('-g')
|
||||
else:
|
||||
g_index = 0
|
||||
if g_index:
|
||||
if first:
|
||||
envlist[g_index+1] = '{0}+{1}+{2}'.format(envlist[g_index+1],side_pad, top_pad)
|
||||
else:
|
||||
envlist[g_index+1] = '{0}+{1}+{2}'.format(sizegeometry,side_pad, top_pad)
|
||||
else:
|
||||
envlist.insert(1, '-geometry')
|
||||
envlist.insert(2, '{0}+{1}+{2}'.format(sizegeometry,side_pad, top_pad))
|
||||
g_index = 1
|
||||
return envlist
|
||||
|
||||
# add funcltionality to close/kill all open consoles
|
||||
if killcon:
|
||||
kill(noderange)
|
||||
|
||||
#added functionality for wcons
|
||||
if options.windowed:
|
||||
result=subprocess.Popen(['xwininfo', '-root'], stdout=subprocess.PIPE)
|
||||
rootinfo=result.communicate()[0]
|
||||
result.wait()
|
||||
for line in rootinfo.decode('utf-8').split('\n'):
|
||||
if 'Width' in line:
|
||||
screenwidth = int(line.split(':')[1])
|
||||
if 'Height' in line:
|
||||
screenheight = int(line.split(':')[1])
|
||||
|
||||
envstring=os.environ.get('NODECONSOLE_WINDOWED_COMMAND')
|
||||
if not envstring:
|
||||
sizegeometry='100x31'
|
||||
corrected_x, corrected_y = (13,84)
|
||||
envlist = handle_geometry(['xterm'] + pass_through_args + ['-e'],sizegeometry, first=True)
|
||||
#envlist=['xterm', '-bg', 'black', '-fg', 'white', '-geometry', '{sizegeometry}+0+0'.format(sizegeometry=sizegeometry), '-e']
|
||||
else:
|
||||
envlist=os.environ.get('NODECONSOLE_WINDOWED_COMMAND').split(' ')
|
||||
if envlist[0] == 'xterm':
|
||||
if '-geometry' in envlist:
|
||||
g_index = envlist.index('-geometry')
|
||||
elif '-g' in envlist:
|
||||
g_index = envlist.index('-g')
|
||||
else:
|
||||
g_index = 0
|
||||
if g_index:
|
||||
envlist[g_index+1] = envlist[g_index+1] + '+0+0'
|
||||
|
||||
else:
|
||||
envlist.insert(1, '-geometry')
|
||||
envlist.insert(2, '100x31+0+0')
|
||||
g_index = 1
|
||||
|
||||
nodes = []
|
||||
sess = client.Command()
|
||||
for res in sess.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
node = res.get('item', {}).get('href', '/').replace('/', '')
|
||||
if not node:
|
||||
sys.stderr.write(res.get('error', repr(res)) + '\n')
|
||||
sys.exit(1)
|
||||
nodes.append(node)
|
||||
|
||||
if options.tile and not envlist[0] == 'xterm':
|
||||
sys.stderr.write('[ERROR] UNSUPPORTED OPTIONS. \nWindowed and tiled consoles are only supported when using xterm \n')
|
||||
sys.exit(1)
|
||||
firstnode=nodes[0]
|
||||
nodes.pop(0)
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
xopen=subprocess.Popen(envlist + [confettypath, '-c', '/tmp/controlpath-{0}'.format(firstnode), '-m', '5', 'start', '/nodes/{0}/console/session'.format(firstnode) ] , stdin=devnull)
|
||||
time.sleep(2)
|
||||
s=socket.socket(socket.AF_UNIX)
|
||||
winid=''
|
||||
try:
|
||||
s.connect('/tmp/controlpath-{firstnode}'.format(firstnode=firstnode))
|
||||
s.recv(64)
|
||||
s.send(b'GETWINID')
|
||||
winid=s.recv(64).decode('utf-8')
|
||||
|
||||
except:
|
||||
time.sleep(2)
|
||||
# try to get id of first panel/xterm window using name
|
||||
win=subprocess.Popen(['xwininfo', '-tree', '-root'], stdout=subprocess.PIPE)
|
||||
wintr=win.communicate()[0]
|
||||
for line in wintr.decode('utf-8').split('\n'):
|
||||
if 'console: {firstnode}'.format(firstnode=firstnode) in line or 'confetty' in line:
|
||||
win_obj = [ele for ele in line.split(' ') if ele.strip()]
|
||||
winid = win_obj[0]
|
||||
if winid:
|
||||
firstnode_window=subprocess.Popen(['xwininfo', '-id', '{winid}'.format(winid=winid)], stdout=subprocess.PIPE)
|
||||
xinfo=firstnode_window.communicate()[0]
|
||||
xinfl = xinfo.decode('utf-8').split('\n')
|
||||
for line in xinfl:
|
||||
if 'Absolute upper-left X:' in line:
|
||||
side_pad = int(line.split(':')[1])
|
||||
elif 'Absolute upper-left Y:' in line:
|
||||
top_pad = int(line.split(':')[1])
|
||||
|
||||
elif 'Width:' in line:
|
||||
window_width = int(line.split(':')[1])
|
||||
elif 'Height' in line:
|
||||
window_height = int(line.split(':')[1])
|
||||
elif '-geometry' in line:
|
||||
l = re.split(' |x|-|\\+', line)
|
||||
l_nosp = [ele for ele in l if ele.strip()]
|
||||
wmxo = int(l_nosp[1])
|
||||
wmyo = int(l_nosp[2])
|
||||
sizegeometry = str(wmxo) + 'x' + str(wmyo)
|
||||
else:
|
||||
pass
|
||||
|
||||
window_width += side_pad*2
|
||||
window_height += side_pad+top_pad
|
||||
screenwidth -= wmxo
|
||||
screenheight -= wmyo
|
||||
currx = window_width
|
||||
curry = 0
|
||||
maxcol = int(screenwidth/window_width)
|
||||
|
||||
for node in sortutil.natural_sort(nodes):
|
||||
if options.tile and envlist[0] == 'xterm':
|
||||
corrected_x = currx
|
||||
corrected_y = curry
|
||||
xgeometry = '{0}+{1}+{2}'.format(sizegeometry, corrected_x, corrected_y)
|
||||
currx += window_width
|
||||
if currx >= screenwidth:
|
||||
currx=0
|
||||
curry += window_height
|
||||
if curry > screenheight:
|
||||
curry =top_pad
|
||||
if not envstring:
|
||||
envlist= handle_geometry(envlist, sizegeometry, corrected_x, corrected_y)
|
||||
else:
|
||||
if g_index:
|
||||
envlist[g_index+1] = xgeometry
|
||||
elif envlist[0] == 'xterm':
|
||||
envlist=handle_geometry(envlist, sizegeometry, side_pad, top_pad)
|
||||
side_pad+=(side_pad+1)
|
||||
top_pad+=(top_pad+30)
|
||||
else:
|
||||
pass
|
||||
with open(os.devnull, 'wb') as devnull:
|
||||
xopen=subprocess.Popen(envlist + [confettypath, '-c', '/tmp/controlpath-{0}'.format(node), '-m', '5', 'start', '/nodes/{0}/console/session'.format(node)] , stdin=devnull)
|
||||
sys.exit(0)
|
||||
#end of wcons
|
||||
if options.tile:
|
||||
null = open('/dev/null', 'w')
|
||||
nodes = []
|
||||
@@ -296,35 +50,28 @@ if options.tile:
|
||||
sys.exit(1)
|
||||
nodes.append(node)
|
||||
initial = True
|
||||
in_tmux = False
|
||||
pane = 0
|
||||
sessname = 'nodeconsole_{0}'.format(os.getpid())
|
||||
if os.environ.get("TMUX"):
|
||||
initial = False
|
||||
in_tmux = True
|
||||
subprocess.call(['tmux', 'rename-session', sessname])
|
||||
for node in sortutil.natural_sort(nodes):
|
||||
panename = '{0}:{1}'.format(sessname, pane)
|
||||
if initial:
|
||||
initial = False
|
||||
subprocess.call(
|
||||
['tmux', 'new-session', '-d', '-s',
|
||||
sessname, '-x', '800', '-y',
|
||||
'nodeconsole_{0}'.format(os.getpid()), '-x', '800', '-y',
|
||||
'800', '{0} -m 5 start /nodes/{1}/console/session'.format(
|
||||
confettypath, node)])
|
||||
else:
|
||||
subprocess.call(['tmux', 'select-pane', '-t', sessname])
|
||||
subprocess.call(['tmux', 'set-option', '-t', panename, 'pane-border-status', 'top'], stderr=null)
|
||||
subprocess.call(['tmux', 'select-pane', '-t', str(pane)])
|
||||
subprocess.call(['tmux', 'set-option', 'pane-border-status', 'top'], stderr=null)
|
||||
pane += 1
|
||||
subprocess.call(
|
||||
['tmux', 'split', '-h', '-t', sessname,
|
||||
['tmux', 'split', '-h',
|
||||
'{0} -m 5 start /nodes/{1}/console/session'.format(
|
||||
confettypath, node)])
|
||||
subprocess.call(['tmux', 'select-layout', '-t', sessname, 'tiled'], stdout=null)
|
||||
pane += 1
|
||||
subprocess.call(['tmux', 'select-pane', '-t', sessname])
|
||||
subprocess.call(['tmux', 'set-option', '-t', panename, 'pane-border-status', 'top'], stderr=null)
|
||||
if not in_tmux:
|
||||
os.execlp('tmux', 'tmux', 'attach', '-t', sessname)
|
||||
subprocess.call(['tmux', 'select-layout', 'tiled'], stdout=null)
|
||||
subprocess.call(['tmux', 'select-pane', '-t', '0'])
|
||||
subprocess.call(['tmux', 'set-option', 'pane-border-status', 'top'], stderr=null)
|
||||
os.execlp('tmux', 'tmux', 'attach', '-t', 'nodeconsole_{0}'.format(
|
||||
os.getpid()))
|
||||
else:
|
||||
os.execl(confettypath, confettypath, 'start',
|
||||
'/nodes/{0}/console/session'.format(args[0]))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -55,4 +55,4 @@ for r in session.create('/noderange/', attribs):
|
||||
exitcode |= 1
|
||||
if 'created' in r:
|
||||
print('{0}: created'.format(r['created']))
|
||||
sys.exit(exitcode)
|
||||
sys.exit(exitcode)
|
||||
@@ -1,178 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
|
||||
def cleararm(nr, cli):
|
||||
nodes = set([])
|
||||
for rsp in cli.read('/noderange/{0}/attributes/current'.format(nr)):
|
||||
for node in rsp.get('databynode', {}):
|
||||
nodeinfo = rsp['databynode'][node]
|
||||
for attr in nodeinfo:
|
||||
if attr == 'deployment.apiarmed':
|
||||
curr = nodeinfo[attr].get('value', '')
|
||||
if curr == 'continuous':
|
||||
nodes.add(node)
|
||||
noderange = nr
|
||||
if nodes:
|
||||
noderange += ',-({0})'.format(','.join(nodes))
|
||||
for rsp in cli.update('/noderange/{0}/attributes/current'.format(noderange),
|
||||
{'deployment.apiarmed': ''}):
|
||||
pass
|
||||
|
||||
|
||||
def armonce(nr, cli):
|
||||
nodes = set([])
|
||||
for rsp in cli.read('/noderange/{0}/attributes/current'.format(nr)):
|
||||
for node in rsp.get('databynode', {}):
|
||||
nodeinfo = rsp['databynode'][node]
|
||||
for attr in nodeinfo:
|
||||
if attr == 'deployment.apiarmed':
|
||||
curr = nodeinfo[attr].get('value', '')
|
||||
if curr == 'continuous':
|
||||
nodes.add(node)
|
||||
noderange = nr
|
||||
if nodes:
|
||||
noderange += ',-({0})'.format(','.join(nodes))
|
||||
for rsp in cli.update('/noderange/{0}/attributes/current'.format(noderange),
|
||||
{'deployment.apiarmed': 'once'}):
|
||||
pass
|
||||
|
||||
|
||||
def setpending(nr, profile, cli):
|
||||
args = {'deployment.pendingprofile': profile, 'deployment.state': '', 'deployment.state_detail': ''}
|
||||
if not profile.startswith('genesis-'):
|
||||
args['deployment.stagedprofile'] = ''
|
||||
args['deployment.profile'] = ''
|
||||
for rsp in cli.update('/noderange/{0}/attributes/current'.format(nr),
|
||||
args):
|
||||
pass
|
||||
|
||||
|
||||
def clearpending(nr, cli):
|
||||
for rsp in cli.update('/noderange/{0}/attributes/current'.format(nr),
|
||||
{'deployment.pendingprofile': ''}):
|
||||
pass
|
||||
|
||||
def main(args):
|
||||
ap = argparse.ArgumentParser(description='Deploy OS to nodes')
|
||||
ap.add_argument('-c', '--clear', help='Clear any pending deployment action', action='store_true')
|
||||
ap.add_argument('-n', '--network', help='Initiate deployment over PXE/HTTP', action='store_true')
|
||||
ap.add_argument('-p', '--prepareonly', help='Prepare only, skip any interaction with a BMC associated with this deployment action', action='store_true')
|
||||
ap.add_argument('-m', '--maxnodes', help='Specifiy a maximum nodes to be deployed')
|
||||
ap.add_argument('noderange', help='Set of nodes to deploy')
|
||||
ap.add_argument('profile', nargs='?', help='Profile name to deploy')
|
||||
args, extra = ap.parse_known_args(args)
|
||||
if args.profile is None and len(extra) == 1:
|
||||
args.profile = extra[0]
|
||||
extra = extra[1:]
|
||||
if args.profile and not args.network:
|
||||
sys.stderr.write('-n is a required argument currently to perform an install, optionally with -p\n')
|
||||
return 1
|
||||
if not args.profile and args.network:
|
||||
sys.stderr.write('Both noderange and a profile name are required arguments to request a network deployment\n')
|
||||
return 1
|
||||
if args.clear and args.profile:
|
||||
sys.stderr.write(
|
||||
'The -c/--clear option should not be used with a profile, '
|
||||
'it is a request to not deploy any profile, and will clear '
|
||||
'whatever the current profile is without being specified\n')
|
||||
return 1
|
||||
if extra:
|
||||
sys.stderr.write('Unrecognized arguments: ' + repr(extra) + '\n')
|
||||
c = client.Command()
|
||||
c.stop_if_noderange_over(args.noderange, args.maxnodes)
|
||||
errnodes = set([])
|
||||
for rsp in c.read('/noderange/{0}/nodes/'.format(args.noderange)):
|
||||
if 'error' in rsp:
|
||||
sys.stderr.write(rsp['error'] + '\n')
|
||||
sys.exit(1)
|
||||
if args.clear:
|
||||
cleararm(args.noderange, c)
|
||||
clearpending(args.noderange, c)
|
||||
elif args.profile:
|
||||
profnames = []
|
||||
for prof in c.read('/deployment/profiles/'):
|
||||
profname = prof.get('item', {}).get('href', None)
|
||||
if profname:
|
||||
profname = profname.replace('/', '')
|
||||
profnames.append(profname)
|
||||
if profname == args.profile:
|
||||
break
|
||||
else:
|
||||
sys.stderr.write('The specified profile "{}" is not an available profile\n'.format(args.profile))
|
||||
if profnames:
|
||||
sys.stderr.write('The following profiles are available:\n')
|
||||
for profname in profnames:
|
||||
sys.stderr.write(' ' + profname + '\n')
|
||||
else:
|
||||
sys.stderr.write('No deployment profiles available, try osdeploy import or imgutil capture\n')
|
||||
sys.exit(1)
|
||||
armonce(args.noderange, c)
|
||||
setpending(args.noderange, args.profile, c)
|
||||
else:
|
||||
databynode = {}
|
||||
for r in c.read('/noderange/{0}/attributes/current'.format(args.noderange)):
|
||||
dbn = r.get('databynode', {})
|
||||
for node in dbn:
|
||||
if node not in databynode:
|
||||
databynode[node] = {}
|
||||
for attr in dbn[node]:
|
||||
if attr in ('deployment.pendingprofile', 'deployment.apiarmed', 'deployment.stagedprofile', 'deployment.profile', 'deployment.state', 'deployment.state_detail'):
|
||||
databynode[node][attr] = dbn[node][attr].get('value', '')
|
||||
for node in sortutil.natural_sort(databynode):
|
||||
profile = databynode[node].get('deployment.pendingprofile', '')
|
||||
if profile:
|
||||
profile = 'pending: {}'.format(profile)
|
||||
else:
|
||||
profile = databynode[node].get('deployment.stagedprofile', '')
|
||||
if profile:
|
||||
profile = 'staged: {}'.format(profile)
|
||||
else:
|
||||
profile = databynode[node].get('deployment.profile', '')
|
||||
if profile:
|
||||
profile = 'completed: {}'.format(profile)
|
||||
else:
|
||||
profile= 'No profile pending or applied'
|
||||
armed = databynode[node].get('deployment.apiarmed', '')
|
||||
if armed in ('once', 'continuous'):
|
||||
armed = ' (node authentication armed)'
|
||||
else:
|
||||
armed = ''
|
||||
stateinfo = ''
|
||||
deploymentstate = databynode[node].get('deployment.state', '')
|
||||
if deploymentstate:
|
||||
statedetails = databynode[node].get('deployment.state_detail', '')
|
||||
if statedetails:
|
||||
stateinfo = '{}: {}'.format(deploymentstate, statedetails)
|
||||
else:
|
||||
stateinfo = deploymentstate
|
||||
if stateinfo:
|
||||
print('{0}: {1} ({2})'.format(node, profile, stateinfo))
|
||||
else:
|
||||
print('{0}: {1}{2}'.format(node, profile, armed))
|
||||
sys.exit(0)
|
||||
if not args.clear and args.network and not args.prepareonly:
|
||||
rc = c.simple_noderange_command(args.noderange, '/boot/nextdevice', 'network',
|
||||
bootmode='uefi',
|
||||
persistent=False,
|
||||
errnodes=errnodes)
|
||||
if errnodes:
|
||||
sys.stderr.write(
|
||||
'Unable to set boot device for following nodes: {0}\n'.format(
|
||||
','.join(errnodes)))
|
||||
return 1
|
||||
rc |= c.simple_noderange_command(args.noderange, '/power/state', 'boot')
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -34,7 +34,6 @@ defcolumns = ['Node', 'Model', 'Serial', 'UUID', 'Mac Address', 'Type',
|
||||
columnmapping = {
|
||||
'Node': 'nodename',
|
||||
'Model': 'modelnumber',
|
||||
'Model Name': 'modelname',
|
||||
'Serial': 'serialnumber',
|
||||
'UUID': 'uuid',
|
||||
'Type': 'types',
|
||||
@@ -48,54 +47,20 @@ columnmapping = {
|
||||
'Advertised IP': 'otheripaddrs',
|
||||
'Other IP': 'otheripaddrs',
|
||||
}
|
||||
#TODO: add chassis uuid
|
||||
|
||||
def register_endpoint(options, session, addr):
|
||||
neednewline = False
|
||||
current = 0
|
||||
for rsp in session.update('/discovery/register', {'addresses': addr}):
|
||||
if 'count' in rsp:
|
||||
total = rsp['count']
|
||||
elif total > 1:
|
||||
current += 1
|
||||
sys.stdout.write('\x1b[2K\r{0:.1f}% Scanned'.format(100 * current / total))
|
||||
sys.stdout.flush()
|
||||
neednewline = True
|
||||
if 'created' in rsp:
|
||||
if neednewline:
|
||||
print('')
|
||||
neednewline = False
|
||||
print('Registered: {0}'.format(rsp['created']))
|
||||
if neednewline:
|
||||
print('')
|
||||
|
||||
def subscribe_discovery(options, session, subscribe, targ):
|
||||
keyn = 'subscribe' if subscribe else 'unsubscribe'
|
||||
payload = {keyn: targ}
|
||||
if subscribe:
|
||||
for rsp in session.update('/discovery/subscriptions/{0}'.format(targ), payload):
|
||||
if 'status' in rsp:
|
||||
print(rsp['status'])
|
||||
else:
|
||||
for rsp in session.delete('/discovery/subscriptions/{0}'.format(targ)):
|
||||
if 'status' in rsp:
|
||||
print(rsp['status'])
|
||||
|
||||
def print_disco(options, session, currmac, outhandler, columns):
|
||||
procinfo = {}
|
||||
for tmpinfo in session.read('/discovery/by-mac/{0}'.format(currmac)):
|
||||
|
||||
procinfo.update(tmpinfo)
|
||||
if 'Switch' in columns or 'Port' in columns:
|
||||
if 'switch' in procinfo:
|
||||
procinfo['port'] = procinfo['switchport']
|
||||
else:
|
||||
for tmpinfo in session.read(
|
||||
'/networking/macs/by-mac/{0}'.format(currmac)):
|
||||
if 'ports' in tmpinfo:
|
||||
# The api sorts so that the most specific available value
|
||||
# is last
|
||||
procinfo.update(tmpinfo['ports'][-1])
|
||||
for tmpinfo in session.read(
|
||||
'/networking/macs/by-mac/{0}'.format(currmac)):
|
||||
if 'ports' in tmpinfo:
|
||||
# The api sorts so that the most specific available value
|
||||
# is last
|
||||
procinfo.update(tmpinfo['ports'][-1])
|
||||
record = []
|
||||
for col in columns:
|
||||
rawcol = columnmapping[col]
|
||||
@@ -127,8 +92,6 @@ def process_header(header):
|
||||
fields.append('hardwaremanagement.manager')
|
||||
elif datum in ('bmc gateway', 'xcc gateway', 'imm gateway'):
|
||||
fields.append('net.bmc.ipv4_gateway')
|
||||
elif datum in ('bmc_gateway', 'xcc_gateway', 'imm_gateway'):
|
||||
fields.append('net.bmc.ipv4_gateway')
|
||||
elif datum in ('bmcuser', 'username', 'user'):
|
||||
fields.append('secret.hardwaremanagementuser')
|
||||
elif datum in ('bmcpass', 'password', 'pass'):
|
||||
@@ -183,14 +146,12 @@ unique_fields = frozenset(['serial', 'mac', 'uuid'])
|
||||
def import_csv(options, session):
|
||||
nodedata = []
|
||||
unique_data = {}
|
||||
exitcode = 0
|
||||
with open(options.importfile, 'r') as datasrc:
|
||||
records = csv.reader(datasrc)
|
||||
fields = process_header(next(records))
|
||||
for field in fields:
|
||||
if field in unique_fields:
|
||||
unique_data[field] = set([])
|
||||
broken = False
|
||||
for record in records:
|
||||
currfields = list(fields)
|
||||
nodedatum = {}
|
||||
@@ -207,16 +168,14 @@ def import_csv(options, session):
|
||||
nodedatum[currfield] = datum
|
||||
if not datum_complete(nodedatum):
|
||||
sys.exit(1)
|
||||
if not search_record(nodedatum, options, session) and not broken:
|
||||
if not search_record(nodedatum, options, session):
|
||||
blocking_scan(session)
|
||||
if not search_record(nodedatum, options, session):
|
||||
sys.stderr.write(
|
||||
"Could not match the following data: " +
|
||||
repr(nodedatum) + '\n')
|
||||
broken = True
|
||||
sys.exit(1)
|
||||
nodedata.append(nodedatum)
|
||||
if broken:
|
||||
sys.exit(1)
|
||||
for datum in nodedata:
|
||||
maclist = search_record(datum, options, session)
|
||||
datum = datum_to_attrib(datum)
|
||||
@@ -224,7 +183,6 @@ def import_csv(options, session):
|
||||
for res in session.create('/nodes/', datum):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode |= res.get('errorcode', 1)
|
||||
continue
|
||||
elif 'created' in res:
|
||||
print('Defined ' + res['created'])
|
||||
@@ -235,14 +193,11 @@ def import_csv(options, session):
|
||||
{'node': nodename}):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode |= res.get('errorcode', 1)
|
||||
continue
|
||||
elif 'assigned' in res:
|
||||
print('Discovered ' + res['assigned'])
|
||||
else:
|
||||
print(repr(res))
|
||||
if exitcode:
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def list_discovery(options, session):
|
||||
@@ -277,12 +232,8 @@ def clear_discovery(options, session):
|
||||
else:
|
||||
print(repr(res))
|
||||
|
||||
def list_matching_macs(options, session, node=None, checknode=True):
|
||||
def list_matching_macs(options, session):
|
||||
path = '/discovery/'
|
||||
if node:
|
||||
path += 'by-node/{0}/'.format(node)
|
||||
elif checknode and options.node:
|
||||
path += 'by-node/{0}/'.format(options.node)
|
||||
if options.model:
|
||||
path += 'by-model/{0}/'.format(options.model)
|
||||
if options.serial:
|
||||
@@ -303,46 +254,36 @@ def list_matching_macs(options, session, node=None, checknode=True):
|
||||
return [options.mac.replace(':', '-')]
|
||||
else:
|
||||
path += 'by-mac/'
|
||||
ret = []
|
||||
for x in session.read(path):
|
||||
if 'item' in x and 'href' in x['item']:
|
||||
ret.append(x['item']['href'])
|
||||
return ret
|
||||
return [x['item']['href'] for x in session.read(path)]
|
||||
|
||||
def assign_discovery(options, session, needid=True):
|
||||
def assign_discovery(options, session):
|
||||
abort = False
|
||||
if options.importfile:
|
||||
return import_csv(options, session)
|
||||
if not options.node:
|
||||
sys.stderr.write("Node (-n) must be specified for assignment\n")
|
||||
abort = True
|
||||
if needid and not (options.serial or options.uuid or options.mac):
|
||||
if not (options.serial or options.uuid or options.mac):
|
||||
sys.stderr.write(
|
||||
"UUID (-u), serial (-s), or ether address (-e) required for "
|
||||
"assignment\n")
|
||||
abort = True
|
||||
if not options.node:
|
||||
sys.stderr.write("Node (-n) must be specified for assignment\n")
|
||||
abort = True
|
||||
if abort:
|
||||
sys.exit(1)
|
||||
matches = list_matching_macs(options, session, None if needid else options.node, False)
|
||||
matches = list_matching_macs(options, session)
|
||||
if not matches:
|
||||
# Do a rescan to catch missing requested data
|
||||
blocking_scan(session)
|
||||
matches = list_matching_macs(options, session, None if needid else options.node, False)
|
||||
matches = list_matching_macs(options, session)
|
||||
if not matches:
|
||||
sys.stderr.write("No matching discovery candidates found\n")
|
||||
sys.exit(1)
|
||||
exitcode = 0
|
||||
for res in session.update('/discovery/by-mac/{0}'.format(matches[0]),
|
||||
{'node': options.node}):
|
||||
if 'assigned' in res:
|
||||
print('Assigned: {0}'.format(res['assigned']))
|
||||
elif 'error' in res:
|
||||
sys.stderr.write('Error: {0}\n'.format(res['error']))
|
||||
exitcode |= res.get('errorcode', 1)
|
||||
else:
|
||||
print(repr(res))
|
||||
if exitcode:
|
||||
sys.exit(exitcode)
|
||||
|
||||
def blocking_scan(session):
|
||||
list(session.update('/discovery/rescan', {'rescan': 'start'}))
|
||||
@@ -353,7 +294,7 @@ def blocking_scan(session):
|
||||
|
||||
def main():
|
||||
parser = optparse.OptionParser(
|
||||
usage='Usage: %prog [list|assign|rescan|clear|subscribe|unsubscribe|register] [options]')
|
||||
usage='Usage: %prog [list|assign|rescan|clear] [options]')
|
||||
# -a for 'address' maybe?
|
||||
# order by
|
||||
# show state (discovered or..
|
||||
@@ -391,15 +332,9 @@ def main():
|
||||
parser.add_option('-o', '--order', dest='order',
|
||||
help='Order output by given field', metavar='ORDER')
|
||||
(options, args) = parser.parse_args()
|
||||
if len(args) == 0 or args[0] not in ('list', 'assign', 'reassign', 'rescan', 'clear', 'subscribe', 'unsubscribe', 'register'):
|
||||
if len(args) == 0 or args[0] not in ('list', 'assign', 'rescan', 'clear'):
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
if args[0] == 'register' and len(args) != 2:
|
||||
sys.stderr.write('Register requires target address or range (e.g. 192.168.2.30, 192.168.2.0/24, or 192.168.2.1-192.168.2.30)\n')
|
||||
sys.exit(1)
|
||||
if 'subscribe' in args[0] and len(args) != 2:
|
||||
sys.stderr.write('subscribe/unsubscribe subcommands require switch name as argument\n')
|
||||
sys.exit(1)
|
||||
session = client.Command()
|
||||
if args[0] == 'list':
|
||||
list_discovery(options, session)
|
||||
@@ -407,14 +342,6 @@ def main():
|
||||
clear_discovery(options, session)
|
||||
if args[0] == 'assign':
|
||||
assign_discovery(options, session)
|
||||
if args[0] == 'reassign':
|
||||
assign_discovery(options, session, False)
|
||||
if args[0] == 'register':
|
||||
register_endpoint(options, session, args[1])
|
||||
if args[0] == 'subscribe':
|
||||
subscribe_discovery(options, session, True, args[1])
|
||||
if args[0] == 'unsubscribe':
|
||||
subscribe_discovery(options, session, False, args[1])
|
||||
if args[0] == 'rescan':
|
||||
blocking_scan(session)
|
||||
print("Rescan complete")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2019 Lenovo
|
||||
# Copyright 2015-2017 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
import codecs
|
||||
from datetime import datetime as dt
|
||||
from datetime import timedelta
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
@@ -34,26 +33,10 @@ if path.startswith('/opt'):
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
||||
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
||||
|
||||
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')
|
||||
argparser.add_option('-l', '--lines', type='int',
|
||||
help='return the last <n> entries '
|
||||
'for each node in the eventlog. '
|
||||
)
|
||||
argparser.add_option('-t', '--timeframe', type='string',
|
||||
help='return entries within a specified timeframe '
|
||||
'for each node in the eventlog. This will return '
|
||||
'entries from the last hours or days. '
|
||||
'1h would be one hour, 4d would be four days. '
|
||||
'format <num>h or <num>d'
|
||||
)
|
||||
usage="Usage: %prog [options] noderange [clear]")
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -62,11 +45,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()
|
||||
@@ -81,8 +64,6 @@ def format_event(evt):
|
||||
display = dt.strptime(evt['timestamp'], '%Y-%m-%dT%H:%M:%S')
|
||||
retparts.append(display.strftime('%m/%d/%Y %H:%M:%S'))
|
||||
dscparts = []
|
||||
if evt.get('log_id', None):
|
||||
retparts.append(evt['log_id'] + ':')
|
||||
if 'component_type' in evt and evt['component_type'] is not None:
|
||||
dscparts.append(evt['component_type'])
|
||||
if 'component' in evt and evt['component'] is not None:
|
||||
@@ -96,39 +77,13 @@ def format_event(evt):
|
||||
pass
|
||||
dscparts.append(evttext)
|
||||
retparts.append(' - '.join(dscparts))
|
||||
msg = evt.get('message')
|
||||
if not msg:
|
||||
msg = ''
|
||||
return ' '.join(retparts) + msg
|
||||
return ' '.join(retparts)
|
||||
|
||||
|
||||
if deletemode:
|
||||
func = session.delete
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
else:
|
||||
func = session.read
|
||||
|
||||
if options.timeframe:
|
||||
try:
|
||||
delta = int(options.timeframe[:-1])
|
||||
except ValueError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
if options.timeframe[-1].lower() == 'd':
|
||||
tdelta = timedelta(days=delta)
|
||||
elif options.timeframe[-1].lower() == 'h':
|
||||
tdelta = timedelta(hours=delta)
|
||||
else:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
timeframe = dt.now() - tdelta
|
||||
|
||||
event_dict = {}
|
||||
nodes = []
|
||||
for res in session.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
node = res.get('item', {}).get('href', '/').replace('/', '')
|
||||
nodes.append(node)
|
||||
event_dict[node] = []
|
||||
|
||||
for rsp in func('/noderange/{0}/events/hardware/log'.format(noderange)):
|
||||
if 'error' in rsp:
|
||||
sys.stderr.write(rsp['error'] + '\n')
|
||||
@@ -141,31 +96,6 @@ for rsp in func('/noderange/{0}/events/hardware/log'.format(noderange)):
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, thisdata['error']))
|
||||
exitcode |= 1
|
||||
if 'events' in thisdata:
|
||||
evtdata = thisdata['events']
|
||||
if options.lines:
|
||||
event_dict[node].extend(evtdata)
|
||||
else:
|
||||
for evt in evtdata:
|
||||
if options.timeframe:
|
||||
# check if line is in timeframe
|
||||
if 'timestamp' in evt and evt['timestamp'] is not None:
|
||||
display = dt.strptime(evt['timestamp'], '%Y-%m-%dT%H:%M:%S')
|
||||
if display > timeframe:
|
||||
print('{0}: {1}'.format(node, format_event(evt)))
|
||||
else:
|
||||
print('{0}: {1}'.format(node, format_event(evt)))
|
||||
|
||||
if options.lines:
|
||||
for node in nodes:
|
||||
evtdata_list = event_dict[node]
|
||||
if len(evtdata_list) != 0:
|
||||
if len(evtdata_list) > options.lines:
|
||||
evtdata_list = evtdata_list[-abs(options.lines):]
|
||||
for evt in evtdata_list:
|
||||
if options.timeframe:
|
||||
if 'timestamp' in evt and evt['timestamp'] is not None:
|
||||
display = dt.strptime(evt['timestamp'], '%Y-%m-%dT%H:%M:%S')
|
||||
if display > timeframe:
|
||||
print('{0}: {1}'.format(node, format_event(evt)))
|
||||
else:
|
||||
print('{0}: {1}'.format(node, format_event(evt)))
|
||||
evtdata = thisdata['events']
|
||||
for evt in evtdata:
|
||||
print '{0}: {1}'.format(node, format_event(evt))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -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:
|
||||
@@ -88,9 +84,9 @@ client.check_globbing(noderange)
|
||||
|
||||
def get_update_progress(session, url):
|
||||
for res in session.read(url):
|
||||
status = res.get('phase', 'error')
|
||||
percent = res.get('progress', None)
|
||||
detail = res.get('detail', repr(res)),
|
||||
status = res['phase']
|
||||
percent = res['progress']
|
||||
detail = res['detail']
|
||||
if status == 'error':
|
||||
text = 'error!'
|
||||
else:
|
||||
@@ -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)
|
||||
@@ -109,12 +104,6 @@ def update_firmware(session, filename):
|
||||
if options.backup:
|
||||
upargs['bank'] = 'backup'
|
||||
noderrs = {}
|
||||
if session.unixdomain:
|
||||
of = open(filename, 'rb')
|
||||
try:
|
||||
session.add_file(filename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
for res in session.create(resource, upargs):
|
||||
if 'created' not in res:
|
||||
for nodename in res.get('databynode', ()):
|
||||
@@ -147,25 +136,22 @@ def update_firmware(session, filename):
|
||||
def show_firmware(session):
|
||||
global exitcode
|
||||
firmware_shown = False
|
||||
nodes_matched = False
|
||||
for component in components:
|
||||
for res in session.read(
|
||||
'/noderange/{0}/inventory/firmware/all/{1}'.format(
|
||||
noderange, component)):
|
||||
nodes_matched = True
|
||||
exitcode |= client.printerror(res)
|
||||
if 'databynode' not in res:
|
||||
continue
|
||||
for node in res['databynode']:
|
||||
exitcode |= client.printerror(res['databynode'][node], node)
|
||||
if 'firmware' not in res['databynode'][node]:
|
||||
continue
|
||||
for inv in res['databynode'][node]['firmware']:
|
||||
for prefix in inv:
|
||||
firmware_shown = True
|
||||
printfirm(node, prefix, inv[prefix])
|
||||
if not nodes_matched:
|
||||
sys.stderr.write('No matching nodes for noderange "{0}"\n'.format(noderange))
|
||||
elif not firmware_shown and not exitcode:
|
||||
if not firmware_shown:
|
||||
argparser.print_help()
|
||||
|
||||
|
||||
@@ -177,4 +163,4 @@ try:
|
||||
update_firmware(session, upfile)
|
||||
except KeyboardInterrupt:
|
||||
print('')
|
||||
sys.exit(exitcode)
|
||||
sys.exit(exitcode)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -60,12 +60,10 @@ nodetype="nodegroups"
|
||||
|
||||
try:
|
||||
nodegroups = args[0]
|
||||
client.check_globbing(nodegroups)
|
||||
nodelist = '/{0}/{1}/'.format(nodetype,nodegroups)
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
nodelist = '/nodegroups/'
|
||||
client.check_globbing(nodegroups)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
|
||||
@@ -99,7 +97,7 @@ if len(args) > 1:
|
||||
|
||||
requestargs=args[1:]
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
print str(e)
|
||||
|
||||
if exitcode != 0:
|
||||
sys.exit(exitcode)
|
||||
@@ -125,6 +123,6 @@ else:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
print(res['item']['href'].replace('/', ''))
|
||||
print res['item']['href'].replace('/', '')
|
||||
|
||||
sys.exit(exitcode)
|
||||
sys.exit(exitcode)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -45,15 +45,14 @@ except IndexError:
|
||||
client.check_globbing(noderange)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
for group in noderange.split(','):
|
||||
attribs = {'name': group}
|
||||
for arg in args[1:]:
|
||||
key, val = arg.split('=', 1)
|
||||
attribs[key] = val
|
||||
for r in session.create('/nodegroups/', attribs):
|
||||
if 'error' in r:
|
||||
sys.stderr.write(r['error'] + '\n')
|
||||
exitcode |= 1
|
||||
if 'created' in r:
|
||||
print('{0}: created'.format(r['created']))
|
||||
sys.exit(exitcode)
|
||||
attribs = {'name': noderange}
|
||||
for arg in args[1:]:
|
||||
key, val = arg.split('=', 1)
|
||||
attribs[key] = val
|
||||
for r in session.create('/nodegroups/', attribs):
|
||||
if 'error' in r:
|
||||
sys.stderr.write(r['error'] + '\n')
|
||||
exitcode |= 1
|
||||
if 'created' in r:
|
||||
print('{0}: created'.format(r['created']))
|
||||
sys.exit(exitcode)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -43,11 +43,10 @@ noderange = args[0]
|
||||
client.check_globbing(noderange)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
for group in noderange.split(','):
|
||||
for r in session.delete('/nodegroups/{0}'.format(group)):
|
||||
if 'error' in r:
|
||||
sys.stderr.write(r['error'] + '\n')
|
||||
exitcode |= 1
|
||||
if 'deleted' in r:
|
||||
print('{0}: deleted'.format(r['deleted']))
|
||||
sys.exit(exitcode)
|
||||
for r in session.delete('/nodegroups/{0}'.format(noderange)):
|
||||
if 'error' in r:
|
||||
sys.stderr.write(r['error'] + '\n')
|
||||
exitcode |= 1
|
||||
if 'deleted' in r:
|
||||
print('{0}: deleted'.format(r['deleted']))
|
||||
sys.exit(exitcode)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2019 Lenovo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -32,8 +32,7 @@ if path.startswith('/opt'):
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
||||
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange>")
|
||||
(options, args) = argparser.parse_args()
|
||||
@@ -50,7 +49,6 @@ codemappings = {
|
||||
'warning': 1,
|
||||
'critical': 2,
|
||||
'failed': 2,
|
||||
'unknown': 2,
|
||||
}
|
||||
|
||||
def main():
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -31,7 +31,7 @@ if path.startswith('/opt'):
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange> [on|off|blink]")
|
||||
argparser = optparse.OptionParser(usage="Usage: %prog <noderange> [on|off]")
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -42,6 +42,9 @@ client.check_globbing(noderange)
|
||||
identifystate = None
|
||||
if len(sys.argv) > 2:
|
||||
identifystate = sys.argv[2]
|
||||
else:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
sys.exit(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -34,8 +34,7 @@ if path.startswith('/opt'):
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
||||
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
|
||||
|
||||
filters = []
|
||||
|
||||
@@ -51,22 +50,15 @@ def print_mem_info(node, prefix, meminfo):
|
||||
memdescfmt = '{0}GB PC'
|
||||
if meminfo['memory_type'] == 'DDR3 SDRAM':
|
||||
memdescfmt += '3-{1} '
|
||||
elif 'DDR4' in meminfo['memory_type']:
|
||||
elif meminfo['memory_type'] == 'DDR4 SDRAM':
|
||||
memdescfmt += '4-{1} '
|
||||
elif 'DDR5' in meminfo['memory_type']:
|
||||
memdescfmt += '5-{1} '
|
||||
elif 'DCPMM' in meminfo['memory_type']:
|
||||
memdescfmt = '{0}GB {1} '
|
||||
meminfo['module_type'] = 'DCPMM'
|
||||
else:
|
||||
print('{0}: {1}: Unrecognized Memory'.format(node, prefix))
|
||||
return
|
||||
if meminfo.get('ecc', False):
|
||||
if meminfo['ecc']:
|
||||
memdescfmt += 'ECC '
|
||||
capacity = meminfo['capacity_mb'] / 1024
|
||||
modtype = meminfo.get('module_type', None)
|
||||
if modtype:
|
||||
memdescfmt += modtype
|
||||
memdescfmt += meminfo['module_type']
|
||||
memdesc = memdescfmt.format(capacity, meminfo['speed'])
|
||||
print('{0}: {1} description: {2}'.format(node, prefix, memdesc))
|
||||
print('{0}: {1} manufacturer: {2}'.format(
|
||||
@@ -74,11 +66,10 @@ def print_mem_info(node, prefix, meminfo):
|
||||
print('{0}: {1} model: {2}'.format(node, prefix, meminfo['model']))
|
||||
print('{0}: {1} serial number: {2}'.format(node, prefix,
|
||||
meminfo['serial']))
|
||||
if 'manufacture_date' in meminfo:
|
||||
print('{0}: {1} manufacture date: {2}'.format(node, prefix,
|
||||
meminfo['manufacture_date']))
|
||||
print('{0}: {1} manufacture location: {2}'.format(
|
||||
node, prefix, meminfo['manufacture_location']))
|
||||
print('{0}: {1} manufacture date: {2}'.format(node, prefix,
|
||||
meminfo['manufacture_date']))
|
||||
print('{0}: {1} manufacture location: {2}'.format(
|
||||
node, prefix, meminfo['manufacture_location']))
|
||||
|
||||
exitcode = 0
|
||||
|
||||
@@ -101,7 +92,6 @@ usedprefixes = set([])
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog <noderange> [serial|model|uuid|mac]")
|
||||
argparser.add_option('-j', '--json', action='store_true', help='Output JSON')
|
||||
argparser.add_option('-s', '--store', action='store_true', help='Store serial, model, and uuid into id.serial, id.model, and id.uuid')
|
||||
(options, args) = argparser.parse_args()
|
||||
try:
|
||||
noderange = args[0]
|
||||
@@ -129,36 +119,17 @@ if len(args) > 1:
|
||||
try:
|
||||
if options.json:
|
||||
databynode = {}
|
||||
if options.store and len(args) <= 1:
|
||||
url = '/noderange/{0}/inventory/hardware/all/system'
|
||||
pushattribs = {}
|
||||
session = client.Command()
|
||||
for res in session.read(url.format(noderange)):
|
||||
printerror(res)
|
||||
if 'databynode' not in res:
|
||||
continue
|
||||
for node in res['databynode']:
|
||||
if options.store and node not in pushattribs:
|
||||
pushattribs[node] = {}
|
||||
printerror(res['databynode'][node], node)
|
||||
if 'inventory' not in res['databynode'][node]:
|
||||
continue
|
||||
for inv in res['databynode'][node]['inventory']:
|
||||
prefix = inv['name']
|
||||
if options.store and prefix == 'System':
|
||||
currinfo = inv.get('information', {})
|
||||
curruuid = currinfo.get('UUID', '')
|
||||
if curruuid:
|
||||
curruuid = curruuid.lower()
|
||||
pushattribs[node]['id.uuid'] = curruuid
|
||||
currserial = currinfo.get('Serial Number', '')
|
||||
if currserial:
|
||||
currserial = currserial.strip()
|
||||
pushattribs[node]['id.serial'] = currserial
|
||||
currmodelnum = currinfo.get('Model', '')
|
||||
if currmodelnum:
|
||||
currmodelnum = currmodelnum.strip()
|
||||
pushattribs[node]['id.model'] = currmodelnum
|
||||
idx = 2
|
||||
while (node, prefix) in usedprefixes:
|
||||
prefix = '{0} {1}'.format(inv['name'], idx)
|
||||
@@ -170,8 +141,8 @@ try:
|
||||
if node not in databynode:
|
||||
databynode[node] = {}
|
||||
databynode[node][prefix] = inv
|
||||
elif not options.store:
|
||||
print('{0}: {1}: Not Present'.format(node, prefix))
|
||||
else:
|
||||
print '{0}: {1}: Not Present'.format(node, prefix)
|
||||
continue
|
||||
info = inv['information']
|
||||
info.pop('board_extra', None)
|
||||
@@ -201,18 +172,12 @@ try:
|
||||
databynode[node] = {}
|
||||
databynode[node][prefix] = inv
|
||||
break
|
||||
elif not options.store:
|
||||
print(u'{0}: {1} {2}: {3}'.format(node, prefix,
|
||||
pretty(datum),
|
||||
info[datum]))
|
||||
print(u'{0}: {1} {2}: {3}'.format(node, prefix,
|
||||
pretty(datum),
|
||||
info[datum]))
|
||||
if options.json:
|
||||
print(json.dumps(databynode, sort_keys=True, indent=4,
|
||||
separators=(',', ': ')))
|
||||
if pushattribs:
|
||||
for node in pushattribs:
|
||||
for rsp in session.update('/nodes/{0}/attributes/current'.format(node), pushattribs[node]):
|
||||
if 'error' in rsp:
|
||||
sys.stderr.write(rsp['error'] + '\n')
|
||||
except KeyboardInterrupt:
|
||||
print('')
|
||||
sys.exit(exitcode)
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
#!/usr/libexec/platform-python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 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.
|
||||
|
||||
__author__ = 'tkucherera'
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog <start_node> -i <interface> <end_node> -e <eface>",
|
||||
)
|
||||
argparser.add_option('-i', '--interface', type='str',
|
||||
help='interface to check path against for the start node')
|
||||
argparser.add_option('-e', '--eface', type='str',
|
||||
help='interface to check path against for the end node')
|
||||
argparser.add_option('-c', '--cumulus', action="store_true", dest="cumulus",
|
||||
help='return layer 2 route through cumulus switches only')
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
try:
|
||||
start_node = args[0]
|
||||
end_node = args[1]
|
||||
interface = options.interface
|
||||
eface = options.eface
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
session = client.Command()
|
||||
|
||||
def get_neighbors(switch):
|
||||
switch_neigbors = []
|
||||
url = '/networking/neighbors/by-switch/{0}/by-peername/'.format(switch)
|
||||
for neighbor in session.read(url):
|
||||
try:
|
||||
switch = neighbor['item']['href'].strip('/')
|
||||
except:
|
||||
continue
|
||||
if switch in all_switches:
|
||||
switch_neigbors.append(switch)
|
||||
return switch_neigbors
|
||||
|
||||
def find_path(start, end, path=[]):
|
||||
path = path + [start]
|
||||
if start == end:
|
||||
return path # If start and end are the same, return the path
|
||||
|
||||
for node in get_neighbors(start):
|
||||
if node not in path:
|
||||
new_path = find_path(node, end, path)
|
||||
if new_path:
|
||||
return new_path # If a path is found, return it
|
||||
|
||||
return None # If no path is found, return None
|
||||
|
||||
def is_cumulus(switch):
|
||||
try:
|
||||
read_attrib = subprocess.check_output(['nodeattrib', switch, 'hardwaremanagement.method'])
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
for attribs in read_attrib.decode('utf-8').split('\n'):
|
||||
if len(attribs.split(':')) > 1:
|
||||
attrib = attribs.split(':')
|
||||
if attrib[2].strip() == 'affluent':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def host_to_switch(node, interface=None):
|
||||
# first check the the node config to see what switches are connected
|
||||
# if host is in rhel can use nmstate package
|
||||
if node in all_switches:
|
||||
return [node]
|
||||
switches = []
|
||||
netarg = 'net.*.switch'
|
||||
if interface:
|
||||
netarg = 'net.{0}.switch'.format(interface)
|
||||
try:
|
||||
read_attrib = subprocess.check_output(['nodeattrib', node, netarg])
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
for attribs in read_attrib.decode('utf-8').split('\n'):
|
||||
attrib = attribs.split(':')
|
||||
try:
|
||||
if ' net.mgt.switch' in attrib or attrib[2] == '':
|
||||
continue
|
||||
except IndexError:
|
||||
continue
|
||||
switch = attrib[2].strip()
|
||||
if is_cumulus(switch) and options.cumulus:
|
||||
switches.append(switch)
|
||||
else:
|
||||
switches.append(switch)
|
||||
return switches
|
||||
|
||||
def path_between_nodes(start_switches, end_switches):
|
||||
for start_switch in start_switches:
|
||||
for end_switch in end_switches:
|
||||
if start_switch == end_switch:
|
||||
return [start_switch]
|
||||
else:
|
||||
path = find_path(start_switch, end_switch)
|
||||
if path:
|
||||
return path
|
||||
else:
|
||||
return 'No path found'
|
||||
|
||||
|
||||
all_switches = []
|
||||
for res in session.read('/networking/neighbors/by-switch/'):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
switch = (res['item']['href'].replace('/', ''))
|
||||
all_switches.append(switch)
|
||||
|
||||
end_nodeslist = []
|
||||
nodelist = '/noderange/{0}/nodes/'.format(end_node)
|
||||
for res in session.read(nodelist):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
elem=(res['item']['href'].replace('/', ''))
|
||||
end_nodeslist.append(elem)
|
||||
|
||||
start_switches = host_to_switch(start_node, interface)
|
||||
for end_node in end_nodeslist:
|
||||
if end_node:
|
||||
end_switches = host_to_switch(end_node, eface)
|
||||
if not end_switches:
|
||||
print('Error: net.{0}.switch attribute is not valid')
|
||||
continue
|
||||
path = path_between_nodes(start_switches, end_switches)
|
||||
print(f'{start_node} to {end_node}: {path}')
|
||||
|
||||
# TODO dont put switches that are connected through management interfaces.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2019 Lenovo
|
||||
@@ -36,116 +36,49 @@ exitcode = 0
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: "
|
||||
"%prog <noderange> [list][install <license_filename>|save <directory>|delete <license_feature_name>]")
|
||||
argparser.add_option('-m', '--maxnodes', type='int',
|
||||
help='Specify a maximum number of '
|
||||
'nodes to delete licenses from, '
|
||||
'prompting if over the threshold')
|
||||
"%prog <noderange> [list][install <file>]")
|
||||
(options, args) = argparser.parse_args()
|
||||
upfile = None
|
||||
downdir = None
|
||||
delete = False
|
||||
try:
|
||||
noderange = args[0]
|
||||
if len(args) > 1:
|
||||
if args[1] == 'install':
|
||||
upfile = args[2]
|
||||
elif args[1] == 'save':
|
||||
downdir = args[2]
|
||||
elif args[1] == 'delete':
|
||||
delete = args[2]
|
||||
elif args[1] != 'list':
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
else:
|
||||
components = ['all']
|
||||
except IndexError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
client.check_globbing(noderange)
|
||||
|
||||
|
||||
def install_license(session, filename):
|
||||
global exitcode
|
||||
resource = '/noderange/{0}/configuration/' \
|
||||
'management_controller/licenses/'.format(noderange)
|
||||
filename = os.path.abspath(filename)
|
||||
instargs = {'filename': filename}
|
||||
if session.unixdomain:
|
||||
of = open(filename, 'rb')
|
||||
try:
|
||||
session.add_file(filename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
for res in session.create(resource, instargs):
|
||||
for node in res.get('databynode', []):
|
||||
if 'error' in res['databynode'][node]:
|
||||
sys.stderr.write('{0}: {1}\n'.format(
|
||||
node, res['databynode'][node]['error']))
|
||||
sys.exit(res['databynode'][node].get('errorcode', 1))
|
||||
show_licenses(session)
|
||||
|
||||
|
||||
def save_licenses(session, dirname):
|
||||
global exitcode
|
||||
resource = '/noderange/{0}/configuration/' \
|
||||
'management_controller/save_licenses'.format(noderange)
|
||||
filename = os.path.abspath(dirname)
|
||||
instargs = {'dirname': filename}
|
||||
for res in session.create(resource, instargs):
|
||||
for node in res.get('databynode', {}):
|
||||
fname = res['databynode'][node].get('filename', None)
|
||||
if fname:
|
||||
print('{0}: Saved license to {1}'.format(node, fname))
|
||||
elif 'error' in res['databynode'][node]:
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, res['databynode'][node]['error']))
|
||||
else:
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, repr(res['databynode'][node])))
|
||||
|
||||
pass # print(repr(res))
|
||||
show_licenses()
|
||||
|
||||
def show_licenses(session):
|
||||
global exitcode
|
||||
firmware_shown = False
|
||||
for res in session.read(
|
||||
'/noderange/{0}/configuration/management_controller/licenses/'
|
||||
'all'.format(noderange)):
|
||||
for node in res.get('databynode', {}):
|
||||
for license in res['databynode'][node].get('License', []):
|
||||
msg = '{0}: {1}'.format(node, license.get('feature',
|
||||
'Unknown'))
|
||||
if license.get('state', 'Active') != 'Active':
|
||||
msg += ' ({0})'.format(license['state'])
|
||||
print(msg)
|
||||
print('{0}: {1}'.format(node, license.get('feature',
|
||||
'Unknown')))
|
||||
|
||||
|
||||
def delete_license(session, licname):
|
||||
global exitcode
|
||||
licstodel = []
|
||||
for res in list(session.read(
|
||||
'/noderange/{0}/configuration/management_controller/licenses/'
|
||||
'all'.format(noderange))):
|
||||
for node in res.get('databynode', {}):
|
||||
for license in res['databynode'][node].get('License', []):
|
||||
if license.get('feature', None) == licname:
|
||||
prefix = '/nodes/{0}/configuration/management_controller/licenses/'.format(node)
|
||||
for currlic in list(session.read(prefix)):
|
||||
currlic = currlic.get('item', {}).get('href', 'all')
|
||||
if currlic == 'all':
|
||||
continue
|
||||
currname = list(session.read(prefix + currlic))[0]
|
||||
currname = currname.get('License', [{}])[0].get('feature', None)
|
||||
if currname == licname:
|
||||
list(session.delete(prefix + currlic))
|
||||
show_licenses(session)
|
||||
|
||||
try:
|
||||
session = client.Command()
|
||||
if upfile:
|
||||
install_license(session, upfile)
|
||||
elif downdir:
|
||||
save_licenses(session, downdir)
|
||||
elif delete:
|
||||
session.stop_if_noderange_over(noderange, options.maxnodes)
|
||||
delete_license(session, delete)
|
||||
else:
|
||||
if upfile is None:
|
||||
show_licenses(session)
|
||||
else:
|
||||
install_license(session, upfile)
|
||||
except KeyboardInterrupt:
|
||||
print('')
|
||||
sys.exit(exitcode)
|
||||
sys.exit(exitcode)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/libexec/platform-python
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -15,15 +15,13 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__author__ = 'jjohnson2,alin37,wgrzeda'
|
||||
__author__ = 'jjohnson2,alin37'
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
@@ -41,12 +39,9 @@ def main():
|
||||
" or: %prog [options] noderange <nodeattribute>...")
|
||||
argparser.add_option('-b', '--blame', action='store_true',
|
||||
help='Show information about how attributes inherited')
|
||||
argparser.add_option('-d', '--delim', metavar="STRING", default = "\n",
|
||||
help='Delimiter separating the values')
|
||||
(options, args) = argparser.parse_args()
|
||||
noderange=""
|
||||
nodelist=""
|
||||
list = []
|
||||
try:
|
||||
noderange = args[0]
|
||||
nodelist = '/noderange/{0}/nodes/'.format(noderange)
|
||||
@@ -66,9 +61,7 @@ def main():
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
else:
|
||||
elem=(res['item']['href'].replace('/', ''))
|
||||
list.append(elem)
|
||||
print(options.delim.join(list))
|
||||
print res['item']['href'].replace('/', '')
|
||||
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2018 Lenovo
|
||||
@@ -83,8 +83,7 @@ def list_media(noderange, media):
|
||||
for node in res.get('databynode', []):
|
||||
url = res['databynode'][node].get('url', None)
|
||||
name = res['databynode'][node].get('name', None)
|
||||
if (url and not url.startswith('file:') and
|
||||
not res['databynode'][node].get('secure', False)):
|
||||
if url and not res['databynode'][node].get('secure', False):
|
||||
name += ' (insecure)'
|
||||
if not name:
|
||||
continue
|
||||
@@ -111,12 +110,6 @@ def upload_media(noderange, media):
|
||||
resource = '/noderange/{0}/media/uploads/'.format(noderange)
|
||||
upargs = {'filename': filename}
|
||||
noderrs = {}
|
||||
if session.unixdomain:
|
||||
of = open(filename, 'rb')
|
||||
try:
|
||||
session.add_file(filename, of.fileno(), 'rb')
|
||||
except Exception:
|
||||
pass
|
||||
nodeurls = {}
|
||||
for res in session.create(resource, upargs):
|
||||
if 'created' not in res:
|
||||
@@ -193,7 +186,5 @@ def main():
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
handler(noderange, media)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(exitcode)
|
||||
main()
|
||||
@@ -1,140 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 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.
|
||||
|
||||
from collections import deque
|
||||
import optparse
|
||||
import os
|
||||
import select
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
pass
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
|
||||
|
||||
def run():
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] noderange")
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
help='Number of commands to run at a time')
|
||||
argparser.add_option('-s', '--substitutename',
|
||||
help='Use a different name other than the nodename for ping, with {}, it is the entire name evaluated as an expression, otherwise it is used as a suffix')
|
||||
# among other things, FD_SETSIZE limits. Besides, spawning too many
|
||||
# processes can be unkind for the unaware on memory pressure and such...
|
||||
(options, args) = argparser.parse_args()
|
||||
if len(args) < 1:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
client.check_globbing(args[0])
|
||||
concurrentprocs = options.count
|
||||
c = client.Command()
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
pipedesc = {}
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
|
||||
nodemap = {}
|
||||
if options.substitutename:
|
||||
subname = options.substitutename
|
||||
if '{' not in subname:
|
||||
subname = '{node}' + subname
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
|
||||
{'expression': subname}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
nodemap[node] = ex[node]['value']
|
||||
for rsp in c.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
if 'error' in rsp:
|
||||
sys.stderr.write(rsp['error'] + '\n')
|
||||
exitcode |= rsp.get('errorcode', 1)
|
||||
if rsp.get('item', {}).get('href', None):
|
||||
node = rsp['item']['href'][:-1]
|
||||
pingnode = nodemap.get(node, node)
|
||||
cmdv = ['ping', '-c', '1', '-W', '1', pingnode]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(pingnode, cmdv, all, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((pingnode, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
while all:
|
||||
pernodeout = {}
|
||||
for r in rdy:
|
||||
desc = pipedesc[r]
|
||||
node = desc['node']
|
||||
data = True
|
||||
while data and select.select([r], [], [], 0)[0]:
|
||||
data = r.readline()
|
||||
if not data:
|
||||
pop = desc['popen']
|
||||
ret = pop.poll()
|
||||
if ret is not None:
|
||||
exitcode = exitcode | ret
|
||||
all.discard(r)
|
||||
r.close()
|
||||
if desc['type'] == 'stdout':
|
||||
if ret:
|
||||
print('{0}: no_ping'.format(node))
|
||||
else:
|
||||
print('{0}: ping'.format(node))
|
||||
if pendingexecs:
|
||||
node, cmdv = pendingexecs.popleft()
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
if options.nonodeprefix:
|
||||
sys.stdout.write(line)
|
||||
else:
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
sys.exit(exitcode)
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
'type': 'stderr'}
|
||||
all.add(nopen.stdout)
|
||||
all.add(nopen.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -32,16 +32,11 @@ if path.startswith('/opt'):
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] <noderange> "
|
||||
"([status|on|off|shutdown|boot|reset|pdu_status|pdu_off|pdu_on])")
|
||||
usage="Usage: %prog [options] noderange "
|
||||
"([status|on|off|shutdown|boot|reset])")
|
||||
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]
|
||||
@@ -51,27 +46,22 @@ except IndexError:
|
||||
client.check_globbing(noderange)
|
||||
setstate = None
|
||||
if len(args) > 1:
|
||||
setstate = args[1]
|
||||
if setstate == 'softoff':
|
||||
setstate = 'shutdown'
|
||||
elif not args[1] in ('stat', 'state', 'status'):
|
||||
setstate = args[1]
|
||||
|
||||
if setstate not in (None, 'on', 'off', 'shutdown', 'boot', 'reset', 'pdu_status', 'pdu_stat', 'pdu_on', 'pdu_off', 'status', 'stat', 'state'):
|
||||
if setstate not in (None, 'on', 'off', 'shutdown', 'boot', 'reset'):
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
session = client.Command()
|
||||
exitcode = 0
|
||||
session.add_precede_key('oldstate')
|
||||
powurl = 'state'
|
||||
if setstate and setstate.startswith('pdu_'):
|
||||
setstate = setstate.replace('pdu_', '')
|
||||
powurl = 'inlets/all'
|
||||
if setstate in ('status', 'state', 'stat'):
|
||||
setstate = None
|
||||
|
||||
if options.previous:
|
||||
# get previous states
|
||||
prev = {}
|
||||
for rsp in session.read("/noderange/{0}/power/{1}".format(noderange, powurl)):
|
||||
for rsp in session.read("/noderange/{0}/power/state".format(noderange)):
|
||||
# gets previous (current) states
|
||||
|
||||
databynode = rsp["databynode"]
|
||||
@@ -82,7 +72,4 @@ if options.previous:
|
||||
# add dictionary to session
|
||||
session.add_precede_dict(prev)
|
||||
|
||||
def outhandler(node, res):
|
||||
for k in res[node]:
|
||||
client.cprint('{0}: {1}: {2}'.format(node, k.replace('inlet_', ''), res[node][k]))
|
||||
sys.exit(session.simple_noderange_command(noderange, '/power/{0}'.format(powurl), setstate, promptover=options.maxnodes, key='state', outhandler=outhandler))
|
||||
sys.exit(session.simple_noderange_command(noderange, '/power/state', setstate))
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2017 Lenovo
|
||||
@@ -33,12 +33,8 @@ if path.startswith('/opt'):
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage='''\n %prog <noderange>
|
||||
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,11 +43,10 @@ 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')
|
||||
exitcode |= 1
|
||||
if 'deleted' in r:
|
||||
print('{0}: deleted'.format(r['deleted']))
|
||||
sys.exit(exitcode)
|
||||
sys.exit(exitcode)
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2019 Lenovo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -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
|
||||
@@ -60,7 +56,7 @@ for node in session.read('/noderange/{0}/nodes/'.format(noderange)):
|
||||
goodNodes = allNodes - errorNodes
|
||||
|
||||
for node in goodNodes:
|
||||
print(node + ": Reseat successful")
|
||||
print node + ": Reseat successful"
|
||||
|
||||
|
||||
sys.exit(success)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -40,16 +40,8 @@ import confluent.sortutil as sortutil
|
||||
|
||||
def run():
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog <file/directorylist> <noderange>:<destination>",
|
||||
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('-l', '--loginname', type='str',
|
||||
help='Username to use when connecting, defaults to current user.')
|
||||
argparser.add_option('-s', '--substitutename',
|
||||
help='Use a different name other than the nodename for rsync')
|
||||
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
|
||||
@@ -63,39 +55,24 @@ def run():
|
||||
noderange, targpath = args[-1].split(':', 1)
|
||||
client.check_globbing(noderange)
|
||||
c = client.Command()
|
||||
cmdstr = ' '.join(args[:-1])
|
||||
cmdstr = " ".join(args[:-1])
|
||||
cmdstr = 'rsync -av --info=progress2 ' + cmdstr
|
||||
|
||||
targname = options.substitutename
|
||||
if targname and '{' in targname:
|
||||
targname = targname + ':'
|
||||
elif targname:
|
||||
targname = '{node}' + targname + ':'
|
||||
else:
|
||||
targname = '{node}:'
|
||||
|
||||
if options.loginname:
|
||||
cmdstr += ' {}@'.format(options.loginname) + targname + targpath
|
||||
else:
|
||||
cmdstr += ' {}'.format(targname) + targpath
|
||||
|
||||
cmdstr += ' {node}:' + targpath
|
||||
|
||||
currprocs = 0
|
||||
all = set([])
|
||||
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}):
|
||||
{'expression': cmdstr}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
cmd = ex[node]['value']
|
||||
if not isinstance(cmd, bytes) and not isinstance(cmd, str):
|
||||
cmd = cmd.encode('utf-8')
|
||||
cmd = ex[node]['value'].encode('utf-8')
|
||||
cmdv = shlex.split(cmd)
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
@@ -121,7 +98,7 @@ def run():
|
||||
if desc['type'] == 'stdout':
|
||||
if node not in pernodeout:
|
||||
pernodeout[node] = ''
|
||||
pernodeout[node] += client.stringify(data)
|
||||
pernodeout[node] += data
|
||||
if '\n' in pernodeout[node]:
|
||||
currout, pernodeout[node] = pernodeout[node].split('\n', 1)
|
||||
if currout:
|
||||
@@ -140,7 +117,7 @@ def run():
|
||||
output.set_output(node, 'error!')
|
||||
if node not in nodeerrs:
|
||||
nodeerrs[node] = ''
|
||||
nodeerrs[node] += client.stringify(data)
|
||||
nodeerrs[node] += data
|
||||
else:
|
||||
pop = desc['popen']
|
||||
ret = pop.poll()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -35,24 +35,17 @@ if path.startswith('/opt'):
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
devnull = None
|
||||
|
||||
|
||||
def run():
|
||||
global devnull
|
||||
devnull = open(os.devnull, 'rb')
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] <noderange> <command expression>",
|
||||
usage="Usage: %prog [options] noderange commandexpression",
|
||||
epilog="Expressions are the same as in attributes, e.g. "
|
||||
"'ipmitool -H {hardwaremanagement.manager}' will be expanded.")
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
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()
|
||||
@@ -70,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:
|
||||
@@ -78,9 +71,7 @@ def run():
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
cmd = ex[node]['value']
|
||||
if not isinstance(cmd, bytes) and not isinstance(cmd, str):
|
||||
cmd = cmd.encode('utf-8')
|
||||
cmd = ex[node]['value'].encode('utf-8')
|
||||
cmdv = shlex.split(cmd)
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
@@ -104,7 +95,6 @@ def run():
|
||||
pernodeout[node] = []
|
||||
pernodeout[node].append(data)
|
||||
else:
|
||||
data = client.stringify(data)
|
||||
if options.nonodeprefix:
|
||||
sys.stderr.write(data)
|
||||
else:
|
||||
@@ -122,7 +112,6 @@ def run():
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
if options.nonodeprefix:
|
||||
sys.stdout.write(line)
|
||||
else:
|
||||
@@ -134,14 +123,8 @@ def run():
|
||||
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
try:
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
except OSError as e:
|
||||
if e.errno == 2:
|
||||
sys.stderr.write('{0}: Unable to find local executable file "{1}"'.format(node, cmdv[0]))
|
||||
return
|
||||
raise
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015-2017 Lenovo
|
||||
@@ -32,19 +32,6 @@ path = os.path.realpath(os.path.join(path, '..', 'lib', 'python'))
|
||||
if path.startswith('/opt'):
|
||||
sys.path.append(path)
|
||||
|
||||
|
||||
class hybridcsv(csv.excel):
|
||||
lineterminator = '\n'
|
||||
|
||||
|
||||
def floatformat(num):
|
||||
fm = u'{:.5f}'.format(num).rstrip('0')
|
||||
if fm[-1:] == u'.':
|
||||
return fm + u'0'
|
||||
return fm
|
||||
|
||||
csv.register_dialect('hybrid', hybridcsv)
|
||||
|
||||
import confluent.client as client
|
||||
|
||||
sensorcollections = {
|
||||
@@ -67,7 +54,7 @@ argparser.add_option('-n', '--numreadings', type='int',
|
||||
argparser.add_option('-c', '--csv', action='store_true',
|
||||
help='Output in CSV format')
|
||||
argparser.add_option('-s', '--skipnumberless', action='store_true',
|
||||
help='Do not show non-numeric sensors')
|
||||
help='Output in CSV format')
|
||||
(options, args) = argparser.parse_args()
|
||||
repeatmode = False
|
||||
if options.interval:
|
||||
@@ -127,8 +114,7 @@ def sensorpass(showout=True, appendtime=False):
|
||||
continue
|
||||
for redundant_state in ('Non-Critical', 'Critical'):
|
||||
try:
|
||||
if sensedata.get('states', False):
|
||||
sensedata['states'].remove(redundant_state)
|
||||
sensedata['states'].remove(redundant_state)
|
||||
except ValueError:
|
||||
pass
|
||||
resultdata[node][sensedata['name']] = sensedata
|
||||
@@ -141,17 +127,16 @@ def sensorpass(showout=True, appendtime=False):
|
||||
if sensedata['value'] is None:
|
||||
showval = ''
|
||||
elif isinstance(sensedata['value'], float):
|
||||
showval = u' {0} '.format(floatformat(sensedata['value']))
|
||||
showval = u' {0:.5f} '.format(sensedata['value'])
|
||||
else:
|
||||
showval = u' {0} '.format(sensedata['value'])
|
||||
if sensedata['units'] not in (None, u''):
|
||||
showval += sensedata['units']
|
||||
if sensedata.get('health', 'ok') != 'ok':
|
||||
if sensedata['health'] != 'ok':
|
||||
datadescription = [sensedata['health']]
|
||||
else:
|
||||
datadescription = []
|
||||
if sensedata.get('states', False):
|
||||
datadescription.extend(sensedata['states'])
|
||||
datadescription.extend(sensedata['states'])
|
||||
if datadescription:
|
||||
if showval == '':
|
||||
showval += u' {0}'.format(
|
||||
@@ -162,11 +147,8 @@ def sensorpass(showout=True, appendtime=False):
|
||||
if appendtime:
|
||||
showval += ' @' + time.strftime(
|
||||
'%Y-%m-%dT%H:%M:%S')
|
||||
printval = u'{0}: {1}:{2}'.format(
|
||||
node, sensedata['name'], showval)
|
||||
if not isinstance(printval, str):
|
||||
printval = printval.encode('utf-8')
|
||||
print(printval)
|
||||
print(u'{0}: {1}:{2}'.format(
|
||||
node, sensedata['name'], showval).encode('utf-8'))
|
||||
sys.stdout.flush()
|
||||
return resultdata
|
||||
|
||||
@@ -174,7 +156,7 @@ def sensorpass(showout=True, appendtime=False):
|
||||
def format_csv(csvwriter, orderedsensors, resdata, showtime=True):
|
||||
for nodekey in resdata:
|
||||
if showtime:
|
||||
if isinstance(showtime, int):
|
||||
if showtime.is_integer():
|
||||
rowdata = [time.strftime('%Y-%m-%dT%H:%M:%S'), nodekey]
|
||||
else:
|
||||
rowdata = [time.strftime('%Y-%m-%dT%H:%M:%S.') +
|
||||
@@ -197,13 +179,10 @@ def format_csv(csvwriter, orderedsensors, resdata, showtime=True):
|
||||
datum = ','.join([datum, healthstates])
|
||||
else:
|
||||
datum = healthstates
|
||||
if isinstance(datum, float):
|
||||
datum = floatformat(datum)
|
||||
rowdata.append(datum)
|
||||
except KeyError:
|
||||
rowdata.append('N/A')
|
||||
csvwriter.writerow(rowdata)
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def main():
|
||||
@@ -217,14 +196,10 @@ def main():
|
||||
orderedsensors.append(name)
|
||||
orderedsensors.sort()
|
||||
for name in orderedsensors:
|
||||
headername = sensorheaders[name]
|
||||
if (not isinstance(headername, str) and
|
||||
not isinstance(headername, bytes)):
|
||||
headername = headername.encode('utf-8')
|
||||
headernames.append(headername)
|
||||
headernames.append(sensorheaders[name].encode('utf-8'))
|
||||
if options.csv:
|
||||
linebyline = False
|
||||
csvwriter = csv.writer(sys.stdout, dialect='hybrid')
|
||||
csvwriter = csv.writer(sys.stdout)
|
||||
if options.interval:
|
||||
csvwriter.writerow(['time', 'node'] + headernames)
|
||||
else:
|
||||
@@ -245,7 +220,7 @@ def main():
|
||||
sys.exit(exitcode)
|
||||
sleeptime = nextstart - os.times()[4]
|
||||
if sleeptime > 0:
|
||||
time.sleep(sleeptime)
|
||||
time.sleep(nextstart - os.times()[4])
|
||||
else:
|
||||
if options.csv:
|
||||
format_csv(csvwriter, orderedsensors, resdata, showtime=False)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2015 Lenovo
|
||||
@@ -32,7 +32,7 @@ if path.startswith('/opt'):
|
||||
import confluent.client as client
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage='Usage: %prog [options] <noderange> [default|cd|network|setup|hd|usb|floppy]')
|
||||
usage='Usage: %prog [options] noderange [default|cd|network|setup|hd]')
|
||||
argparser.add_option('-b', '--bios', dest='biosmode',
|
||||
action='store_true', default=False,
|
||||
help='Request BIOS style boot (rather than UEFI)')
|
||||
@@ -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))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2016-2017 Lenovo
|
||||
@@ -34,31 +34,18 @@ if path.startswith('/opt'):
|
||||
|
||||
import confluent.client as client
|
||||
import confluent.sortutil as sortutil
|
||||
devnull = None
|
||||
|
||||
|
||||
def run():
|
||||
global devnull
|
||||
devnull = open(os.devnull, 'rb')
|
||||
|
||||
argparser = optparse.OptionParser(
|
||||
usage="Usage: %prog [options] noderange commandexpression",
|
||||
epilog="Expressions are the same as in attributes, e.g. "
|
||||
"'ipmitool -H {hardwaremanagement.manager}' will be expanded.")
|
||||
argparser.add_option('-f', '-c', '--count', type='int', default=168,
|
||||
help='Number of commands to run at a time')
|
||||
argparser.add_option('-l', '--loginname', type='str',
|
||||
help='Username to use when connecting, defaults to current user.')
|
||||
argparser.add_option('-n', '--nonodeprefix', action='store_true',
|
||||
help='Do not prefix output with node names')
|
||||
argparser.add_option('-p', '--port', type='int', default=0,
|
||||
help='Specify a custom port for ssh')
|
||||
argparser.add_option('-s', '--substitutename',
|
||||
help='Use a different name other than the nodename for ssh')
|
||||
argparser.add_option('-x', '--noexpression', action='store_true',
|
||||
help='Suppress expression expansion of command')
|
||||
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()
|
||||
@@ -77,53 +64,21 @@ def run():
|
||||
pendingexecs = deque()
|
||||
exitcode = 0
|
||||
|
||||
c.stop_if_noderange_over(args[0], options.maxnodes)
|
||||
nodemap = {}
|
||||
if options.substitutename:
|
||||
subname = options.substitutename
|
||||
if '{' not in subname:
|
||||
subname = '{node}' + subname
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
|
||||
{'expression': subname}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
nodemap[node] = ex[node]['value']
|
||||
cmdparms = []
|
||||
if options.noexpression:
|
||||
for res in c.read('/noderange/{0}/nodes/'.format(args[0])):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode |= res.get('errorcode', 1)
|
||||
break
|
||||
node = res['item']['href'][:-1]
|
||||
cmdparms.append((node, cmdstr))
|
||||
else:
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
|
||||
{'expression': cmdstr}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
cmdparms.append((node, ex[node]['value']))
|
||||
for node, cmd in cmdparms:
|
||||
sshnode = nodemap.get(node, node)
|
||||
if not isinstance(cmd, str) and not isinstance(cmd, bytes):
|
||||
cmd = cmd.encode('utf-8')
|
||||
cmdv = ['ssh']
|
||||
if options.port:
|
||||
cmdv += ['-p', '{0}'.format(options.port)]
|
||||
if options.loginname:
|
||||
cmdv += ['-l', options.loginname]
|
||||
cmdv += [sshnode, cmd]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((node, cmdv))
|
||||
|
||||
for exp in c.create('/noderange/{0}/attributes/expression'.format(args[0]),
|
||||
{'expression': cmdstr}):
|
||||
if 'error' in exp:
|
||||
sys.stderr.write(exp['error'] + '\n')
|
||||
exitcode |= exp.get('errorcode', 1)
|
||||
ex = exp.get('databynode', ())
|
||||
for node in ex:
|
||||
cmd = ex[node]['value'].encode('utf-8')
|
||||
cmdv = ['ssh', node, cmd]
|
||||
if currprocs < concurrentprocs:
|
||||
currprocs += 1
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
else:
|
||||
pendingexecs.append((node, cmdv))
|
||||
if not all or exitcode:
|
||||
sys.exit(exitcode)
|
||||
rdy, _, _ = select.select(all, [], [], 10)
|
||||
@@ -141,7 +96,6 @@ def run():
|
||||
pernodeout[node] = []
|
||||
pernodeout[node].append(data)
|
||||
else:
|
||||
data = client.stringify(data)
|
||||
if options.nonodeprefix:
|
||||
sys.stderr.write(data)
|
||||
else:
|
||||
@@ -159,11 +113,9 @@ def run():
|
||||
run_cmdv(node, cmdv, all, pipedesc)
|
||||
for node in sortutil.natural_sort(pernodeout):
|
||||
for line in pernodeout[node]:
|
||||
line = client.stringify(line)
|
||||
if options.nonodeprefix:
|
||||
sys.stdout.write(line)
|
||||
else:
|
||||
line = line.lstrip('\x08')
|
||||
sys.stdout.write('{0}: {1}'.format(node, line))
|
||||
sys.stdout.flush()
|
||||
if all:
|
||||
@@ -173,7 +125,7 @@ def run():
|
||||
|
||||
def run_cmdv(node, cmdv, all, pipedesc):
|
||||
nopen = subprocess.Popen(
|
||||
cmdv, stdin=devnull, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
cmdv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
pipedesc[nopen.stdout] = {'node': node, 'popen': nopen,
|
||||
'type': 'stdout'}
|
||||
pipedesc[nopen.stderr] = {'node': node, 'popen': nopen,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2018 Lenovo
|
||||
@@ -20,10 +20,6 @@ import os
|
||||
import signal
|
||||
import sys
|
||||
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except AttributeError:
|
||||
@@ -67,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'],
|
||||
@@ -118,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)
|
||||
@@ -126,8 +115,6 @@ def createstorage(noderange, options, args):
|
||||
'name': names}
|
||||
if options.size:
|
||||
parms['size'] = options.size
|
||||
if options.stripsizes:
|
||||
parms['stripsizes'] = options.stripsizes
|
||||
_print_cfg(session.create(
|
||||
'/noderange/{0}/configuration/storage/volumes/{1}'.format(
|
||||
noderange, names), parms))
|
||||
@@ -143,7 +130,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)):
|
||||
@@ -161,27 +147,13 @@ def deletestorage(noderange, options, args):
|
||||
print(repr(rsp))
|
||||
|
||||
|
||||
def setdisk(noderange, options, args):
|
||||
if options.disks is None:
|
||||
if len(args):
|
||||
names = args.pop(0)
|
||||
else:
|
||||
sys.stderr.write('-d is required to indicate disk to modify\n')
|
||||
sys.exit(1)
|
||||
else:
|
||||
names = options.disks
|
||||
if not len(args) or args[0] not in ('hotspare', 'jbod', 'unconfigured'):
|
||||
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)
|
||||
# def setstorage(noderange, options, args):
|
||||
# pass
|
||||
|
||||
funmap = {
|
||||
'create': createstorage,
|
||||
'show': showstorage,
|
||||
'diskset': setdisk,
|
||||
# 'set': setstorage,
|
||||
'delete': deletestorage,
|
||||
'rm': deletestorage,
|
||||
}
|
||||
@@ -190,7 +162,7 @@ funmap = {
|
||||
|
||||
def main():
|
||||
argparser = OptParser(
|
||||
usage='Usage: %prog <noderange> [show|create|delete|diskset] [hotspare|jbod|unconfigured] [options]',
|
||||
usage='Usage: %prog <noderange> [show|create|delete]',
|
||||
epilog='',
|
||||
)
|
||||
argparser.add_option('-r', '--raidlevel', type='int',
|
||||
@@ -199,8 +171,6 @@ def main():
|
||||
help='Comma separated list of disks to use, or the '
|
||||
'word "rest" to indicate use of all available '
|
||||
'disks')
|
||||
argparser.add_option('-i', '--interactive', default=False, action='store_true',
|
||||
help='Confirm any potentially destructive changes interactively')
|
||||
argparser.add_option('-s', '--size', type='str',
|
||||
help='Comma separated list of sizes to use when '
|
||||
'creating volumes. The sizes may be absolute '
|
||||
@@ -213,14 +183,6 @@ def main():
|
||||
'naming volumes, or selecting a volume for '
|
||||
'delete. Default behavior is to use '
|
||||
'implementation provided default names.')
|
||||
argparser.add_option('-z', '--stripsizes', type='str',
|
||||
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')
|
||||
@@ -236,13 +198,6 @@ def main():
|
||||
except KeyError:
|
||||
argparser.print_help()
|
||||
sys.exit(1)
|
||||
if options.interactive and operation != 'show':
|
||||
showstorage(noderange, None, None)
|
||||
sys.stdout.write('The above configuration will be changed by this action, do you wish to continue (y/N): ')
|
||||
confirm = input()
|
||||
if confirm and confirm.lower()[0] != 'y':
|
||||
sys.stdout.write('Aborting\n')
|
||||
sys.exit(1)
|
||||
handler(noderange, options, args[2:])
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2018 Lenovo
|
||||
@@ -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', ()):
|
||||
@@ -116,16 +115,12 @@ class OptParser(optparse.OptionParser):
|
||||
def main():
|
||||
argparser = OptParser(
|
||||
usage="Usage: %prog <noderange> servicedata "
|
||||
"<directory or filename>",
|
||||
"<filename>",
|
||||
epilog='\nservicedata will save service data to the given '
|
||||
'directory. It is saved to the location on the relevant '
|
||||
'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()
|
||||
main()
|
||||
@@ -1,200 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# 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 argparse
|
||||
import base64
|
||||
import csv
|
||||
import io
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
try:
|
||||
import sixel
|
||||
|
||||
class DumbWriter(sixel.SixelWriter):
|
||||
def restore_position(self, output):
|
||||
return
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def iterm_draw(data):
|
||||
databuf = data.getbuffer()
|
||||
datalen = len(databuf)
|
||||
data = base64.b64encode(databuf).decode('utf8')
|
||||
sys.stdout.write(
|
||||
'\x1b]1337;File=inline=1;size={}:'.format(datalen))
|
||||
sys.stdout.write(data)
|
||||
sys.stdout.write('\a')
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def kitty_draw(data):
|
||||
data = base64.b64encode(data.getbuffer())
|
||||
while data:
|
||||
chunk, data = data[:4096], data[4096:]
|
||||
m = 1 if data else 0
|
||||
sys.stdout.write('\x1b_Ga=T,f=100,m={};'.format(m))
|
||||
sys.stdout.write(chunk.decode('utf8'))
|
||||
sys.stdout.write('\x1b\\')
|
||||
sys.stdout.flush()
|
||||
sys.stdout.write('\n')
|
||||
|
||||
|
||||
def plot(gui, output, plotdata, bins, fmt):
|
||||
import matplotlib as mpl
|
||||
if gui and mpl.get_backend() == 'agg':
|
||||
sys.stderr.write('Error: No GUI backend available and -g specified!\n')
|
||||
if not gui:
|
||||
mpl.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
n, bins, patches = plt.hist(plotdata, bins)
|
||||
plt.show()
|
||||
if not gui:
|
||||
if output:
|
||||
tdata = output
|
||||
else:
|
||||
tdata = io.BytesIO()
|
||||
plt.savefig(tdata)
|
||||
if not gui and not output:
|
||||
if fmt == 'sixel':
|
||||
writer = DumbWriter()
|
||||
writer.draw(tdata)
|
||||
elif fmt == 'kitty':
|
||||
kitty_draw(tdata)
|
||||
elif fmt == 'iterm':
|
||||
iterm_draw(tdata)
|
||||
return n, bins
|
||||
|
||||
def textplot(plotdata, bins):
|
||||
n, bins = np.histogram(plotdata, bins)
|
||||
labels = []
|
||||
for bin in bins:
|
||||
labels.append('{0:0.1f}'.format(bin))
|
||||
width = 80
|
||||
# Since this will be primarily piped into, hard to get
|
||||
# terminal width
|
||||
labelwidth = 0
|
||||
for lab in labels:
|
||||
if len(lab) > labelwidth:
|
||||
labelwidth = len(lab)
|
||||
width -= (labelwidth) + 1
|
||||
labelfmt = '{{0:>{0}s}}|'.format(labelwidth)
|
||||
maxn = 0.0
|
||||
for lgth in n:
|
||||
if lgth > maxn:
|
||||
maxn = float(lgth)
|
||||
for i in range(len(n)):
|
||||
print(labelfmt.format(labels[i]) + '=' * int(np.round((n[i]/maxn) * width)))
|
||||
return n, bins
|
||||
|
||||
histogram = False
|
||||
aparser = argparse.ArgumentParser(description='Quick access to common statistics')
|
||||
aparser.add_argument('-c', type=int, default=0, help='Column number to analyze (default is last column)')
|
||||
aparser.add_argument('-d', default=None, help='Value used to separate columns')
|
||||
aparser.add_argument('-x', default=False, action='store_true', help='Output histogram in graphical format')
|
||||
aparser.add_argument('-f', default='sixel', help='Format for histogram output (sixel/iterm/kitty)')
|
||||
aparser.add_argument('-s', default=0, help='Number of header lines to skip before processing')
|
||||
aparser.add_argument('-g', default=False, action='store_true', help='Open histogram in separate graphical window')
|
||||
aparser.add_argument('-o', default=None, help='Output histogram to the specified filename in PNG format')
|
||||
aparser.add_argument('-t', default=False, action='store_true', help='Output a histogram in text format')
|
||||
aparser.add_argument('-v', default=False, action='store_true', help='Attempt to list nodes relevant to each histogram bar (requires -s, -o, or -t)')
|
||||
aparser.add_argument('-b', type=int, default=10, help='Number of bins to use in histogram (default is 10)')
|
||||
args = aparser.parse_args(sys.argv[1:])
|
||||
plotdata = []
|
||||
headlines = int(args.s)
|
||||
while headlines >= 0:
|
||||
data = sys.stdin.readline()
|
||||
headlines -= 1
|
||||
if args.d:
|
||||
delimiter = args.d
|
||||
else:
|
||||
if '\t' in data:
|
||||
delimiter = '\t'
|
||||
elif ' ' in data:
|
||||
delimiter = ' '
|
||||
elif ',' in data:
|
||||
delimiter = ','
|
||||
else:
|
||||
delimiter = ' ' # handle single column
|
||||
data = list(csv.reader([data], delimiter=delimiter))[0]
|
||||
nodebydatum = {}
|
||||
idx = args.c - 1
|
||||
autoidx = False
|
||||
while data:
|
||||
node = None
|
||||
if ':' in data[0]:
|
||||
node, data[0] = data[0].split(':', 1)
|
||||
else:
|
||||
node = data[0]
|
||||
if idx == -1 and not autoidx:
|
||||
while not autoidx:
|
||||
try:
|
||||
datum = float(data[idx])
|
||||
except ValueError:
|
||||
idx -= 1
|
||||
continue
|
||||
except IndexError:
|
||||
sys.stderr.write('Unable to identify a numerical column\n')
|
||||
sys.exit(1)
|
||||
autoidx = True
|
||||
else:
|
||||
datum = float(data[idx])
|
||||
if node:
|
||||
if datum in nodebydatum:
|
||||
nodebydatum[datum].add(node)
|
||||
else:
|
||||
nodebydatum[datum] = set([node])
|
||||
plotdata.append(datum)
|
||||
data = sys.stdin.readline()
|
||||
data = list(csv.reader([data], delimiter=delimiter))[0]
|
||||
n = None
|
||||
if args.g or args.o or args.x:
|
||||
n, bins = plot(args.g, args.o, plotdata, bins=args.b, fmt=args.f)
|
||||
if args.t:
|
||||
n, bins = textplot(plotdata, bins=args.b)
|
||||
print('Samples: {5} Min: {3} Median: {0} Mean: {1} Max: {4} StandardDeviation: {2} Sum: {6}'.format(np.median(plotdata), np.mean(plotdata), np.std(plotdata), np.min(plotdata), np.max(plotdata), len(plotdata), np.sum(plotdata)))
|
||||
if args.v and n is not None and nodebydatum:
|
||||
print('')
|
||||
currbin = bins[0]
|
||||
bins = bins[1:]
|
||||
currbinmembers = []
|
||||
for datum in sorted(nodebydatum):
|
||||
if datum > bins[0]:
|
||||
nextbin = None
|
||||
endbin = bins[0]
|
||||
while len(bins) and bins[0] < datum:
|
||||
nextbin = bins[0]
|
||||
bins = bins[1:]
|
||||
if not nextbin:
|
||||
nextbin = np.max(plotdata)
|
||||
print('Entries between {0} and {1}'.format(currbin, endbin))
|
||||
currbin = nextbin
|
||||
print('-' * 80)
|
||||
print(','.join(sorted(currbinmembers)))
|
||||
print('')
|
||||
print('')
|
||||
currbinmembers = []
|
||||
for node in nodebydatum[datum]:
|
||||
currbinmembers.append(node)
|
||||
if currbinmembers:
|
||||
print('Entries between {0} and {1}'.format(currbin, np.max(plotdata)))
|
||||
print('-' * 80)
|
||||
print(','.join(sorted(currbinmembers)))
|
||||
print('')
|
||||
print('')
|
||||
@@ -1,7 +1,7 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2014 IBM Corporation
|
||||
# Copyright 2015-2019 Lenovo
|
||||
# Copyright 2015-2018 Lenovo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -15,10 +15,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
try:
|
||||
import anydbm as dbm
|
||||
except ImportError:
|
||||
import dbm
|
||||
import anydbm as dbm
|
||||
import csv
|
||||
import errno
|
||||
import fnmatch
|
||||
@@ -36,31 +33,9 @@ SO_PASSCRED = 16
|
||||
_attraliases = {
|
||||
'bmc': 'hardwaremanagement.manager',
|
||||
'bmcuser': 'secret.hardwaremanagementuser',
|
||||
'switchuser': 'secret.hardwaremanagementuser',
|
||||
'bmcpass': 'secret.hardwaremanagementpassword',
|
||||
'switchpass': 'secret.hardwaremanagementpassword',
|
||||
}
|
||||
|
||||
try:
|
||||
getinput = raw_input
|
||||
except NameError:
|
||||
getinput = input
|
||||
|
||||
|
||||
class NestedDict(dict):
|
||||
def __missing__(self, key):
|
||||
value = self[key] = type(self)()
|
||||
return value
|
||||
|
||||
|
||||
def stringify(instr):
|
||||
# Normalize unicode and bytes to 'str', correcting for
|
||||
# current python version
|
||||
if isinstance(instr, bytes) and not isinstance(instr, str):
|
||||
return instr.decode('utf-8')
|
||||
elif not isinstance(instr, bytes) and not isinstance(instr, str):
|
||||
return instr.encode('utf-8')
|
||||
return instr
|
||||
|
||||
class Tabulator(object):
|
||||
def __init__(self, headers):
|
||||
@@ -118,12 +93,6 @@ def printerror(res, node=None):
|
||||
exitcode = 0
|
||||
if 'errorcode' in res:
|
||||
exitcode = res['errorcode']
|
||||
for node in res.get('databynode', {}):
|
||||
exitcode = res['databynode'][node].get('errorcode', exitcode)
|
||||
if 'error' in res['databynode'][node]:
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, res['databynode'][node]['error']))
|
||||
if exitcode == 0:
|
||||
exitcode = 1
|
||||
if 'error' in res:
|
||||
if node:
|
||||
sys.stderr.write('{0}: {1}\n'.format(node, res['error']))
|
||||
@@ -135,10 +104,7 @@ def printerror(res, node=None):
|
||||
|
||||
|
||||
def cprint(txt):
|
||||
try:
|
||||
print(txt)
|
||||
except UnicodeEncodeError:
|
||||
print(txt.encode('utf8'))
|
||||
print(txt)
|
||||
sys.stdout.flush()
|
||||
|
||||
def _parseserver(string):
|
||||
@@ -161,7 +127,6 @@ class Command(object):
|
||||
self._prevkeyname = None
|
||||
self.connection = None
|
||||
self._currnoderange = None
|
||||
self.unixdomain = False
|
||||
if server is None:
|
||||
if 'CONFLUENT_HOST' in os.environ:
|
||||
self.serverloc = os.environ['CONFLUENT_HOST']
|
||||
@@ -171,13 +136,11 @@ class Command(object):
|
||||
self.serverloc = server
|
||||
if os.path.isabs(self.serverloc) and os.path.exists(self.serverloc):
|
||||
self._connect_unix()
|
||||
self.unixdomain = True
|
||||
elif self.serverloc == '/var/run/confluent/api.sock':
|
||||
raise Exception('Confluent service is not available')
|
||||
else:
|
||||
self._connect_tls()
|
||||
self.protversion = int(tlvdata.recv(self.connection).split(
|
||||
b'--')[1].strip()[1:])
|
||||
tlvdata.recv(self.connection)
|
||||
authdata = tlvdata.recv(self.connection)
|
||||
if authdata['authpassed'] == 1:
|
||||
self.authenticated = True
|
||||
@@ -188,13 +151,6 @@ class Command(object):
|
||||
passphrase = os.environ['CONFLUENT_PASSPHRASE']
|
||||
self.authenticate(username, passphrase)
|
||||
|
||||
def add_file(self, name, handle, mode):
|
||||
if self.protversion < 3:
|
||||
raise Exception('Not supported with connected confluent server')
|
||||
if not self.unixdomain:
|
||||
raise Exception('Can only add a file to a unix domain connection')
|
||||
tlvdata.send(self.connection, {'filename': name, 'mode': mode}, handle)
|
||||
|
||||
def authenticate(self, username, password):
|
||||
tlvdata.send(self.connection,
|
||||
{'username': username, 'password': password})
|
||||
@@ -208,7 +164,7 @@ class Command(object):
|
||||
def add_precede_dict(self, dict):
|
||||
self._prevdict = dict
|
||||
|
||||
def handle_results(self, ikey, rc, res, errnodes=None, outhandler=None):
|
||||
def handle_results(self, ikey, rc, res, errnodes=None):
|
||||
if 'error' in res:
|
||||
if errnodes is not None:
|
||||
errnodes.add(self._currnoderange)
|
||||
@@ -245,12 +201,10 @@ class Command(object):
|
||||
node, val, self._prevdict[node]))
|
||||
else:
|
||||
cprint('{0}: {1}'.format(node, val))
|
||||
elif outhandler:
|
||||
outhandler(node, res)
|
||||
return rc
|
||||
|
||||
def simple_noderange_command(self, noderange, resource, input=None,
|
||||
key=None, errnodes=None, promptover=None, outhandler=None, **kwargs):
|
||||
key=None, errnodes=None, **kwargs):
|
||||
try:
|
||||
self._currnoderange = noderange
|
||||
rc = 0
|
||||
@@ -264,45 +218,17 @@ class Command(object):
|
||||
if input is None:
|
||||
for res in self.read('/noderange/{0}/{1}'.format(
|
||||
noderange, resource)):
|
||||
rc = self.handle_results(ikey, rc, res, errnodes, outhandler)
|
||||
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):
|
||||
rc = self.handle_results(ikey, rc, res, errnodes, outhandler)
|
||||
rc = self.handle_results(ikey, rc, res, errnodes)
|
||||
self._currnoderange = None
|
||||
return rc
|
||||
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 = getinput('Command is about to affect node {0}, continue (y/n)? '.format(nodename))
|
||||
else:
|
||||
p = getinput('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:
|
||||
@@ -396,14 +322,13 @@ class Command(object):
|
||||
if knownhosts:
|
||||
certdata = self.connection.getpeercert(binary_form=True)
|
||||
fingerprint = 'sha512$' + hashlib.sha512(certdata).hexdigest()
|
||||
fingerprint = fingerprint.encode('utf-8')
|
||||
hostid = '@'.join((port, server))
|
||||
khf = dbm.open(os.path.join(clientcfgdir, "knownhosts"), 'c', 384)
|
||||
if hostid in khf:
|
||||
if fingerprint == khf[hostid]:
|
||||
return
|
||||
else:
|
||||
replace = getinput(
|
||||
replace = raw_input(
|
||||
"MISMATCHED CERTIFICATE DATA, ACCEPT NEW? (y/n):")
|
||||
if replace not in ('y', 'Y'):
|
||||
raise Exception("BAD CERTIFICATE")
|
||||
@@ -436,7 +361,7 @@ def send_request(operation, path, server, parameters=None):
|
||||
result = tlvdata.recv(server)
|
||||
|
||||
|
||||
def attrrequested(attr, attrlist, seenattributes, node=None):
|
||||
def attrrequested(attr, attrlist, seenattributes):
|
||||
for candidate in attrlist:
|
||||
truename = candidate
|
||||
if candidate.startswith('hm'):
|
||||
@@ -444,16 +369,10 @@ def attrrequested(attr, attrlist, seenattributes, node=None):
|
||||
if candidate in _attraliases:
|
||||
candidate = _attraliases[candidate]
|
||||
if fnmatch.fnmatch(attr.lower(), candidate.lower()):
|
||||
if node is None:
|
||||
seenattributes.add(truename)
|
||||
else:
|
||||
seenattributes[node][truename] = True
|
||||
seenattributes.add(truename)
|
||||
return True
|
||||
elif attr.lower().startswith(candidate.lower() + '.'):
|
||||
if node is None:
|
||||
seenattributes.add(truename)
|
||||
else:
|
||||
seenattributes[node][truename] = 1
|
||||
seenattributes.add(truename)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -462,45 +381,39 @@ 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):
|
||||
def print_attrib_path(path, session, requestargs, options, rename=None):
|
||||
exitcode = 0
|
||||
seenattributes = NestedDict()
|
||||
allnodes = set([])
|
||||
seenattributes = set([])
|
||||
for res in session.read(path):
|
||||
if 'error' in res:
|
||||
sys.stderr.write(res['error'] + '\n')
|
||||
exitcode = 1
|
||||
continue
|
||||
for node in sorted(res['databynode']):
|
||||
allnodes.add(node)
|
||||
for attr, val in sorted(res['databynode'][node].items(), key=_sort_attrib):
|
||||
for attr, val in sorted(
|
||||
res['databynode'][node].items(),
|
||||
key=lambda (k, v): v.get('sortid', k) if isinstance(v, dict) else k):
|
||||
if attr == 'error':
|
||||
sys.stderr.write('{0}: Error: {1}\n'.format(node, val))
|
||||
continue
|
||||
if attr == 'errorcode':
|
||||
exitcode |= val
|
||||
continue
|
||||
seenattributes[node][attr] = True
|
||||
seenattributes.add(attr)
|
||||
if rename:
|
||||
printattr = rename.get(attr, attr)
|
||||
else:
|
||||
printattr = attr
|
||||
if attrprefix:
|
||||
printattr = attrprefix + printattr
|
||||
currattr = res['databynode'][node][attr]
|
||||
if show_attr(attr, requestargs, seenattributes, options, node):
|
||||
if show_attr(attr, requestargs, seenattributes, options):
|
||||
if 'value' in currattr:
|
||||
if currattr['value'] is not None:
|
||||
val = currattr['value']
|
||||
if isinstance(val, list):
|
||||
val = ','.join(val)
|
||||
attrout = '{0}: {1}: {2}'.format(
|
||||
node, printattr, val).strip()
|
||||
node, printattr, val)
|
||||
else:
|
||||
attrout = '{0}: {1}:'.format(node, printattr)
|
||||
elif 'isset' in currattr:
|
||||
@@ -509,7 +422,7 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
|
||||
printattr)
|
||||
else:
|
||||
attrout = '{0}: {1}:'.format(node, printattr)
|
||||
elif isinstance(currattr, dict) and 'broken' in currattr:
|
||||
elif 'broken' in currattr:
|
||||
attrout = '{0}: {1}: *ERROR* BROKEN EXPRESSION: ' \
|
||||
'{2}'.format(node, printattr,
|
||||
currattr['broken'])
|
||||
@@ -526,7 +439,7 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
|
||||
blame = options.blame
|
||||
except AttributeError:
|
||||
blame = False
|
||||
if blame or (isinstance(currattr, dict) and 'broken' in currattr):
|
||||
if blame or 'broken' in currattr:
|
||||
blamedata = []
|
||||
if 'inheritedfrom' in currattr:
|
||||
blamedata.append('inherited from group {0}'.format(
|
||||
@@ -565,48 +478,29 @@ def print_attrib_path(path, session, requestargs, options, rename=None, attrpref
|
||||
details = False
|
||||
if details:
|
||||
if currattr.get('help', None):
|
||||
attrout += u' (Help: {0})'.format(
|
||||
currattr['help'])
|
||||
attrout += ' (Help: {0})'.format(
|
||||
currattr['help'].encode('utf-8'))
|
||||
if currattr.get('possible', None):
|
||||
try:
|
||||
attrout += u' (Choices: {0})'.format(
|
||||
','.join(currattr['possible']))
|
||||
except TypeError:
|
||||
pass
|
||||
attrout += ' (Choices: {0})'.format(
|
||||
','.join(currattr['possible']))
|
||||
cprint(attrout)
|
||||
somematched = set([])
|
||||
printmissing = set([])
|
||||
badnodes = NestedDict()
|
||||
if not exitcode:
|
||||
if requestargs:
|
||||
for attr in requestargs:
|
||||
for node in allnodes:
|
||||
if attr in seenattributes[node]:
|
||||
somematched.add(attr)
|
||||
else:
|
||||
badnodes[node][attr] = True
|
||||
exitcode = 1
|
||||
for node in sortutil.natural_sort(badnodes):
|
||||
for attr in badnodes[node]:
|
||||
if attr in somematched:
|
||||
sys.stderr.write(
|
||||
'Error: {0} matches no valid value for {1}\n'.format(
|
||||
attr, node))
|
||||
else:
|
||||
printmissing.add(attr)
|
||||
for missing in printmissing:
|
||||
sys.stderr.write('Error: {0} not a valid attribute\n'.format(missing))
|
||||
if attr not in seenattributes:
|
||||
sys.stderr.write('Error: {0} not a valid attribute\n'.format(attr))
|
||||
exitcode = 1
|
||||
return exitcode
|
||||
|
||||
|
||||
def show_attr(attr, requestargs, seenattributes, options, node):
|
||||
def show_attr(attr, requestargs, seenattributes, options):
|
||||
try:
|
||||
reverse = options.exclude
|
||||
except AttributeError:
|
||||
reverse = False
|
||||
if requestargs is None or requestargs == []:
|
||||
return True
|
||||
processattr = attrrequested(attr, requestargs, seenattributes, node)
|
||||
processattr = attrrequested(attr, requestargs, seenattributes)
|
||||
if reverse:
|
||||
processattr = not processattr
|
||||
return processattr
|
||||
@@ -635,7 +529,7 @@ def printgroupattributes(session, requestargs, showtype, nodetype, noderange, op
|
||||
attrout = '{0}: {1}: ********'.format(noderange, attr)
|
||||
else:
|
||||
attrout = '{0}: {1}:'.format(noderange, attr)
|
||||
elif isinstance(currattr, dict) and 'broken' in currattr:
|
||||
elif 'broken' in currattr:
|
||||
attrout = '{0}: {1}: *ERROR* BROKEN EXPRESSION: ' \
|
||||
'{2}'.format(noderange, attr,
|
||||
currattr['broken'])
|
||||
@@ -668,9 +562,6 @@ def updateattrib(session, updateargs, nodetype, noderange, options, dictassign=N
|
||||
for attrib in updateargs[1:]:
|
||||
keydata[attrib] = None
|
||||
for res in session.update(targpath, keydata):
|
||||
for node in res.get('databynode', {}):
|
||||
for warnmsg in res['databynode'][node].get('_warnings', []):
|
||||
sys.stderr.write('Warning: ' + warnmsg + '\n')
|
||||
if 'error' in res:
|
||||
if 'errorcode' in res:
|
||||
exitcode = res['errorcode']
|
||||
@@ -705,14 +596,6 @@ def updateattrib(session, updateargs, nodetype, noderange, options, dictassign=N
|
||||
noderange, 'attributes/all', dictassign[key], key)
|
||||
else:
|
||||
if "=" in updateargs[1]:
|
||||
update_ready = True
|
||||
for arg in updateargs[1:]:
|
||||
if not '=' in arg:
|
||||
update_ready = False
|
||||
exitcode = 1
|
||||
if not update_ready:
|
||||
sys.stderr.write('Error: {0} Can not set and read at the same time!\n'.format(str(updateargs[1:])))
|
||||
sys.exit(exitcode)
|
||||
try:
|
||||
for val in updateargs[1:]:
|
||||
val = val.split('=', 1)
|
||||
@@ -769,4 +652,4 @@ def check_globbing(noderange):
|
||||
'bash or change directories such that there is no filename '
|
||||
'that would conflict.'
|
||||
'\n'.format(noderange))
|
||||
sys.exit(1)
|
||||
sys.exit(1)
|
||||
@@ -1,342 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
import collections
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import fcntl
|
||||
import select
|
||||
import termios
|
||||
import tty
|
||||
|
||||
def writeout(data):
|
||||
if isinstance(data, str) and not isinstance(data, bytes):
|
||||
data = data.encode('utf8')
|
||||
try:
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
sys.stdout.buffer.write(data)
|
||||
else:
|
||||
sys.stdout.write(data)
|
||||
except IOError:
|
||||
time.sleep(0.1)
|
||||
pass
|
||||
|
||||
|
||||
class LogReplay(object):
|
||||
def __init__(self, logfile, cblfile):
|
||||
self.bin = open(cblfile, 'rb')
|
||||
self.txt = open(logfile, 'rb')
|
||||
self.cleardata = []
|
||||
self.clearidx = 0
|
||||
self.pendingdata = collections.deque([])
|
||||
self.priordata = collections.deque([])
|
||||
self.laststamp = None
|
||||
self.needclear = False
|
||||
self.lasttxt = ''
|
||||
|
||||
def _rewind(self, datasize=None):
|
||||
curroffset = self.bin.tell() - 16
|
||||
if self.cleardata and self.clearidx > 1:
|
||||
self.clearidx -= 1
|
||||
priordata = self.cleardata[self.clearidx - 1]
|
||||
return curroffset, priordata
|
||||
self.cleardata = []
|
||||
self.clearidx = 0
|
||||
newoffset = curroffset - 32
|
||||
if newoffset < 0: #TODO: Follow a log roll
|
||||
newoffset = 0
|
||||
if datasize:
|
||||
while datasize > 0 and newoffset > 0:
|
||||
self.bin.seek(newoffset)
|
||||
tmprec = self.bin.read(16)
|
||||
newoffset -= 32
|
||||
tmprec = struct.unpack('!BBIHIBBH', tmprec)
|
||||
if tmprec[1] == 2:
|
||||
datasize -= tmprec[3]
|
||||
if newoffset >= 0:
|
||||
self.bin.seek(newoffset)
|
||||
return curroffset, None
|
||||
|
||||
def debuginfo(self):
|
||||
return '{0}, {1}'.format(self.bin.tell(), self.clearidx)
|
||||
|
||||
def get_output(self, reverse=False):
|
||||
endoffset = None
|
||||
output = b''
|
||||
if reverse: # Forget the uncommited future, if present
|
||||
output += b'\x1b[2J\x1b[H'
|
||||
endoffset, priordata = self._rewind(4096)
|
||||
if priordata is not None:
|
||||
return priordata, 1
|
||||
elif self.needclear:
|
||||
output += b'\x1b[2J\x1b[H'
|
||||
self.needclear = False
|
||||
if self.cleardata and self.clearidx < len(self.cleardata):
|
||||
datachunk = self.cleardata[self.clearidx]
|
||||
self.clearidx += 1
|
||||
return datachunk, 1
|
||||
self.cleardata = []
|
||||
self.clearidx = 0
|
||||
while (not reverse) or (self.bin.tell() < endoffset):
|
||||
record = self.bin.read(16)
|
||||
if not record:
|
||||
return b'', 0
|
||||
record = struct.unpack('!BBIHIBBH', record)
|
||||
if record[0] > 16:
|
||||
# Unsupported record, skip
|
||||
self.bin.seek(record[0] - 16, 1)
|
||||
continue
|
||||
type = record[1]
|
||||
offset = record[2]
|
||||
size = record[3]
|
||||
evtdata = record[5]
|
||||
auxdata = record[6]
|
||||
if type == 3:
|
||||
#TODO: provide data for status bar
|
||||
continue
|
||||
elif type == 2:
|
||||
self.laststamp = record[4]
|
||||
self.txt.seek(offset)
|
||||
txtout = self.txt.read(size)
|
||||
if reverse and self.bin.tell() < endoffset:
|
||||
output += txtout
|
||||
continue
|
||||
self.paginate(txtout)
|
||||
if self.cleardata:
|
||||
self.clearidx = 0
|
||||
if not self.cleardata[0]:
|
||||
self.cleardata = self.cleardata[1:]
|
||||
if self.cleardata:
|
||||
if reverse:
|
||||
output = self.cleardata[-1]
|
||||
self.clearidx = len(self.cleardata)
|
||||
else:
|
||||
output += self.cleardata[0]
|
||||
self.clearidx = 1
|
||||
else:
|
||||
output += txtout
|
||||
break
|
||||
if endoffset is not None and endoffset >= 0:
|
||||
self.bin.seek(endoffset)
|
||||
self.lasttxt = output
|
||||
return output, 1
|
||||
|
||||
def paginate(self, txtout):
|
||||
cleardata = [txtout]
|
||||
nextcleardata = []
|
||||
for sep in (b'\x1b[2J', b'\x1b[H\x1b[J'):
|
||||
replacementcleardata = []
|
||||
for txtout in cleardata:
|
||||
nextcleardata = txtout.split(sep)
|
||||
if len(nextcleardata) > 1:
|
||||
for idx in range(1, len(nextcleardata)):
|
||||
nextcleardata[idx] = sep + nextcleardata[idx]
|
||||
replacementcleardata.extend(nextcleardata)
|
||||
nextcleardata = []
|
||||
cleardata = replacementcleardata
|
||||
if len(cleardata) > 1:
|
||||
self.cleardata = cleardata
|
||||
return
|
||||
|
||||
def begin(self):
|
||||
self.needclear = True
|
||||
self.bin.seek(0)
|
||||
|
||||
def end(self):
|
||||
self.bin.seek(0, 2)
|
||||
|
||||
def search(self, searchstr, skipfirst=False):
|
||||
overlap = len(searchstr)
|
||||
firstoffset = self.bin.tell()
|
||||
lastoffset = firstoffset
|
||||
output = self.lasttxt
|
||||
while searchstr not in output:
|
||||
output, delay = self.get_output()
|
||||
if not self.cleardata and lastoffset == self.bin.tell():
|
||||
self.bin.seek(firstoffset)
|
||||
return b'', 0
|
||||
lastoffset = self.bin.tell()
|
||||
if skipfirst:
|
||||
output, delay = self.get_output()
|
||||
while searchstr not in output:
|
||||
output, delay = self.get_output()
|
||||
if not self.cleardata and lastoffset == self.bin.tell():
|
||||
self.bin.seek(firstoffset)
|
||||
return b'', 0
|
||||
lastoffset = self.bin.tell()
|
||||
clear = b'\x1b[2J\x1b[H'
|
||||
txtchunks = self.lasttxt.split(searchstr)
|
||||
output = clear + txtchunks[0]
|
||||
txtchunks = txtchunks[1:]
|
||||
while txtchunks:
|
||||
output += b'\x1b[7m' + searchstr + b'\x1b[27m' + txtchunks[0]
|
||||
txtchunks = txtchunks[1:]
|
||||
return output, 0
|
||||
|
||||
|
||||
def _replay_to_console(txtfile, binfile):
|
||||
replay = LogReplay(txtfile, binfile)
|
||||
oldtcattr = termios.tcgetattr(sys.stdin.fileno())
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
||||
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl | os.O_NONBLOCK)
|
||||
reverse = False
|
||||
skipnext = False
|
||||
quitit = False
|
||||
searchstr = None
|
||||
writeout('\x1b[2J\x1b[;H')
|
||||
prepend = ''
|
||||
try:
|
||||
while not quitit:
|
||||
if not skipnext:
|
||||
newdata, delay = replay.get_output(reverse)
|
||||
skipnext = False
|
||||
reverse = False
|
||||
if newdata:
|
||||
if prepend:
|
||||
newdata = prepend + newdata
|
||||
prepend = b''
|
||||
writeout(newdata)
|
||||
newdata = ''
|
||||
writeout('\x1b]0;[Time: {0}]\x07'.format(
|
||||
time.strftime('%m/%d %H:%M:%S', time.localtime(replay.laststamp))))
|
||||
try:
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
sys.stdout.buffer.flush()
|
||||
else:
|
||||
sys.stdout.flush()
|
||||
except IOError:
|
||||
pass
|
||||
while True:
|
||||
select.select((sys.stdin,), (), (), 86400)
|
||||
myinput = sys.stdin.read()
|
||||
if myinput.startswith('\x1b[C') or myinput.startswith('\x1bOC') or myinput == '\r': # right
|
||||
break
|
||||
elif myinput.startswith('\x1b[D') or myinput.startswith('\x1bOD') or myinput == 'y': # left
|
||||
writeout('\x1b[2J\x1b[;H')
|
||||
reverse = True
|
||||
break
|
||||
elif myinput == 'G' or myinput.startswith('\x1b[F'):
|
||||
replay.end()
|
||||
reverse = True
|
||||
break
|
||||
elif myinput == 'g' or myinput.startswith('\x1b[H'):
|
||||
replay.begin()
|
||||
break
|
||||
elif myinput.lower() == 'q' or myinput == '\x03':
|
||||
quitit = True
|
||||
break
|
||||
elif myinput.lower() == 'd':
|
||||
writeout('\x1b];{0}\x07'.format(replay.debuginfo()))
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
sys.stdout.buffer.flush()
|
||||
else:
|
||||
sys.stdout.flush()
|
||||
elif myinput.lower() == '/':
|
||||
sys.stdout.write('\x1b7\x1b[99999;0H\x1b[2K')
|
||||
searchstr = ''
|
||||
nxtchr = '/'
|
||||
while '\r' not in searchstr:
|
||||
sys.stdout.write(nxtchr)
|
||||
sys.stdout.flush()
|
||||
select.select((sys.stdin,), (), (), 86400)
|
||||
nxtchr = sys.stdin.read(1)
|
||||
if nxtchr in ('\x08', '\x7f'):
|
||||
searchstr = searchstr[:-1]
|
||||
nxtchr = '\x08 \x08'
|
||||
else:
|
||||
searchstr += nxtchr
|
||||
if not isinstance(searchstr, bytes):
|
||||
searchstr = searchstr.encode('utf8')
|
||||
searchstr = searchstr[:-1]
|
||||
newdata, delay = replay.search(searchstr)
|
||||
if not newdata:
|
||||
sys.stdout.write('\x1b[1K\rNo match found')
|
||||
prepend = b'\x1b[1K\x1b8'
|
||||
else:
|
||||
sys.stdout.write('\x1b[1K\x1b8')
|
||||
sys.stdout.flush()
|
||||
skipnext = True
|
||||
break
|
||||
elif myinput.lower() == 'n' and searchstr:
|
||||
newdata, delay = replay.search(searchstr, True)
|
||||
if not newdata:
|
||||
sys.stdout.write('\x1b7\x1b[99999;0H\x1b[2KNo more matches found')
|
||||
sys.stdout.flush()
|
||||
prepend = b'\x1b[1K\x1b8'
|
||||
skipnext = True
|
||||
break
|
||||
else:
|
||||
pass # print(repr(myinput))
|
||||
except Exception:
|
||||
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
||||
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
|
||||
writeout('\x1b[m\x1b[?25h\n')
|
||||
raise
|
||||
currfl = fcntl.fcntl(sys.stdin.fileno(), fcntl.F_GETFL)
|
||||
fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, currfl ^ os.O_NONBLOCK)
|
||||
termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr)
|
||||
writeout('\x1b[m\x1b[?25h\n')
|
||||
|
||||
def replay_to_console(txtfile):
|
||||
if os.path.exists(txtfile + '.cbl'):
|
||||
binfile = txtfile + '.cbl'
|
||||
elif '.' not in txtfile:
|
||||
if '/' not in txtfile:
|
||||
txtfile = os.getcwd() + '/' + txtfile
|
||||
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(txtfile + '.cbl'))
|
||||
sys.exit(1)
|
||||
else:
|
||||
fileparts = txtfile.split('.')
|
||||
prefix = '.'.join(fileparts[:-1])
|
||||
binfile = prefix + '.cbl.' + fileparts[-1]
|
||||
if not os.path.exists(binfile):
|
||||
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(binfile))
|
||||
sys.exit(1)
|
||||
_replay_to_console(txtfile, binfile)
|
||||
|
||||
def dump_to_console(txtfile):
|
||||
if os.path.exists(txtfile + '.cbl'):
|
||||
binfile = txtfile + '.cbl'
|
||||
elif '.' not in txtfile:
|
||||
if '/' not in txtfile:
|
||||
txtfile = os.getcwd() + '/' + txtfile
|
||||
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(txtfile + '.cbl'))
|
||||
sys.exit(1)
|
||||
else:
|
||||
fileparts = txtfile.split('.')
|
||||
prefix = '.'.join(fileparts[:-1])
|
||||
binfile = prefix + '.cbl.' + fileparts[-1]
|
||||
if not os.path.exists(binfile):
|
||||
sys.stderr.write('Unable to locate cbl file: "{0}"\n'.format(binfile))
|
||||
sys.exit(1)
|
||||
replay = LogReplay(txtfile, binfile)
|
||||
quitit = False
|
||||
writeout('\x1b[2J\x1b[;H')
|
||||
prepend = ''
|
||||
try:
|
||||
while not quitit:
|
||||
newdata, delay = replay.get_output(False)
|
||||
if newdata:
|
||||
if prepend:
|
||||
newdata = prepend + newdata
|
||||
prepend = b''
|
||||
writeout(newdata.replace(b'\r\n', '\r\n[ {0} ] '.format(time.strftime('%m/%d %H:%M:%S', time.localtime(replay.laststamp))).encode('utf8')))
|
||||
newdata = ''
|
||||
try:
|
||||
if hasattr(sys.stdout, 'buffer'):
|
||||
sys.stdout.buffer.flush()
|
||||
else:
|
||||
sys.stdout.flush()
|
||||
except IOError:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
except Exception:
|
||||
writeout('\x1b[m\x1b[?25h\n')
|
||||
raise
|
||||
writeout('\x1b[m\x1b[?25h\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
replay_to_console(sys.argv[1])
|
||||
@@ -17,9 +17,9 @@ import sys
|
||||
import struct
|
||||
import termios
|
||||
|
||||
def get_screengeom():
|
||||
def get_screenwidth():
|
||||
return struct.unpack('hh', fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ,
|
||||
b'....'))
|
||||
b'....'))[1]
|
||||
class ScreenPrinter(object):
|
||||
|
||||
def __init__(self, noderange, client, textlen=4):
|
||||
@@ -29,9 +29,6 @@ class ScreenPrinter(object):
|
||||
self.client = client
|
||||
self.nodeoutput = {}
|
||||
self.nodelist = []
|
||||
self.nodepos = {}
|
||||
self.lastrows = 0
|
||||
self.fieldchanged = False
|
||||
maxlen = 0
|
||||
for ans in self.client.read('/noderange/{0}/nodes/'.format(noderange)):
|
||||
if 'error' in ans:
|
||||
@@ -47,68 +44,33 @@ class ScreenPrinter(object):
|
||||
self.fieldwidth = maxlen + textlen + 1 # 1 for column
|
||||
|
||||
def set_output(self, node, text):
|
||||
if self.nodeoutput.get(node, None) == text:
|
||||
if self.nodeoutput[node] == text:
|
||||
return
|
||||
self.nodeoutput[node] = text
|
||||
if len(text) >= self.textlen:
|
||||
self.textlen = len(text) + 1
|
||||
self.fieldwidth = self.textlen + self.nodenamelen + 1
|
||||
self.fieldchanged = True
|
||||
self.drawscreen(node)
|
||||
self.drawscreen()
|
||||
|
||||
def drawscreen(self, node=None):
|
||||
def drawscreen(self):
|
||||
if self.squeeze:
|
||||
currheight, currwidth = get_screengeom()
|
||||
currheight -= 2
|
||||
if currheight < 1:
|
||||
currheight = 1
|
||||
currwidth = get_screenwidth()
|
||||
numfields = currwidth // self.fieldwidth
|
||||
fieldformat = '{{0:>{0}}}:{{1:{1}}}'.format(self.nodenamelen,
|
||||
self.textlen)
|
||||
#sys.stdout.write('\x1b[2J\x1b[;H') # clear screen
|
||||
if len(self.nodelist) < (numfields * currheight):
|
||||
numfields = len(self.nodelist) // currheight + 1
|
||||
sys.stdout.write('\x1b[2J\x1b[;H') # clear screen
|
||||
else:
|
||||
numfields = 1
|
||||
fieldformat = '{0}: {1}'
|
||||
if self.squeeze:
|
||||
columns = [self.nodelist[x:x+currheight] for x in range(0, len(self.nodelist), currheight)]
|
||||
if self.lastrows:
|
||||
sys.stdout.write('\x1b[{0}A'.format(self.lastrows))
|
||||
if node and self.lastrows == len(columns[0]) and not self.fieldchanged:
|
||||
targline, targcol = self.nodepos[node]
|
||||
if targline:
|
||||
sys.stdout.write('\x1b[{0}B'.format(targline))
|
||||
if targcol:
|
||||
sys.stdout.write('\x1b[{0}C'.format(targcol))
|
||||
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
|
||||
sys.stdout.write('\r\x1b[{0}B'.format(self.lastrows - targline))
|
||||
else:
|
||||
self.lastrows = 0
|
||||
self.fieldchanged = False
|
||||
column = 0
|
||||
for currow in range(0, len(columns[0])):
|
||||
sys.stdout.write('\x1b[2K')
|
||||
for col in columns:
|
||||
try:
|
||||
node = col[currow]
|
||||
self.nodepos[node] = (currow, column)
|
||||
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
|
||||
column += len(fieldformat.format(node, self.nodeoutput[node]))
|
||||
except IndexError:
|
||||
break
|
||||
sys.stdout.write('\n')
|
||||
column = 0
|
||||
self.lastrows += 1
|
||||
sys.stdout.write('\x1b[J')
|
||||
else:
|
||||
if node:
|
||||
nodes = [node]
|
||||
else:
|
||||
nodes = self.nodelist
|
||||
for node in nodes:
|
||||
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
|
||||
currfields = 0
|
||||
for node in self.nodelist:
|
||||
if currfields >= numfields:
|
||||
sys.stdout.write('\n')
|
||||
currfields = 1
|
||||
else:
|
||||
currfields += 1
|
||||
sys.stdout.write(fieldformat.format(node, self.nodeoutput[node]))
|
||||
sys.stdout.write('\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
@@ -118,8 +80,8 @@ class ScreenPrinter(object):
|
||||
if __name__ == '__main__':
|
||||
import confluent.client as client
|
||||
c = client.Command()
|
||||
p = ScreenPrinter('d1-d12', c)
|
||||
p.set_output('d3', 'Upload: 67%')
|
||||
p = ScreenPrinter('n1-n13', c)
|
||||
p.set_output('n3', 'Upload: 67%')
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -53,16 +53,13 @@ class TermHandler(object):
|
||||
connection = None
|
||||
try:
|
||||
connection, address = self.socket.accept()
|
||||
connection.sendall(b"confetty control v1--\n")
|
||||
connection.sendall("confetty control v1--\n")
|
||||
cmd = connection.recv(8)
|
||||
if b'GETWINID' == cmd:
|
||||
winid = os.environ['WINDOWID']
|
||||
if not isinstance(winid, bytes):
|
||||
winid = winid.encode('utf8')
|
||||
connection.sendall(winid)
|
||||
if 'GETWINID' == cmd:
|
||||
connection.sendall(os.environ['WINDOWID'])
|
||||
connection.close()
|
||||
except BaseException:
|
||||
pass
|
||||
finally:
|
||||
if connection is not None:
|
||||
connection.close()
|
||||
connection.close()
|
||||
@@ -98,24 +98,17 @@ class GroupedData(object):
|
||||
self.byoutput = {}
|
||||
self.header = {}
|
||||
self.client = confluentconnection
|
||||
self.detectedpad = None
|
||||
|
||||
def generate_byoutput(self):
|
||||
self.byoutput = {}
|
||||
thepad = self.detectedpad if self.detectedpad else ''
|
||||
for n in self.bynode:
|
||||
output = ''
|
||||
for ln in self.bynode[n]:
|
||||
output += ln.replace(thepad, '', 1) + '\n'
|
||||
output = '\n'.join(self.bynode[n])
|
||||
if output not in self.byoutput:
|
||||
self.byoutput[output] = set([n])
|
||||
else:
|
||||
self.byoutput[output].add(n)
|
||||
|
||||
def add_line(self, node, line):
|
||||
wspc = re.search(r'^\s*', line).group()
|
||||
if self.detectedpad is None or len(wspc) < len(self.detectedpad):
|
||||
self.detectedpad = wspc
|
||||
if node not in self.bynode:
|
||||
self.bynode[node] = [line]
|
||||
else:
|
||||
@@ -171,9 +164,7 @@ class GroupedData(object):
|
||||
self.byoutput[outdata])))
|
||||
currout += '\n====================================\n'
|
||||
currout += outdata
|
||||
if currout[-1] != '\n':
|
||||
currout += '\n'
|
||||
currout += '\n'
|
||||
currout += '\n\n'
|
||||
output.write(currout)
|
||||
output.flush()
|
||||
|
||||
@@ -213,9 +204,7 @@ class GroupedData(object):
|
||||
else:
|
||||
currout += '\n'.join(colordiff(modaloutput.split('\n'),
|
||||
outdata.split('\n')))
|
||||
if currout[-1] != '\n':
|
||||
currout += '\n'
|
||||
currout += '\n'
|
||||
currout += '\n\n'
|
||||
if reverse:
|
||||
revoutput.append(currout)
|
||||
else:
|
||||
@@ -230,4 +219,4 @@ if __name__ == '__main__':
|
||||
if not line:
|
||||
continue
|
||||
groupoutput.add_line(*line.split(': ', 1))
|
||||
groupoutput.print_deviants()
|
||||
groupoutput.print_deviants()
|
||||
@@ -17,4 +17,4 @@
|
||||
|
||||
|
||||
class Types(object):
|
||||
text, json, filehandle = range(3)
|
||||
text, json = range(2)
|
||||
|
||||
@@ -15,19 +15,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import array
|
||||
import ctypes
|
||||
import ctypes.util
|
||||
import confluent.tlv as tlv
|
||||
try:
|
||||
import eventlet.green.socket as socket
|
||||
import eventlet.green.select as select
|
||||
except ImportError:
|
||||
import socket
|
||||
import select
|
||||
from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
import struct
|
||||
|
||||
try:
|
||||
@@ -35,77 +25,6 @@ try:
|
||||
except NameError:
|
||||
unicode = str
|
||||
|
||||
try:
|
||||
range = xrange
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
class iovec(ctypes.Structure): # from uio.h
|
||||
_fields_ = [('iov_base', ctypes.c_void_p),
|
||||
('iov_len', ctypes.c_size_t)]
|
||||
|
||||
|
||||
iovec_ptr = ctypes.POINTER(iovec)
|
||||
|
||||
|
||||
class cmsghdr(ctypes.Structure): # also from bits/socket.h
|
||||
_fields_ = [('cmsg_len', ctypes.c_size_t),
|
||||
('cmsg_level', ctypes.c_int),
|
||||
('cmsg_type', ctypes.c_int)]
|
||||
|
||||
@classmethod
|
||||
def init_data(cls, cmsg_len, cmsg_level, cmsg_type, cmsg_data):
|
||||
Data = ctypes.c_ubyte * ctypes.sizeof(cmsg_data)
|
||||
class _flexhdr(ctypes.Structure):
|
||||
_fields_ = cls._fields_ + [('cmsg_data', Data)]
|
||||
|
||||
datab = Data(*bytearray(cmsg_data))
|
||||
return _flexhdr(cmsg_len=cmsg_len, cmsg_level=cmsg_level,
|
||||
cmsg_type=cmsg_type, cmsg_data=datab)
|
||||
|
||||
|
||||
def CMSG_LEN(length):
|
||||
sizeof_cmshdr = ctypes.sizeof(cmsghdr)
|
||||
return ctypes.c_size_t(CMSG_ALIGN(sizeof_cmshdr).value + length)
|
||||
|
||||
|
||||
SCM_RIGHTS = 1
|
||||
|
||||
|
||||
class msghdr(ctypes.Structure): # from bits/socket.h
|
||||
_fields_ = [('msg_name', ctypes.c_void_p),
|
||||
('msg_namelen', ctypes.c_uint),
|
||||
('msg_iov', ctypes.POINTER(iovec)),
|
||||
('msg_iovlen', ctypes.c_size_t),
|
||||
('msg_control', ctypes.c_void_p),
|
||||
('msg_controllen', ctypes.c_size_t),
|
||||
('msg_flags', ctypes.c_int)]
|
||||
|
||||
|
||||
def CMSG_ALIGN(length): # bits/socket.h
|
||||
ret = (length + ctypes.sizeof(ctypes.c_size_t) - 1
|
||||
& ~(ctypes.sizeof(ctypes.c_size_t) - 1))
|
||||
return ctypes.c_size_t(ret)
|
||||
|
||||
|
||||
def CMSG_SPACE(length): # bits/socket.h
|
||||
ret = CMSG_ALIGN(length).value + CMSG_ALIGN(ctypes.sizeof(cmsghdr)).value
|
||||
return ctypes.c_size_t(ret)
|
||||
|
||||
|
||||
class ClientFile(object):
|
||||
def __init__(self, name, mode, fd):
|
||||
self.fileobject = os.fdopen(fd, mode)
|
||||
self.filename = name
|
||||
|
||||
libc = ctypes.CDLL(ctypes.util.find_library('c'))
|
||||
recvmsg = libc.recvmsg
|
||||
recvmsg.argtypes = [ctypes.c_int, ctypes.POINTER(msghdr), ctypes.c_int]
|
||||
recvmsg.restype = ctypes.c_int
|
||||
sendmsg = libc.sendmsg
|
||||
sendmsg.argtypes = [ctypes.c_int, ctypes.POINTER(msghdr), ctypes.c_int]
|
||||
sendmsg.restype = ctypes.c_size_t
|
||||
|
||||
def decodestr(value):
|
||||
ret = None
|
||||
try:
|
||||
@@ -115,13 +34,11 @@ def decodestr(value):
|
||||
ret = value.decode('cp437')
|
||||
except UnicodeDecodeError:
|
||||
ret = value
|
||||
except AttributeError:
|
||||
return value
|
||||
return ret
|
||||
|
||||
def unicode_dictvalues(dictdata):
|
||||
for key in dictdata:
|
||||
if isinstance(dictdata[key], bytes):
|
||||
if isinstance(dictdata[key], str):
|
||||
dictdata[key] = decodestr(dictdata[key])
|
||||
elif isinstance(dictdata[key], datetime):
|
||||
dictdata[key] = dictdata[key].strftime('%Y-%m-%dT%H:%M:%S')
|
||||
@@ -132,7 +49,7 @@ def unicode_dictvalues(dictdata):
|
||||
|
||||
|
||||
def _unicode_list(currlist):
|
||||
for i in range(len(currlist)):
|
||||
for i in xrange(len(currlist)):
|
||||
if isinstance(currlist[i], str):
|
||||
currlist[i] = decodestr(currlist[i])
|
||||
elif isinstance(currlist[i], dict):
|
||||
@@ -141,13 +58,13 @@ def _unicode_list(currlist):
|
||||
_unicode_list(currlist[i])
|
||||
|
||||
|
||||
def send(handle, data, filehandle=None):
|
||||
def send(handle, data):
|
||||
if isinstance(data, unicode):
|
||||
try:
|
||||
data = data.encode('utf-8')
|
||||
except AttributeError:
|
||||
pass
|
||||
if isinstance(data, bytes) or isinstance(data, unicode):
|
||||
if isinstance(data, str) or isinstance(data, unicode):
|
||||
# plain text, e.g. console data
|
||||
tl = len(data)
|
||||
if tl == 0:
|
||||
@@ -169,27 +86,9 @@ def send(handle, data, filehandle=None):
|
||||
if tl > 16777215:
|
||||
raise Exception("JSON data exceeds protocol limits")
|
||||
# xor in the type (0b1 << 24)
|
||||
if filehandle is None:
|
||||
tl |= 16777216
|
||||
handle.sendall(struct.pack("!I", tl))
|
||||
handle.sendall(sdata)
|
||||
else:
|
||||
tl |= (2 << 24)
|
||||
handle.sendall(struct.pack("!I", tl))
|
||||
cdtype = ctypes.c_ubyte * len(sdata)
|
||||
cdata = cdtype.from_buffer(bytearray(sdata))
|
||||
ciov = iovec(iov_base=ctypes.addressof(cdata),
|
||||
iov_len=ctypes.c_size_t(ctypes.sizeof(cdata)))
|
||||
fd = ctypes.c_int(filehandle)
|
||||
cmh = cmsghdr.init_data(
|
||||
cmsg_len=CMSG_LEN(
|
||||
ctypes.sizeof(fd)), cmsg_level=socket.SOL_SOCKET,
|
||||
cmsg_type=SCM_RIGHTS, cmsg_data=fd)
|
||||
mh = msghdr(msg_name=None, msg_len=0, msg_iov=iovec_ptr(ciov),
|
||||
msg_iovlen=1, msg_control=ctypes.addressof(cmh),
|
||||
msg_controllen=ctypes.c_size_t(ctypes.sizeof(cmh)))
|
||||
sendmsg(handle.fileno(), mh, 0)
|
||||
|
||||
tl |= 16777216
|
||||
handle.sendall(struct.pack("!I", tl))
|
||||
handle.sendall(sdata)
|
||||
|
||||
def recvall(handle, size):
|
||||
rd = handle.recv(size)
|
||||
@@ -219,46 +118,12 @@ def recv(handle):
|
||||
datatype = (tl & 2130706432) >> 24 # grab 7 bits from near beginning
|
||||
if dlen == 0:
|
||||
return None
|
||||
if datatype == tlv.Types.filehandle:
|
||||
filehandles = array.array('i')
|
||||
rawbuffer = bytearray(2048)
|
||||
pkttype = ctypes.c_ubyte * 2048
|
||||
data = pkttype.from_buffer(rawbuffer)
|
||||
cmsgsize = CMSG_SPACE(ctypes.sizeof(ctypes.c_int)).value
|
||||
cmsgarr = bytearray(cmsgsize)
|
||||
cmtype = ctypes.c_ubyte * cmsgsize
|
||||
cmsg = cmtype.from_buffer(cmsgarr)
|
||||
cmsg.cmsg_level = socket.SOL_SOCKET
|
||||
cmsg.cmsg_type = SCM_RIGHTS
|
||||
cmsg.cmsg_len = CMSG_LEN(ctypes.sizeof(ctypes.c_int))
|
||||
iov = iovec()
|
||||
iov.iov_base = ctypes.addressof(data)
|
||||
iov.iov_len = 2048
|
||||
msg = msghdr()
|
||||
msg.msg_iov = ctypes.pointer(iov)
|
||||
msg.msg_iovlen = 1
|
||||
msg.msg_control = ctypes.addressof(cmsg)
|
||||
msg.msg_controllen = ctypes.sizeof(cmsg)
|
||||
select.select([handle], [], [])
|
||||
i = recvmsg(handle.fileno(), ctypes.pointer(msg), 0)
|
||||
cdata = cmsgarr[CMSG_LEN(0).value:]
|
||||
data = rawbuffer[:i]
|
||||
if cmsg.cmsg_level == socket.SOL_SOCKET and cmsg.cmsg_type == SCM_RIGHTS:
|
||||
try:
|
||||
filehandles.fromstring(bytes(
|
||||
cdata[:len(cdata) - len(cdata) % filehandles.itemsize]))
|
||||
except AttributeError:
|
||||
filehandles.frombytes(bytes(
|
||||
cdata[:len(cdata) - len(cdata) % filehandles.itemsize]))
|
||||
data = json.loads(bytes(data))
|
||||
return ClientFile(data['filename'], data['mode'], filehandles[0])
|
||||
else:
|
||||
data = handle.recv(dlen)
|
||||
while len(data) < dlen:
|
||||
ndata = handle.recv(dlen - len(data))
|
||||
if not ndata:
|
||||
raise Exception("Error reading data")
|
||||
data += ndata
|
||||
data = handle.recv(dlen)
|
||||
while len(data) < dlen:
|
||||
ndata = handle.recv(dlen - len(data))
|
||||
if not ndata:
|
||||
raise Exception("Error reading data")
|
||||
data += ndata
|
||||
if datatype == tlv.Types.text:
|
||||
return data
|
||||
elif datatype == tlv.Types.json:
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
%define name confluent_client
|
||||
%define version #VERSION#
|
||||
%define fversion %{lua:
|
||||
sv, _ = string.gsub("#VERSION#", "[~+]", "-")
|
||||
print(sv)
|
||||
}
|
||||
%define release 1
|
||||
|
||||
Summary: Client libraries and utilities for confluent
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
Release: %{release}
|
||||
Source0: %{name}-%{fversion}.tar.gz
|
||||
License: Apache2
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
License: UNKNOWN
|
||||
Group: Development/Libraries
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
|
||||
Prefix: %{_prefix}
|
||||
BuildArch: noarch
|
||||
Vendor: Lenovo
|
||||
Url: http://github.com/lenovo/confluent
|
||||
Vendor: Jarrod Johnson <jjohnson2@lenovo.com>
|
||||
Url: http://xcat.sf.net/
|
||||
Obsoletes: confluent_common
|
||||
|
||||
%description
|
||||
@@ -25,28 +21,17 @@ This package enables python development and command line access to
|
||||
a confluent server.
|
||||
|
||||
%prep
|
||||
%setup -n %{name}-%{fversion}
|
||||
%setup -n %{name}-%{version} -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
%if "%{dist}" == ".el7"
|
||||
python2 setup.py build
|
||||
%else
|
||||
python3 setup.py build
|
||||
%endif
|
||||
python setup.py build
|
||||
|
||||
%install
|
||||
%if "%{dist}" == ".el7"
|
||||
python2 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-scripts=/opt/confluent/bin --install-purelib=/opt/confluent/lib/python
|
||||
%else
|
||||
python3 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-scripts=/opt/confluent/bin --install-purelib=/opt/confluent/lib/python
|
||||
%endif
|
||||
python setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-scripts=/opt/confluent/bin --install-purelib=/opt/confluent/lib/python
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%files -f INSTALLED_FILES
|
||||
%license /opt/confluent/share/licenses/confluent_client/LICENSE
|
||||
%license /opt/confluent/share/licenses/confluent_client/COPYRIGHT
|
||||
%defattr(-,root,root)
|
||||
/opt/confluent
|
||||
|
||||
@@ -19,7 +19,6 @@ alias nodebmcreset='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export
|
||||
alias nodeboot='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodeboot'
|
||||
alias nodeconfig='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodeconfig'
|
||||
alias nodeconsole='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodeconsole'
|
||||
alias nodedeploy='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodedeploy'
|
||||
alias nodedefine='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodedefine'
|
||||
alias nodeeventlog='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodeeventlog'
|
||||
alias nodefirmware='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodefirmware'
|
||||
@@ -37,18 +36,17 @@ alias nodereseat='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export C
|
||||
alias noderun='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; noderun'
|
||||
alias nodesensors='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodesensors'
|
||||
alias nodesetboot='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodesetboot'
|
||||
alias nodestorage='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodestorage -i'
|
||||
alias nodestorage='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodestorage'
|
||||
alias nodeshell='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodeshell'
|
||||
alias nodelicense='CURRENT_CMDLINE=$(HISTTIMEFORMAT= builtin history 1); export CURRENT_CMDLINE; nodelicense'
|
||||
# Do not continue for non-bash shells, the rest of this sets up bash completion functions
|
||||
[ -z "$BASH_VERSION" -o -z "$PS1" ] && return
|
||||
|
||||
|
||||
_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=""
|
||||
@@ -75,34 +73,23 @@ 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
|
||||
return;
|
||||
fi
|
||||
}
|
||||
function _confluent_generic_ng_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS -ge 3 ] && [ ! -z "$GENNED" ]; then
|
||||
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
fi
|
||||
if [ $NUMARGS -lt 3 ]; then
|
||||
_confluent_ng_completion
|
||||
return;
|
||||
fi
|
||||
}
|
||||
_confluent_nodeidentify_completion()
|
||||
{
|
||||
COMP_CANDIDATES=("on,off,blink -h")
|
||||
COMP_CANDIDATES=("on,off -h")
|
||||
_confluent_generic_completion
|
||||
}
|
||||
|
||||
|
||||
_confluent_nodesetboot_completion()
|
||||
{
|
||||
COMP_CANDIDATES=("default,cd,network,setup,hd,floppy,usb -h -b -p")
|
||||
COMP_CANDIDATES=("default,cd,network,setup,hd -h -b -p")
|
||||
_confluent_generic_completion
|
||||
}
|
||||
|
||||
@@ -122,7 +109,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
|
||||
@@ -135,7 +122,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
|
||||
@@ -149,76 +136,11 @@ _confluent_nodefirmware_completion()
|
||||
fi
|
||||
}
|
||||
|
||||
_confluent_osimage_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 2 ]; then
|
||||
COMPREPLY=($(compgen -W "initialize import importcheck updateboot rebase" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'initialize' ]; then
|
||||
COMPREPLY=($(compgen -W "-h -u -s -t -i" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
elif [ ${CMPARGS[1]} == 'import' ] || [ ${CMPARGS[1]} == 'importcheck' ]; then
|
||||
compopt -o default
|
||||
COMPREPLY=()
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'updateboot' -o ${CMPARGS[1]} == 'rebase' ]; then
|
||||
COMPREPLY=($(compgen -W "-n $(confetty show /deployment/profiles|sed -e 's/\///')" -- "${COMP_WORDS[COMP_CWORD]}"))
|
||||
return
|
||||
fi
|
||||
}
|
||||
|
||||
_confluent_imgutil_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 2 ]; then
|
||||
COMPREPLY=($(compgen -W "build exec unpack pack capture" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'build' ]; then
|
||||
COMPREPLY=($(compgen -W "-s $(ls /var/lib/confluent/distributions)" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'unpack' ]; then
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
COMPREPLY=($(compgen -W "-s $(ls /var/lib/confluent/public/os)" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return
|
||||
fi
|
||||
compopt -o dirnames
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'pack' ]; then
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
compopt -o dirnames
|
||||
fi
|
||||
COMPREPLY=()
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'capture' ]; then
|
||||
if [ $NUMARGS == 3 ]; then
|
||||
_confluent_nn_completion
|
||||
return
|
||||
fi
|
||||
return
|
||||
elif [ ${CMPARGS[1]} == 'exec' ]; then
|
||||
compopt -o dirnames
|
||||
COMPREPLY=($(compgen -W "-v" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return
|
||||
fi
|
||||
}
|
||||
_confluent_nodedeploy_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS -gt 2 ]; then
|
||||
COMPREPLY=($(compgen -W "-n $(confetty show /deployment/profiles|sed -e 's/\///')" -- "${COMP_WORDS[COMP_CWORD]}"))
|
||||
return
|
||||
fi
|
||||
if [ $NUMARGS -lt 3 ]; then
|
||||
_confluent_nr_completion
|
||||
return;
|
||||
fi
|
||||
}
|
||||
|
||||
_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
|
||||
@@ -236,7 +158,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" -- ${COMP_WORDS[-1]}))
|
||||
return;
|
||||
fi
|
||||
if [ $NUMARGS == 4 ] && [ ${CMPARGS[2]} == 'install' ]; then
|
||||
@@ -244,11 +166,6 @@ _confluent_nodelicense_completion()
|
||||
COMPREPLY=()
|
||||
return
|
||||
fi
|
||||
if [ $NUMARGS == 4 ] && [ ${CMPARGS[2]} == 'save' ]; then
|
||||
compopt -o dirnames
|
||||
COMPREPLY=()
|
||||
return
|
||||
fi
|
||||
if [ $NUMARGS -lt 3 ]; then
|
||||
_confluent_nr_completion
|
||||
return
|
||||
@@ -259,7 +176,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
|
||||
@@ -279,55 +196,48 @@ _confluent_nodeattrib_completion()
|
||||
_confluent_generic_completion
|
||||
}
|
||||
|
||||
_confluent_define_completion()
|
||||
{
|
||||
COMP_CANDIDATES=$(nodegroupattrib 'everything' all | awk '{print $2}'|sed -e 's/://')
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS -ge 3 ] && [ ! -z "$GENNED" ]; then
|
||||
COMPREPLY=($(compgen -W "$GENNED" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
fi
|
||||
}
|
||||
|
||||
_confluent_nodegroupattrib_completion()
|
||||
{
|
||||
COMP_CANDIDATES=$(nodegroupattrib 'everything' all | awk '{print $2}'|sed -e 's/://')
|
||||
_confluent_generic_ng_completion
|
||||
}
|
||||
_confluent_nn_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
INPUT=${COMP_WORDS[COMP_CWORD]}
|
||||
if [ $NUMARGS -gt 2 ]; then
|
||||
return;
|
||||
fi
|
||||
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
|
||||
compopt -o default
|
||||
COMPREPLY=()
|
||||
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()
|
||||
{
|
||||
@@ -335,47 +245,32 @@ _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]}"))
|
||||
}
|
||||
|
||||
_confluent_nodediscover_completion()
|
||||
{
|
||||
_confluent_get_args
|
||||
if [ $NUMARGS == 2 ]; then
|
||||
COMPREPLY=($(compgen -W "list assign rescan clear subscribe unsubscribe register" -- ${COMP_WORDS[COMP_CWORD]}))
|
||||
return;
|
||||
fi
|
||||
|
||||
}
|
||||
complete -F _confluent_nodediscover_completion nodediscover
|
||||
complete -F _confluent_nodeattrib_completion nodeattrib
|
||||
complete -F _confluent_nodeattrib_completion nodegroupattrib
|
||||
complete -F _confluent_nr_completion nodebmcreset
|
||||
complete -F _confluent_nodesetboot_completion nodeboot
|
||||
complete -F _confluent_nr_completion nodeconfig
|
||||
complete -F _confluent_nn_completion nodeconsole
|
||||
complete -F _confluent_define_completion nodedefine
|
||||
complete -F _confluent_define_completion nodegroupdefine
|
||||
complete -F _confluent_nr_completion nodeeventlog
|
||||
complete -F _confluent_nodefirmware_completion nodefirmware
|
||||
complete -F _confluent_nodedeploy_completion nodedeploy
|
||||
complete -F _confluent_osimage_completion osdeploy
|
||||
complete -F _confluent_imgutil_completion imgutil
|
||||
complete -F _confluent_nodegroupattrib_completion nodegroupattrib
|
||||
complete -F _confluent_ng_completion nodegroupattrib
|
||||
complete -F _confluent_ng_completion nodegroupremove
|
||||
complete -F _confluent_nr_completion nodehealth
|
||||
complete -F _confluent_nodeidentify_completion nodeidentify
|
||||
complete -F _confluent_nr_completion nodeinventory
|
||||
complete -F _confluent_nodeattrib_completion nodelist
|
||||
complete -F _confluent_nr_completion nodelist
|
||||
complete -F _confluent_nodemedia_completion nodemedia
|
||||
complete -F _confluent_nodepower_completion nodepower
|
||||
complete -F _confluent_nr_completion noderemove
|
||||
|
||||
@@ -3,7 +3,7 @@ collate(1) -- Organize text input by node
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`<other command> | collate [-a] [-b] [-d] [-w] [-g] [-s] [-c] [-r] [-l lognametemplate]`
|
||||
`<other command> | collate [-a] [-d] [-w] [-s] [-c] [-r] [-l lognametemplate]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -21,41 +21,30 @@ node and group names sorted alphanumerically.
|
||||
when output differs along a telling group boundary. For example, if
|
||||
output suggests a large number of nodes are unreachable, abbreviate
|
||||
showing 'rack1' as being unreachable may make more obvious a possible cause.
|
||||
|
||||
* `-b BASE`, `--base=BASE`:
|
||||
Use given node as reference for comparison when using
|
||||
-d, instead of using the most common result
|
||||
|
||||
* `-d`, `--diff`:
|
||||
Express all but the most common result group in terms of diff from
|
||||
the most common result group
|
||||
|
||||
|
||||
* `-l`, `--log`:
|
||||
Save output per node to individual log files, replacing {node} in the name
|
||||
with the nodename of each
|
||||
|
||||
* `-w`, `--watch`:
|
||||
Update results dynamically as data becomes available, rather than
|
||||
waiting for the command to fully complete.
|
||||
|
||||
* `-g`, `--groupcount`:
|
||||
Show count of output groups rather than the actual
|
||||
output
|
||||
|
||||
|
||||
* `-s`, `--skipcommon`:
|
||||
Suppress printing of the most common result text group. This is used to
|
||||
focus on stray output against a well known and expected result.
|
||||
|
||||
* `-c`, `--count`:
|
||||
Print a count of the number of nodes in an output group under the
|
||||
noderange.
|
||||
noderange.
|
||||
|
||||
* `-r`, `--reverse`:
|
||||
Rather than starting with most common to least common, start with
|
||||
the least common and print the most common last.
|
||||
|
||||
* `-l LOG`, `--log=LOG`:
|
||||
Save output per node to individual log files, replacing {node} in the name
|
||||
with the nodename of each
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
* `-r`, `--reverse`:
|
||||
Rather than starting with most common to least common, start with
|
||||
the least common and print the most common last.
|
||||
|
||||
## EXAMPLES
|
||||
* Organizing power state of multiple nodes:
|
||||
|
||||
@@ -7,7 +7,6 @@ collective(1) -- Check and manage a confluent collective
|
||||
`collective join <server> [-i TOKEN]`
|
||||
`collective show`
|
||||
`collective gencert`
|
||||
`collective delete`
|
||||
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -37,9 +36,6 @@ on circumstances.
|
||||
|
||||
* `-i`:
|
||||
Provide the token as an argument rather than interactively.
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
* Inviting a server called mgt2:
|
||||
|
||||
@@ -15,21 +15,6 @@ be mostly an aid for developing client software, with
|
||||
day to day administration generally being easier with
|
||||
the various function specific commands.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-s SERVER:PORT`, `--server=SERVER:PORT`:
|
||||
Confluent instance to connect to
|
||||
|
||||
* `-c PATH`, `--control=PATH`:
|
||||
Path to offer terminal control
|
||||
|
||||
* `-m MINTIME`, `--mintime=MINTIME`:
|
||||
Minimum time to run or else pause for input (used to
|
||||
keep a terminal from closing quickly on error)
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## COMMANDS
|
||||
|
||||
The CLI may be navigated by shell commands and some other
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
confluent2hosts(8) -- Generate /etc/hosts entries for nodes
|
||||
========================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`confluent2hosts -i <ip expression> -n <name expression> <noderange>`
|
||||
`confluent2hosts -a <noderange>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`confluent2hosts` can be used to help generate /etc/hosts entries for a
|
||||
noderange. There are two general approaches.
|
||||
|
||||
It can be used ad-hoc, using -i and -n to specify the address and name portions respectively. This accepts the standard confluent expression syntax, allowing for things like 172.30.1.{n1} or {node}.{dns.domain} or {bmc}.
|
||||
|
||||
It can also read from the confluent db, using `-a`. In this mode, each net.<value>.<attribute> group is pulled together into hosts lines. ipv4_address and ipv6_address fields are associated with the corresponding hostname attributes.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Generate /etc/hosts entries ad-hoc using default name:
|
||||
`# confluent2hosts -i 10.2.3.{n1} d9-d12`
|
||||
|
||||
* Generate /etc/hosts entries ad-hoc using alternative name:
|
||||
`# confluent2hosts -i 10.2.3.{n1} -n "{node}-alt {node}-alt.{dns.domain}" d9-d12`
|
||||
|
||||
* Generate /etc/hosts entries using the confluent DB as a reference:
|
||||
`# confluent2hosts -a d9-d12`
|
||||
|
||||
@@ -3,7 +3,8 @@ confluentdbutil(8) -- Backup or restore confluent database
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`confluentdbutil [options] [dump|restore] <path>`
|
||||
|
||||
`confluentdbutil [options] <dump|restore> <path>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -15,28 +16,11 @@ the json files (password protected, removed from the files, or unprotected).
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-p PASSWORD`, `--password=PASSWORD`:
|
||||
* `-p`, `--password`:
|
||||
If specified, information such as usernames and passwords will be encrypted
|
||||
using the given password.
|
||||
|
||||
* `-i`, `--interactivepassword`:
|
||||
Prompt for password.
|
||||
|
||||
* `-r`, `--redact`:
|
||||
Indicates to replace usernames and passwords with a dummy string rather
|
||||
than included.
|
||||
|
||||
* `-u`, `--unprotected`:
|
||||
The keys.json file will include the encryption keys without any protection.
|
||||
|
||||
* `-s`, `--skipkeys`:
|
||||
This specifies to dump the encrypted data without
|
||||
dumping the keys needed to decrypt it. This is
|
||||
suitable for an automated incremental backup, where an
|
||||
earlier password protected dump has a protected
|
||||
keys.json file, and only the protected data is needed.
|
||||
keys do not change and as such they do not require
|
||||
incremental backup.
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
The keys.json file will include the encryption keys without any protection.
|
||||
@@ -1,30 +0,0 @@
|
||||
nodeapply(8) -- Execute command on many nodes in a noderange through ssh
|
||||
=========================================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodeapply [options] <noderange>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Provides shortcut access to a number of common operations against deployed
|
||||
nodes. These operations include refreshing ssh certificates and configuration,
|
||||
rerunning syncflies, and executing specified postscripts.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-k`, `--security`
|
||||
Refresh SSH configuration (hosts.equiv and node SSH certificates)
|
||||
|
||||
* `-F`, `--sync`
|
||||
Rerun syncfiles from deployed profile
|
||||
|
||||
* `-P SCRIPTS`, `--scripts=SCRIPTS`
|
||||
Re-run specified scripts, with full path under scripts specified, e.g. post.d/scriptname,firstboot.d/otherscriptname
|
||||
|
||||
* `-c COUNT`, `-f COUNT`, `--count=COUNT`
|
||||
Specify the maximum number of instances to run concurrently
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`
|
||||
Specify a maximum number of nodes to run remote ssh command to, prompting
|
||||
if over the threshold
|
||||
+9
-56
@@ -7,8 +7,7 @@ nodeattrib(8) -- List or change confluent nodes attributes
|
||||
`nodeattrib <noderange> [<nodeattribute1=value1> <nodeattribute2=value2> ...]`
|
||||
`nodeattrib -c <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib -e <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib -p <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
`nodeattrib <noderange> -s <attributes.batch> ...`
|
||||
`nodeattrib -p <noderange> <nodeattribute1> <nodeattribute2> ...`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -35,43 +34,18 @@ For the `groups` attribute, it is possible to add a group by doing
|
||||
`groups,=<newgroup>` and to remove by doing `groups^=<oldgroup>`
|
||||
|
||||
Note that `nodeattrib <group>` will likely not provide the expected behavior.
|
||||
See nodegroupattrib(8) command on how to manage attributes on a group level. Running
|
||||
nodeattrib on a group will simply set node-specific attributes on each individual
|
||||
member of the group.
|
||||
|
||||
Additionally, if wanting to change from a node level attribute value to inherit from a
|
||||
member group, it is required to use '-c' to clear the attribute. Conversely, assigning
|
||||
to a blank value will allow masking a group defined attribute with an empty value.
|
||||
See nodegroupattrib(8) command on how to manage attributes on a group level.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-b`, `--blame`:
|
||||
Annotate inherited and expression based attributes to show their base value.
|
||||
|
||||
* `-c`, `--clear`:
|
||||
Clear specified nodeattributes.
|
||||
|
||||
Clear specified nodeattributes
|
||||
* `-e`, `--environment`:
|
||||
Set specified attributes based on exported environment variable of matching name.
|
||||
Environment variable names may be lower case or all upper case.
|
||||
Replace . with _ as needed (e.g. info.note may be specified as either $info_note or $INFO_NOTE)
|
||||
|
||||
Set specified attributes based on exported environment variable of matching name. Environment variable names may be lower case or all upper case. Replace . with _ as needed (e.g. info.note may be specified as either $info_note or $INFO_NOTE
|
||||
* `-p`, `--prompt`:
|
||||
Request interactive prompting to provide values rather than the command line
|
||||
or environment variables.
|
||||
|
||||
* `-s`, `--set`:
|
||||
Set attributes using a batch file rather than the command line. The attributes in the batch file
|
||||
can be specified as one line of key=value pairs simmilar to command line or each attribute can
|
||||
be in its own line. Lines that start with # sign will be read as a comment. See EXAMPLES for batch
|
||||
file syntax.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Prompt if trying to set attributes on more than
|
||||
specified number of nodes.
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
Request interactive prompting to provide values rather than the command line or environment variables.
|
||||
|
||||
## EXAMPLES
|
||||
* Listing matching nodes of a simple noderange:
|
||||
@@ -90,12 +64,12 @@ to a blank value will allow masking a group defined attribute with an empty valu
|
||||
`# nodeattrib n1,n2 hardwaremanagement --blame`
|
||||
`n1: hardwaremanagement.manager: 172.30.3.1`
|
||||
`n1: hardwaremanagement.method: ipmi (inherited from group everything)`
|
||||
`n1: net.switch: r8e1`
|
||||
`n1: net.switchport: 14`
|
||||
`n1: hardwaremanagement.switch: r8e1`
|
||||
`n1: hardwaremanagement.switchport: 14`
|
||||
`n2: hardwaremanagement.manager: 172.30.3.2`
|
||||
`n2: hardwaremanagement.method: ipmi (inherited from group everything)`
|
||||
`n2: net.switch: r8e1`
|
||||
`n2: net.switchport: 2`
|
||||
`n2: hardwaremanagement.switch: r8e1`
|
||||
`n2: hardwaremanagement.switchport: 2`
|
||||
|
||||
* Listing matching nodes of a simple noderange that are set:
|
||||
`# nodeattrib n1-n2 current`
|
||||
@@ -123,27 +97,6 @@ to a blank value will allow masking a group defined attribute with an empty valu
|
||||
`d1: net.pxe.switch: pxeswitch1`
|
||||
`d1: net.switch:`
|
||||
|
||||
* Setting attributes using a batch file with syntax similar to command line:
|
||||
`# cat nodeattributes.batch`
|
||||
`# power`
|
||||
`power.psu1.outlet=3 power.psu1.pdu=pdu2`
|
||||
`# nodeattrib n41 -s nodeattributes.batch`
|
||||
`n41: 3`
|
||||
`n41: pdu2`
|
||||
|
||||
* Setting attributes using a batch file with syntax where each attribute is in its own line:
|
||||
`# cat nodeattributes.batch`
|
||||
`# management`
|
||||
`custom.mgt.switch=switch_main`
|
||||
`custom.mgt.switch.port=swp4`
|
||||
`# nodeattrib n41 -s nodeattributes.batch`
|
||||
`n41: switch_main`
|
||||
`n41: swp4`
|
||||
|
||||
|
||||
|
||||
## SEE ALSO
|
||||
|
||||
nodegroupattrib(8), nodeattribexpressions(5)
|
||||
|
||||
## ATTRIBUTES
|
||||
@@ -1,28 +0,0 @@
|
||||
nodebmcpassword(8) -- Change management controller password for a specified user
|
||||
=========================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodebmcpassword <noderange> <username> <new_password>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`nodebmcpassword` allows you to change the management controller password for a user on a specified noderange
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Number of nodes to affect before prompting for
|
||||
confirmation
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES:
|
||||
|
||||
* Reset the management controller for nodes n1 through n4:
|
||||
`# nodebmcreset n1-n4`
|
||||
`n1: Password Change Successful`
|
||||
`n2: Password Change Successful`
|
||||
`n3: Password Change Successful`
|
||||
`n4: Password Change Successful`
|
||||
@@ -9,15 +9,6 @@ nodebmcreset(8) -- Reset management controller
|
||||
|
||||
`nodebmcreset` allows you to reset the management controller of the specified noderange
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Number of nodes to affect before prompting for
|
||||
confirmation
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES:
|
||||
|
||||
* Reset the management controller for nodes n1 through n4:
|
||||
|
||||
@@ -3,7 +3,8 @@ nodeboot(8) -- Reboot a confluent node to a specific device
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodeboot [options] <noderange> [default|cd|network|setup|hd]`
|
||||
`nodeboot <noderange>`
|
||||
`nodeboot [options] <noderange>` [default|cd|network|setup|hd]
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -24,13 +25,6 @@ device without inducing a reboot, see the `nodesetboot` command.
|
||||
* `-p`, `--persist`:
|
||||
For a system that supports it, mark the boot override to persist rather than
|
||||
be a one time change. Many systems do not support this functionality.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to boot, prompting
|
||||
if over the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
* `default`:
|
||||
Request a normal default boot with no particular device override
|
||||
|
||||
@@ -3,7 +3,8 @@ nodeconfig(8) -- Show or change node configuration
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodeconfig [options] <noderange> [setting|setting=value]`
|
||||
`nodeconfig <noderange> [options] [<configuration>..]`
|
||||
`nodeconfig <noderange> [options] [<configuration=value>..]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -13,12 +14,6 @@ running configuration on the node firmware. Calling without '=' will show the
|
||||
current value, and '=' will change the value. Network information can be
|
||||
given as a node expression, as documented in the man page for nodeattribexpressions(5).
|
||||
|
||||
Note that when using nodeconfig to submit changes, it will exit when the change
|
||||
is accepted, but the endpoint may not have fully processed it. Doing a show
|
||||
immediately after doing a set may reflect older information. Also, if changing
|
||||
BIOS/UEFI settings, the change may appear in output, but generally won't
|
||||
actually be in effect until a reboot.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-c`, `--comparedefault`:
|
||||
@@ -26,38 +21,23 @@ actually be in effect until a reboot.
|
||||
no configuration values are specified, it will show only those that differ.
|
||||
If combined with `-x`, will show all differing values except those indicated
|
||||
by `-x`
|
||||
|
||||
* `-b settings.batch`, `--batch=settings.batch`:
|
||||
Provide arguments as lines of a file, rather than the command line.
|
||||
|
||||
* `-d`, `--detail`:
|
||||
Provide detailed data as available. This can include help text and valid
|
||||
values for a setting.
|
||||
|
||||
* `-e`, `--extra`:
|
||||
Read settings that are generally not needed, but may be slow to retrieve.
|
||||
Notably this includes the IMM category of Lenovo systems. The most popular
|
||||
IMM settings are available through faster 'bmc' attributes.
|
||||
|
||||
* `-x`, `--exclude`:
|
||||
Rather than listing only the specified configuration parameters, list all
|
||||
attributes except for the specified ones
|
||||
|
||||
* `-a`, `--advanced`:
|
||||
Include advanced settings, which are normally not intended to be used
|
||||
without direction from the relevant server vendor.
|
||||
* `-d`, `--detail`:
|
||||
Provide detailed data as available. This can include help text and valid
|
||||
values for a setting.
|
||||
|
||||
* `-r COMPONENT`, `--restoredefault=COMPONENT`:
|
||||
* `-r`, `--restoredefault`:
|
||||
Request that the specified component of the targeted nodes will have its
|
||||
configuration reset to default. Currently the only component implemented
|
||||
is uefi.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to configure, prompting if over
|
||||
the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
* `-b`, '--batch':
|
||||
Provide arguments as lines of a file, rather than the command line.
|
||||
|
||||
|
||||
## EXAMPLES
|
||||
* Showing the current IP configuration of noderange BMC/IMM/XCC:
|
||||
|
||||
@@ -2,7 +2,7 @@ nodeconsole(8) -- Open a console to a confluent node
|
||||
=====================================================
|
||||
|
||||
## SYNOPSIS
|
||||
`nodeconsole [options] <noderange> [kill][-- [passthroughoptions]]`
|
||||
`nodeconsole [options] <noderange>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -16,45 +16,12 @@ will initiate an automatic retry interval that is randomized between 2 and 4 min
|
||||
The reopen escape sequence below requests an immediate retry, as does connecting
|
||||
a new session.
|
||||
|
||||
When a windowed console is open the `nodeconsole <noderange> kill` command will kill the
|
||||
console process which will result in the console window closing.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-t`, `--tile`:
|
||||
Use tmux to arrange consoles of the given noderange into a tiled layout on
|
||||
the terminal screen
|
||||
|
||||
* `-l`, `--log`:
|
||||
Perform a log reply on the current, local log in /var/log/confluent/consoles.
|
||||
If in collective mode, this only makes sense to use on the current collective
|
||||
manager at this time.
|
||||
|
||||
* `-T`, `--Timestamp`:
|
||||
Dump the log with Timpstamps on the current, local log in /var/log/confluent/consoles.
|
||||
If in collective mode, this only makes sense to use on the current collective
|
||||
manager at this time.
|
||||
|
||||
* `-w`, `--windowed`:
|
||||
Open terminal windows for each node. The
|
||||
environment variable **NODECONSOLE_WINDOWED_COMMAND**
|
||||
should be set, which should be a text string corresponding
|
||||
to a command that can be used to open a windowed console,
|
||||
omitting the `nodeconsole <noderange>` part of the
|
||||
command, for example, to open a set of consoles for a
|
||||
range of nodes in separate xterm windows, set
|
||||
**NODECONSOLE_WINDOWED_COMMAND** to `xterm -e`. To open a
|
||||
set of consoles for a range of nodes in separate
|
||||
GNOME Terminal windows with a size of 100 columns and
|
||||
31 rows, set **NODECONSOLE_WINDOWED_COMMAND**
|
||||
to `gnome-terminal --geometry 100x31 --` or in a WSL
|
||||
environment, to open a set of consoles for a range of
|
||||
nodes in separate Windows Terminal windows, with the
|
||||
title set for each node, set **NODECONSOLE_WINDOWED_COMMAND**
|
||||
to `wt.exe wsl.exe -d AlmaLinux-8 --shell-type login. If the
|
||||
NODECONSOLE_WINDOWED_COMMAND environment variable isn't set,
|
||||
xterm will be used bydefault.
|
||||
|
||||
## ESCAPE SEQUENCE COMMANDS
|
||||
|
||||
While connected to a console, a number of commands may be performed through escape
|
||||
@@ -94,14 +61,3 @@ keystroke will be interpreted as a command. The following commands are availabl
|
||||
Get a list of supported commands
|
||||
* `<ent>`:
|
||||
Hit enter to skip entering a command at the escape prompt.
|
||||
|
||||
|
||||
## PASSTHROUGH OPTIONS
|
||||
|
||||
While opening a windowed console with xterm or any other console of choice. The
|
||||
nodeconsole command gives capality to specify passthrough options targeted at
|
||||
the console. All options after the -- will be parsed the console program. For
|
||||
example, opening a windowed console using xterm with a black background.
|
||||
`nodeconconsole -w n1 -- -bg black`
|
||||
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
nodedeploy(8) -- Request preparation and/or initiating a node deployment
|
||||
=========================================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodedeploy [-h] [-c] [-n] [-p] [-m MAXNODES] <noderange> [profile]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
**nodedeploy** configures attributes and interacts with BMC devices as appropriate
|
||||
to change the operating system on the noderange to that indicated by `profile`. Tab completion
|
||||
is supported under bash to help list and select a profile name. `-n` indicates that it
|
||||
will be a network based deployment (either HTTP or PXE) and `-p` suppresses any activity
|
||||
that requires the BMC (e.g. setting the next boot device and rebooting the system). Currently
|
||||
**nodedeploy** only supports `-n` style deployment flows. Without options it shows the current
|
||||
deployment status.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-c`, `--clear`:
|
||||
Remove any pending deployment action
|
||||
|
||||
* `-n`, `--network`:
|
||||
Prepare for either an HTTP or PXE based deployment, setting boot device to network and rebooting unless `-p` is specified.
|
||||
|
||||
* `-p`, `--prepareonly`:
|
||||
Prepare the network services for deployment, but do not interact with BMCs. This is intended for scenarios where
|
||||
the boot device control and server restart will be handled outside of confluent.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specifiy a maximum nodes to be deployed.
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
* Begin the instalalation of a profile of CentOS 8.2:
|
||||
`# nodedeploy d4 -n centos-8.2-x86_64-default`
|
||||
`d4: network`
|
||||
`d4: reset`
|
||||
|
||||
* Check current deployment state of nodes:
|
||||
`# nodedeploy d4`
|
||||
`d4: pending: centos-8.2-x86_64-default (node authentication armed)`
|
||||
|
||||
|
||||
@@ -5,12 +5,9 @@ nodediscover(8) -- List or manage confluent node discovery
|
||||
|
||||
`nodediscover rescan`
|
||||
`nodediscover [options] list`
|
||||
`nodediscover [options] assign`
|
||||
`nodediscover [options] rescan`
|
||||
`nodediscover [options] clear`
|
||||
`nodediscover [options] subscribe [switch]`
|
||||
`nodediscover [options] unsubscribe [switch]`
|
||||
`nodediscover [options] register [Addresses, e.g. 192.168.1.2 or 192.168.1.0/24 or 192.168.1.1-192.168.1.28]`
|
||||
`nodediscover [options] assign`
|
||||
`nodediscover [options] rescan`
|
||||
`nodediscover [options] clear`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -26,9 +23,6 @@ data may be filtered by various parameters, as denoted in the options below.
|
||||
**nodediscover assign** performs manual discovery, assigning an entry to a node
|
||||
identity or, using `-i`, using a csv file to assign nodes all at once. For
|
||||
example, a spreadsheet of serial numbers to desired node names could be used.
|
||||
Note that if you see that the host is unreachable, it may be due to the IP
|
||||
address on the endpoint having changed since last detected. In such a case, it
|
||||
may help to **clear** and try **assign** again.
|
||||
|
||||
**nodediscover rescan** requests the server to do an active sweep for new
|
||||
devices. Generally every effort is made to passively detect devices as they
|
||||
@@ -38,12 +32,6 @@ scan is the best approach to catch something that appears to be missing.
|
||||
**nodedsicover clear** requests the server forget about a selection of
|
||||
detected device. It takes the same arguments as **nodediscover list**.
|
||||
|
||||
**nodediscover subscribe** and **unsubscribe** instructs confluent to subscribe to or
|
||||
unsubscribe from the designated switch running affluent with system discovery support.
|
||||
|
||||
**nodediscover register** instructs confluent to perform a remote probe of an address, subnet,
|
||||
or range of IP addresses.
|
||||
|
||||
## CSV FORMAT
|
||||
|
||||
The CSV format used by nodediscover consists of one header describing the
|
||||
|
||||
@@ -3,31 +3,14 @@ nodeeventlog(8) -- Pull eventlog from confluent nodes
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodeeventlog [options] <noderange> [clear]`
|
||||
`nodeeventlog <noderange>`
|
||||
`nodeeventlog <noderange> [clear]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`nodeeventlog` pulls and optionally clears the event log from the requested
|
||||
noderange.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to clear if clearing log, prompting if
|
||||
over the threshold
|
||||
|
||||
* `-l LINES`, `--lines=LINES`:
|
||||
return the last <n> entries for each node in the eventlog.
|
||||
|
||||
* `-t TIMEFRAME`, `--timeframe=TIMEFRAME`:
|
||||
return entries within a specified timeframe for each node's event log.
|
||||
This will return entries from the last <n> hours or days. 1h would be
|
||||
entries from with the last one hour.
|
||||
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
* Pull the event log from n2 and n3:
|
||||
`# nodeeventlog n2,n3`
|
||||
|
||||
@@ -3,7 +3,9 @@ nodefirmware(8) -- Report firmware information on confluent nodes
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodefirmware <noderange> [list][update [--backup <file>]]|[<components>]`
|
||||
`nodefirmware <noderange>`
|
||||
`nodefirmware <noderange> list|<components>|core`
|
||||
`nodefirmware <noderange> update [--backup] <filename>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -11,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:
|
||||
@@ -24,18 +22,6 @@ the out of band facilities. Firmware updates can end in one of three states:
|
||||
* `pending`: The firmware update process has completed, but the firmware will not be active until the relevant component next resets. Generally speaking, for UEFI update the system will need a reboot, and for BMC updates, the `nodebmcreset` command will begin the process to activate the firmware.
|
||||
* `complete`: The firmware update process has completed and activation has proceeded. Note that while the activation process has commenced, the component may still be in the process of rebooting when nodefirmware exits.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-b`, `--backup`:
|
||||
Target a backup bank rather than primary
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
When updating, prompt if more than the specified number of servers will
|
||||
be affected
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Pull firmware from a node:
|
||||
|
||||
-14
@@ -25,20 +25,8 @@ the attributes are set on the node versus a group to which a node belongs.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-b`, `--blame`:
|
||||
Show information about how attributes inherited
|
||||
|
||||
* `-e`, `--environment`:
|
||||
Set attributes, but from environment variable of same name
|
||||
|
||||
* `-c`, `--clear`:
|
||||
Clear specified nodeattributes.
|
||||
|
||||
* `-p`, `--prompt`:
|
||||
Prompt for attribute values interactively
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
@@ -53,5 +41,3 @@ the attributes are set on the node versus a group to which a node belongs.
|
||||
## SEE ALSO
|
||||
|
||||
nodeattrib(8), nodeattribexpressions(5)
|
||||
|
||||
## ATTRIBUTES
|
||||
@@ -3,7 +3,7 @@ nodeidentify(8) -- Control the identify LED of confluent nodes
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodidentify <noderange> [on|off|blink]`
|
||||
`nodidentify <noderange> [on|off]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -13,7 +13,6 @@ options are supported:
|
||||
|
||||
* `on`: Turn on the identify LED
|
||||
* `off`: Turn off the identify LED
|
||||
* `blink`: Set the identify LED to blink (when supported by the system)
|
||||
|
||||
## EXAMPLES:
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
nodel2traceroute(8) -- returns the layer 2 route through an Ethernet network managed by confluent given 2 end points.
|
||||
==============================
|
||||
## SYNOPSIS
|
||||
`nodel2traceroute [options] <start_node> <end_noderange>`
|
||||
|
||||
## DESCRIPTION
|
||||
**nodel2traceroute** is a command that returns the layer 2 route for the configered interfaces in nodeattrib.
|
||||
It can also be used with the -i and -e options to check against specific interfaces on the endpoints. If the
|
||||
--interface or --eface option are not used then the command will check for routes against all the defined
|
||||
interfaces in nodeattrib (net.*.switch) for the nodes.
|
||||
|
||||
|
||||
|
||||
## PREREQUISITES
|
||||
**nodel2traceroute** the net.<interface>.switch attributes have to be set on the end points if endpoint is not a switch
|
||||
|
||||
|
||||
## OPTIONS
|
||||
* ` -e` EFACE, --eface=INTERFACE
|
||||
interface to check against for the second end point or end points if using checking against multiple nodes
|
||||
* ` -i` INTERFACE, --interface=INTERFACE
|
||||
interface to check against for the first end point
|
||||
* ` -c` CUMULUS, --cumulus=CUMULUS
|
||||
return layer 2 route through cumulus switches only
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
|
||||
## EXAMPLES
|
||||
* Checking route between two nodes:
|
||||
`# nodel2traceroute n244 n1851`
|
||||
`n244 to n1851: ['switch114']`
|
||||
|
||||
* Checking route from one node to multiple nodes:
|
||||
`# nodel2traceroute n244 n1833,n1851`
|
||||
`n244 to n1833: ['switch114', 'switch7', 'switch32', 'switch253', 'switch85', 'switch72', 'switch21', 'switch2', 'switch96', 'switch103', 'switch115']
|
||||
n244 to n1851: ['switch114']`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,22 +3,9 @@ nodelicense(8) -- Manage license keys on BMC
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodelicense <noderange> [list][install <license_filename>|save <directory>|delete <license_feature_name>]`
|
||||
`nodelicense <noderange> [list|install <filename>]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`nodelicense` manages license keys on supported BMCs. Without an argument, the command
|
||||
lists currently installed license. Using `delete` will remove the specified license name
|
||||
from the BMC. The `save` subcommand will take the passed directory (which may be in the form
|
||||
of /path/to/{node}/ to have the node name substituted for each node) and back up installed licenses
|
||||
to that directory. The `install` command will take the specified filename and install. The filename
|
||||
argument may be of the form xcc_fod_0034_7X21{id.serial}.key to have the serial number substituted
|
||||
to allow unique licenses to be specified in a single command.
|
||||
`nodelicense` shows and installs license keys on supported BMCs
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to delete licenses from, prompting if over the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
@@ -4,7 +4,7 @@ nodelist(8) -- List confluent nodes and their attributes
|
||||
## SYNOPSIS
|
||||
|
||||
`nodelist <noderange>`
|
||||
`nodelist <noderange> [-b] [-d] {string} <nodeattribute>...`
|
||||
`nodelist <noderange> [-b] <nodeattribute>...`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -24,8 +24,7 @@ all attributes that begin with `net.` and end with `switch`.
|
||||
|
||||
* `-b`, `--blame`:
|
||||
Annotate inherited and expression based attributes to show their base value.
|
||||
* `-d`, `--delim`:
|
||||
Choose a delimiter to separat the values. Default - ENTER.
|
||||
|
||||
## EXAMPLES
|
||||
* Listing matching nodes of a simple noderange:
|
||||
`# nodelist n1-n4`
|
||||
|
||||
@@ -34,8 +34,8 @@ the host platform attempts.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
* `-c`:
|
||||
Specify the maximum number of instances to run concurrently.
|
||||
|
||||
## EXAMPLES
|
||||
* Listing currently mounted media:
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
nodeping(8) -- Pings a node or a noderange.
|
||||
==============================
|
||||
## SYNOPSIS
|
||||
`nodeping [options] noderange`
|
||||
|
||||
## DESCRIPTION
|
||||
**nodeping** is a command that pings the default NIC on a node.
|
||||
It can also be used with the `-s` flag to change the ping location to something that is 'non primary'
|
||||
|
||||
|
||||
## OPTIONS
|
||||
* ` -f` COUNT, `-c` COUNT, --count=COUNT
|
||||
Number of commands to run at a time
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
* `-s` SUBSTITUTENAME, --substitutename=SUBSTITUTENAME
|
||||
Use a different name other than the nodename for ping. This may be a
|
||||
expression, such as {bmc} or, if no { character is present, it is treated as a suffix. -s -eth1 would make n1 become n1-eth1, for example.
|
||||
|
||||
|
||||
## EXAMPLES
|
||||
* Pinging a node :
|
||||
`# nodeping <node>`
|
||||
`node : ping`
|
||||
|
||||
* Pinging a group:
|
||||
`# nodeping <groupname>`
|
||||
`Node1 : ping
|
||||
Node2 : ping
|
||||
Node3 : ping`
|
||||
|
||||
* Pinging BMC on a node:
|
||||
`# nodeping -s {bmc} <noderange>`
|
||||
` Node-bmc : ping`
|
||||
|
||||
* Pinging by specifying a suffix:
|
||||
`# nodeping d1-d4 -s -eth1`
|
||||
`d2-eth1: no_ping`
|
||||
`d1-eth1: no_ping`
|
||||
`d3-eth1: no_ping`
|
||||
`d4-eth1: no_ping`
|
||||
|
||||
* Fail to ping node:
|
||||
`# nodeping <node>`
|
||||
`node : no_ping`
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ nodepower(8) -- Check or change power state of confluent nodes
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodepower [options] <noderange> ([status|on|off|shutdown|boot|reset])`
|
||||
`nodepower <noderange>`
|
||||
`nodepower <noderange> [-p] [on|off|boot|shutdown|reset|status]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -24,21 +25,11 @@ respond.
|
||||
* `reset`: Request immediate reset of nodes of the noderange. Nodes that are
|
||||
off will not react to this request.
|
||||
* `status`: Behave identically to having no argument passed at all.
|
||||
* `pdu_status`: Query state of associated PDU outlets, if configured.
|
||||
* `pdu_on`: Energize all PDU outlets associated with the noderange.
|
||||
* `pdu_off`: De-energize all PDU outlets associated with the noderange.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-p`, `--showprevious`:
|
||||
* `-p`, '--showprevious':
|
||||
Show previous power state for all directives that may change power state.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to change power state, prompting if
|
||||
over the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
* Get power state of nodes n1 through n4:
|
||||
|
||||
@@ -42,7 +42,7 @@ The attribute name may use a wildcard:
|
||||
Commas can be used to indicate multiple nodes, and can mix and match any of the above primitives. The following can be a valid single noderange, combining any and all members of each comma separated component
|
||||
`n1,n2,rack1,storage,location.rack=9,~s1..,n20-n30`
|
||||
|
||||
Exclusions can be done by prepending a '-' before a portion of a noderange:
|
||||
Exclusions can be done by prepending a ‘-‘ before a portion of a noderange:
|
||||
`rack1,-n2`
|
||||
`compute,-rack1`
|
||||
`compute,-location.row=12`
|
||||
@@ -54,7 +54,7 @@ To indicate nodes that match multiple selections at once (set intersection), the
|
||||
For complex expressions, () may be used to indicate order of expanding the noderange to be explicit
|
||||
`rack1,-(console.logging=full@compute)`
|
||||
|
||||
Noderange syntax can also indicate 'pagination', or separating the nodes into well defined chunks. > is used to indicate how many nodes to display at a time, and < is used to indicate how many nodes to skip into a noderange:
|
||||
Noderange syntax can also indicate ‘pagination’, or separating the nodes into well defined chunks. > is used to indicate how many nodes to display at a time, and < is used to indicate how many nodes to skip into a noderange:
|
||||
`rack1>3<6`
|
||||
|
||||
The above would show the seventh through ninth nodes of the rack1 group. Like all other noderange operations, this may be combined with any of the above, but must appear as the very last operation. Ordering is done with a natural sort.
|
||||
|
||||
@@ -9,14 +9,6 @@ noderemove(8) -- Remove nodes from the confluent management service
|
||||
|
||||
`noderemove` simply removes the given noderange from the confluent database.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to delete, prompting if over the
|
||||
threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
@@ -30,3 +22,4 @@ noderemove(8) -- Remove nodes from the confluent management service
|
||||
`r2u3: deleted`
|
||||
`r2u2: deleted`
|
||||
`r2u1: deleted`
|
||||
|
||||
|
||||
@@ -11,16 +11,9 @@ nodereseat(8) -- Request a reseat of a node
|
||||
node's slot. This should be equivalent to removing the system entirely from
|
||||
the chassis and putting it back in, but without actually having to do so.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to reseat, prompting if over the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Reseating the node `s1`:
|
||||
`# nodereseat s1`
|
||||
`s1: Reseat successful`
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
nodersync(8) -- Run rsync in parallel against a noderange
|
||||
=========================================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodersync <file/directorylist> <noderange>:<destination>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Supervises execution of rsync to push files or a directory tree to the specified
|
||||
noderange. This will present progress as percentage for all nodes.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-f COUNT`, `-c COUNT`, `--count=COUNT`:
|
||||
Specify how many rsync executions to do concurrently. If noderange
|
||||
exceeds the count, then excess nodes will wait until one of the
|
||||
active count completes.
|
||||
|
||||
* `-s`, `--substitutename`:
|
||||
'Use a different name other than the nodename for rsync'
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to run rsync to, prompting if over the
|
||||
threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
@@ -3,7 +3,7 @@ noderun(8) -- Run arbitrary commands per node in a noderange
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`noderun [options] <noderange> <command expression>`
|
||||
`noderun <noderange> <command string>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -13,21 +13,6 @@ nodeattribexpressions(5) are expanded prior to execution of the command. For
|
||||
noderun, the commands are locally executed. To execute commands on the nodes
|
||||
themselves, see nodeshell(8).
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-f COUNT`, `-c COUNT`, `--count=COUNT`:
|
||||
Number of commands to run at a time
|
||||
|
||||
* `-n`, `--nonodeprefix`:
|
||||
Do not prefix output with node names
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to run the command with, prompting if over
|
||||
the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Run ping against nodes n1 through n4:
|
||||
|
||||
@@ -29,10 +29,6 @@ interval of 1 second is used.
|
||||
Perform the specified number of readings, waiting `-i` indicated interval or 1 second if not
|
||||
otherwise indicated.
|
||||
|
||||
* `-s`, `--skipnumberless`:
|
||||
Return only sensors that provide numeric data (e.g. temperature, fanspeed),
|
||||
ignoring sensors that do not provide numeric data.
|
||||
|
||||
## EXAMPLES
|
||||
* Retrieving all temperature related sensors from one system
|
||||
`# nodesensors n1 temperature`
|
||||
|
||||
@@ -2,8 +2,9 @@ nodesetboot(8) -- Check or set next boot device for noderange
|
||||
====================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodesetboot [options] <noderange> [default|cd|network|setup|hd|usb|floppy]`
|
||||
|
||||
`nodesetboot <noderange>`
|
||||
`nodesetboot [options] <noderange> [default|cd|network|setup|hd]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -20,8 +21,6 @@ to use `nodesetboot <noderange> setup` and then initiate a reboot from within
|
||||
the operating system with ssh or similar rather than using the remote hardware
|
||||
control.
|
||||
|
||||
Running the command with no target queries the current setting.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-b`, `--bios`:
|
||||
@@ -34,13 +33,6 @@ Running the command with no target queries the current setting.
|
||||
|
||||
* `-u`, `--uefi`:
|
||||
This flag does nothing, it is for command compatibility with xCAT's rsetboot
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to modify next boot device, prompting if
|
||||
over the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
* `default`:
|
||||
Request a normal default boot with no particular device override
|
||||
@@ -50,14 +42,6 @@ Running the command with no target queries the current setting.
|
||||
remote media mounted as CD/DVD, and detachable hard disks drives such as usb
|
||||
key devices.
|
||||
|
||||
* `floppy`:
|
||||
Request boot from floppy. Generally speaking firmware uses this to mean a USB
|
||||
flash drive or similar (whether virtual or physical).
|
||||
|
||||
* `usb`:
|
||||
Request boot from usb. Generally speaking firmware uses this to mean a USB
|
||||
flash drive or similar (whether virtual or physical).
|
||||
|
||||
* `network`:
|
||||
Request boot to network
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ nodeshell(8) -- Execute command on many nodes in a noderange through ssh
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodeshell [options] <noderange> <command to execute on each node>`
|
||||
`nodeshell <noderange> [options] <command to execute on each node>`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
@@ -13,29 +13,9 @@ nodeattribexpressions(5). `nodeshell` provides stdout as stdout and stderr
|
||||
as stderr, unlike psh which combines all stdout and stderr into stdout.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-c COUNT`, `-f COUNT`, `--count=COUNT`
|
||||
Specify the maximum number of instances to run concurrently
|
||||
|
||||
* `-l LOGINNAME`, `--loginname=LOGINNAME`
|
||||
Username to use when connecting, defaults to current user.
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`
|
||||
Specify a maximum number of nodes to run remote ssh command to, prompting
|
||||
if over the threshold
|
||||
|
||||
* `-n`, `--nonodeprefix`
|
||||
Do not prefix output with node names
|
||||
|
||||
* `-p PORT`, `--port=PORT`
|
||||
Specify a custom port for ssh
|
||||
|
||||
* `-s SUBSTITUTION`, `--substitutename=SUBSTITITUTION`
|
||||
Specify a substitution name instead of the nodename. If no {} are in the substitution,
|
||||
it is considered to be an append. For example, '-s -ib' would produce 'node1-ib' from 'node1'.
|
||||
Full expression syntax is supported, in which case the substitution is considered to be the entire
|
||||
new name. {node}-ib would be equivalent to -ib. For example, nodeshell -s {bmc} node1
|
||||
would ssh to the BMC instead of the node.
|
||||
* `-c`:
|
||||
Specify the maximum number of instances to run concurrently.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
|
||||
@@ -3,15 +3,12 @@ nodestorage(8) -- Examine/Modify storage configuration of a node
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`nodestorage <noderange> [show|create|delete|diskset] [hotspare|jbod|unconfigured] [options]`
|
||||
`nodestorage <noderange> [show|create|delete] [options]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
`nodestorage` provides access to the remote storage configuration of
|
||||
the noderange. The `show` subcommand will show current storage configuration,
|
||||
`create` can be used to create new arrays or volumes, `delete` can be used to
|
||||
remove volumes and arrays, and `diskset` can modify the usage of disks
|
||||
indicated by `-d` to either be `unconfigured`, `jbod`, or `hotspare`.
|
||||
the noderange.
|
||||
|
||||
## OPTIONS
|
||||
* `-r` **RAIDLEVEL**, `--raidlevel`=**RAIDLEVEL**:
|
||||
@@ -32,23 +29,14 @@ indicated by `-d` to either be `unconfigured`, `jbod`, or `hotspare`.
|
||||
volumes, or selecting a volume for delete. Default
|
||||
behavior is to use implementation provided default
|
||||
|
||||
* `-z` **STRIPSIZES**, `--stripsizes`=**STRIPSIZES**:
|
||||
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
|
||||
|
||||
* `-m` **MAXNODES**, `--maxnodes`=**MAXNODES**:
|
||||
Specify a maximum number of nodes to configure storage on, prompting
|
||||
if over the threshold
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Deleting the volume `somedata`:
|
||||
`$ nodestorage d5 delete somedata`
|
||||
`Deleted: somedata`
|
||||
|
||||
* Creating a raid5 of 4 disks and a volume named `somedata`:
|
||||
`$ nodestorage d5 create -r 5 -d drive0,drive_1,drive_2,drive_3 -n somedata`
|
||||
* Creating a raid5 of 4 disks and a volume named `somedata`:
|
||||
`$ nodestorage d5 create -r 5 -d drive0,drive_1,drive_2,drive_3 -n somedata
|
||||
`d5: Volume somedata: Size: 1.905 TB`
|
||||
`d5: Volume somedata: State: Optimal`
|
||||
`d5: Volume somedata: Array 1-2`
|
||||
|
||||
@@ -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,18 +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.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-m MAXNODES`, `--maxnodes=MAXNODES`:
|
||||
Specify a maximum number of nodes to download diagnostic data from, prompting
|
||||
if over the threshold
|
||||
|
||||
* `-h`, `--help`:
|
||||
Show help message and exit
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
* Download support data from a single node to a specific filename
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
osdeploy(8) --- Configure general OS deployment facilities of confluent
|
||||
==========================================================================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`osdeploy import <iso>`
|
||||
`osdeploy updateboot <profile>`
|
||||
`osdeploy rebase <profile>`
|
||||
`osdeploy initialize [-h] [-g] [-u] [-s] [-k] [-t] [-p] [-i] [-l] [-a]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
**osdeploy** manages the facilities and os deployment content of a confluent server. The
|
||||
`import` subcommand will generate profiles from an iso image. `updateboot` will take any
|
||||
changes in the specified update that need to be pushed into boot configuration and/or images
|
||||
and ensure those needed changes are performed. `initialize` provides assistance in setting
|
||||
up the most commonly required facilities. `rebase` has confluent attempt to update profile
|
||||
content that came from /opt/confluent in an rpm update, if profile supports it.
|
||||
Run `osdeploy initialize -h` for more detail on the options offered by `osdeploy initialize`.
|
||||
@@ -1,49 +0,0 @@
|
||||
stats(8) -- Common basic statistics on typical numeric data in output
|
||||
==============================
|
||||
|
||||
## SYNOPSIS
|
||||
|
||||
`<other command> | stats [-c N] [-d D] [-x|-g|-t|-o image.png] [-s N] [-v] [-b N]
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
The **stats** command helps analyze common numerical data such as performance numbers
|
||||
or temperatures or any other numerical value.
|
||||
|
||||
By default it looks for the last numerical output on the first line to identify the numerical column
|
||||
and analyze that number. This can be overriden by **-c COLUMN** to indicate a column. By default,
|
||||
whitespace and commas are treated to delimit columns, and **-d DELIMITER** can be used to override.
|
||||
|
||||
By default it outputs basic statistics, but a histogram is available either text or through X11 output
|
||||
or sixel or output to an image file depending on whether **-x**, **-g**, **-t*, or **-o image.png** is
|
||||
used.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
* `-c N`:
|
||||
Select column number. Defaults to last column that appears numeric
|
||||
|
||||
* `-d D`:
|
||||
Specify a custom column delimiter
|
||||
|
||||
* `-x`:
|
||||
Output in Sixel format (supported by mlterm and PuTTY, among others)
|
||||
|
||||
* `-g`:
|
||||
Try to open histogram as an X window
|
||||
|
||||
* `-t`:
|
||||
Output histogram as bars rendered by =
|
||||
|
||||
* `-o image.png`:
|
||||
Write graphical histogram to image.png.
|
||||
|
||||
* `-s N`:
|
||||
Ignore specified number of lines as header content before processing numbers
|
||||
|
||||
* `-v`:
|
||||
Treat value before : on each line as a label, and show which labels belong to which histogram buckets.
|
||||
|
||||
* `-b N`:
|
||||
Specify a custom number of buckets for histogram. The default is 10.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#!/bin/sh
|
||||
cd `dirname $0`
|
||||
python3 addattribs.py || python2 addattribs.py
|
||||
cd `dirname $0`/doc/man
|
||||
mkdir -p ../../man/man1
|
||||
mkdir -p ../../man/man5
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This is a sample python script for going through all observed mac addresses
|
||||
# and assuming they are BMC related and printing nodeattrib commands
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
from setuptools import setup
|
||||
import os
|
||||
|
||||
data_files = [('/etc/profile.d', ['confluent_env.sh', 'confluent_env.csh']),
|
||||
('/opt/confluent/share/licenses/confluent_client/', ['LICENSE', 'COPYRIGHT'])
|
||||
]
|
||||
data_files = [('/etc/profile.d', ['confluent_env.sh', 'confluent_env.csh'])]
|
||||
try:
|
||||
scriptlist = ['bin/{0}'.format(d) for d in os.listdir('bin/')]
|
||||
data_files.append(('/opt/confluent/share/man/man1', ['man/man1/' + x for x in os.listdir('man/man1')]))
|
||||
@@ -15,14 +13,10 @@ except OSError:
|
||||
setup(
|
||||
name='confluent_client',
|
||||
version='#VERSION#',
|
||||
author='Lenovo',
|
||||
author='Jarrod Johnson',
|
||||
author_email='jjohnson2@lenovo.com',
|
||||
url='http://github.com/lenovo/confluent/',
|
||||
license='Apache-2.0',
|
||||
description='Command line client and libraries for confluent management server',
|
||||
long_description='Command line client and libraries for confluent management server',
|
||||
url='http://xcat.sf.net/',
|
||||
packages=['confluent'],
|
||||
platforms=['Linux'],
|
||||
scripts=scriptlist,
|
||||
data_files=data_files,
|
||||
)
|
||||
|
||||
@@ -22,10 +22,10 @@ This provides the modules common for both client and server
|
||||
%setup -n %{name}-%{version} -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
python2 setup.py build
|
||||
python setup.py build
|
||||
|
||||
%install
|
||||
python2 setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-purelib=/opt/confluent/lib/python --install-scripts=/opt/confluent/bin
|
||||
python setup.py install --single-version-externally-managed -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES --install-purelib=/opt/confluent/lib/python --install-scripts=/opt/confluent/bin
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
VERSION=`git describe|cut -d- -f 1`
|
||||
NUMCOMMITS=`git describe|cut -d- -f 2`
|
||||
if [ "$NUMCOMMITS" != "$VERSION" ]; then
|
||||
LASTNUM=$(echo $VERSION|rev|cut -d . -f 1|rev)
|
||||
LASTNUM=$((LASTNUM+1))
|
||||
FIRSTPART=$(echo $VERSION|rev|cut -d . -f 2- |rev)
|
||||
VERSION=${FIRSTPART}.${LASTNUM}
|
||||
VERSION=$VERSION~dev$NUMCOMMITS+`git describe|cut -d- -f 3`
|
||||
fi
|
||||
sed -e "s/#VERSION#/$VERSION/" confluent_osdeploy.spec.tmpl > confluent_osdeploy.spec
|
||||
cd ..
|
||||
cp ../LICENSE .
|
||||
tar Jcvf confluent_osdeploy.tar.xz confluent_osdeploy
|
||||
mv confluent_osdeploy.tar.xz ~/rpmbuild/SOURCES/
|
||||
cd -
|
||||
mkdir -p el9bin/opt/confluent/bin
|
||||
mkdir -p el9bin/stateless-bin
|
||||
podman run --privileged --rm -v $(pwd)/utils:/buildutils -i -t fedorabuild make -C /buildutils
|
||||
cd utils
|
||||
cp confluent_imginfo copernicus clortho autocons ../el9bin/opt/confluent/bin
|
||||
cp start_root urlmount ../el9bin/stateless-bin/
|
||||
make clean
|
||||
cd ..
|
||||
mkdir -p el8bin/opt/confluent/bin
|
||||
mkdir -p el8bin/stateless-bin
|
||||
podman run --privileged --rm -v $(pwd)/utils:/buildutils -i -t el7build make -C /buildutils
|
||||
cd utils
|
||||
cp confluent_imginfo copernicus clortho autocons ../el8bin/opt/confluent/bin
|
||||
cp start_root urlmount ../el8bin/stateless-bin/
|
||||
make clean
|
||||
cd ..
|
||||
tar Jcvf confluent_el9bin.tar.xz el9bin/
|
||||
tar Jcvf confluent_el8bin.tar.xz el8bin/
|
||||
mv confluent_el8bin.tar.xz ~/rpmbuild/SOURCES/
|
||||
mv confluent_el9bin.tar.xz ~/rpmbuild/SOURCES/
|
||||
rm -rf el9bin
|
||||
rm -rf el8bin
|
||||
rpmbuild -ba confluent_osdeploy.spec
|
||||
@@ -1,38 +0,0 @@
|
||||
cd $(dirname $0)
|
||||
VERSION=`git describe|cut -d- -f 1`
|
||||
NUMCOMMITS=`git describe|cut -d- -f 2`
|
||||
if [ "$NUMCOMMITS" != "$VERSION" ]; then
|
||||
LASTNUM=$(echo $VERSION|rev|cut -d . -f 1|rev)
|
||||
LASTNUM=$((LASTNUM+1))
|
||||
FIRSTPART=$(echo $VERSION|rev|cut -d . -f 2- |rev)
|
||||
VERSION=${FIRSTPART}.${LASTNUM}
|
||||
VERSION=$VERSION~dev$NUMCOMMITS+`git describe|cut -d- -f 3`
|
||||
fi
|
||||
sed -e "s/#VERSION#/$VERSION/" confluent_osdeploy-aarch64.spec.tmpl > confluent_osdeploy-aarch64.spec
|
||||
cd ..
|
||||
cp ../LICENSE .
|
||||
tar Jcvf confluent_osdeploy.tar.xz confluent_osdeploy
|
||||
mv confluent_osdeploy.tar.xz ~/rpmbuild/SOURCES/
|
||||
cd -
|
||||
mkdir -p el9bin/opt/confluent/bin
|
||||
mkdir -p el9bin/stateless-bin
|
||||
mkdir -p el8bin/opt/confluent/bin
|
||||
mkdir -p el8bin/stateless-bin
|
||||
podman run --privileged --rm -v $(pwd)/utils:/buildutils -i -t el9builder make -C /buildutils
|
||||
cd utils
|
||||
mv confluent_imginfo copernicus clortho autocons ../el9bin/opt/confluent/bin
|
||||
mv start_root urlmount ../el9bin/stateless-bin/
|
||||
cd ..
|
||||
podman run --privileged --rm -v $(pwd)/utils:/buildutils -i -t el8builder make -C /buildutils
|
||||
cd utils
|
||||
mv confluent_imginfo copernicus clortho autocons ../el8bin/opt/confluent/bin
|
||||
mv start_root urlmount ../el8bin/stateless-bin/
|
||||
cd ..
|
||||
tar Jcvf confluent_el9bin.tar.xz el9bin/
|
||||
tar Jcvf confluent_el8bin.tar.xz el8bin/
|
||||
mv confluent_el8bin.tar.xz ~/rpmbuild/SOURCES/
|
||||
mv confluent_el9bin.tar.xz ~/rpmbuild/SOURCES/
|
||||
rm -rf el9bin
|
||||
rm -rf el8bin
|
||||
podman run --privileged --rm -v $HOME:/root el8builder rpmbuild -ba /root/confluent/confluent_osdeploy/confluent_osdeploy-aarch64.spec
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user