mirror of
https://github.com/xcat2/confluent.git
synced 2025-08-11 05:50:16 +00:00
Implement screenshot via nodeconsole -s
This will grab screenshots from Lenovo systems and output them to the console, using the kitty image protocol.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import base64
|
||||
import optparse
|
||||
import os
|
||||
import subprocess
|
||||
@@ -46,6 +47,8 @@ argparser.add_option('-l', '--log', action='store_true', default=False,
|
||||
argparser.add_option('-T', '--Timestamp', action='store_true', default=False,
|
||||
help= 'Dump log in stdout with timestamps')
|
||||
|
||||
argparser.add_option('-s', '--screenshot', action='store_true', default=False,
|
||||
help='Attempt to grab screenshot and render using kitty image protocol')
|
||||
argparser.add_option('-w','--windowed', action='store_true', default=False,
|
||||
help='Open terminal windows for each node. The '
|
||||
'environment variable NODECONSOLE_WINDOWED_COMMAND '
|
||||
@@ -69,6 +72,16 @@ argparser.add_option('-w','--windowed', action='store_true', default=False,
|
||||
|
||||
(options, args) = argparser.parse_args()
|
||||
|
||||
def kitty_draw(data):
|
||||
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')
|
||||
|
||||
pass_through_args = []
|
||||
killcon = False
|
||||
try:
|
||||
@@ -106,6 +119,16 @@ if options.Timestamp:
|
||||
logreader.dump_to_console(logname)
|
||||
sys.exit(0)
|
||||
|
||||
if options.screenshot:
|
||||
sess = client.Command()
|
||||
for res in sess.read('/noderange/{}/console/ikvm_screenshot'.format(args[0])):
|
||||
for node in res.get('databynode', {}):
|
||||
imgdata = res['databynode'][node]['image']['imgdata']
|
||||
sys.stdout.write('{}: '.format(node))
|
||||
kitty_draw(imgdata.encode())
|
||||
sys.stdout.write('\n')
|
||||
sys.exit(0)
|
||||
|
||||
def kill(noderange):
|
||||
sess = client.Command()
|
||||
envstring=os.environ.get('NODECONSOLE_WINDOWED_COMMAND')
|
||||
|
@@ -442,6 +442,10 @@ def _init_core():
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
'ikvm_screenshot': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
'default': 'ipmi',
|
||||
}),
|
||||
},
|
||||
'description': PluginRoute({
|
||||
'pluginattrs': ['hardwaremanagement.method'],
|
||||
|
@@ -18,6 +18,7 @@
|
||||
# This module implements client/server messages emitted from plugins.
|
||||
# Things are defined here to 'encourage' developers to coordinate information
|
||||
# format. This is also how different data formats are supported
|
||||
import base64
|
||||
import confluent.exceptions as exc
|
||||
import confluent.config.configmanager as cfm
|
||||
import confluent.config.conf as cfgfile
|
||||
@@ -1882,6 +1883,12 @@ class GraphicalConsole(ConfluentMessage):
|
||||
else:
|
||||
self.kvpairs = {name: {'Launcher': kv}}
|
||||
|
||||
class ScreenShot(ConfluentMessage):
|
||||
readonly = True
|
||||
def __init__(self, imgdata, node, imgformat=None):
|
||||
self.kvpairs = {node: {'image': {'imgformat': imgformat, 'imgdata': base64.b64encode(imgdata)}}}
|
||||
|
||||
|
||||
class CryptedAttributes(Attributes):
|
||||
defaulttype = 'password'
|
||||
|
||||
|
@@ -28,6 +28,7 @@ import eventlet.greenpool as greenpool
|
||||
import eventlet.queue as queue
|
||||
import eventlet.support.greendns
|
||||
from fnmatch import fnmatch
|
||||
import io
|
||||
import os
|
||||
import pwd
|
||||
import pyghmi.constants as pygconstants
|
||||
@@ -51,6 +52,14 @@ except NameError:
|
||||
|
||||
pci_cache = {}
|
||||
|
||||
class RetainedIO(io.BytesIO):
|
||||
# Need to retain buffer after close
|
||||
def __init__(self):
|
||||
self.resultbuffer = None
|
||||
def close(self):
|
||||
self.resultbuffer = self.getbuffer()
|
||||
super().close()
|
||||
|
||||
def get_dns_txt(qstring):
|
||||
return eventlet.support.greendns.resolver.query(
|
||||
qstring, 'TXT')[0].strings[0].replace('i=', '')
|
||||
@@ -607,6 +616,8 @@ class IpmiHandler(object):
|
||||
self.handle_description()
|
||||
elif self.element == ['console', 'ikvm_methods']:
|
||||
self.handle_ikvm_methods()
|
||||
elif self.element == ['console', 'ikvm_screenshot']:
|
||||
self.handle_ikvm_screenshot()
|
||||
elif self.element == ['console', 'ikvm']:
|
||||
self.handle_ikvm()
|
||||
else:
|
||||
@@ -1641,6 +1652,14 @@ class IpmiHandler(object):
|
||||
dsc = {'ikvm_methods': dsc}
|
||||
self.output.put(msg.KeyValueData(dsc, self.node))
|
||||
|
||||
def handle_ikvm_screenshot(self):
|
||||
# good background for the webui, and kitty
|
||||
imgdata = RetainedIO()
|
||||
imgformat = self.ipmicmd.get_screenshot(imgdata)
|
||||
imgdata = imgdata.getvalue()
|
||||
if imgdata:
|
||||
self.output.put(msg.ScreenShot(imgdata, self.node, imgformat=imgformat))
|
||||
|
||||
def handle_ikvm(self):
|
||||
methods = self.ipmicmd.get_ikvm_methods()
|
||||
if 'openbmc' in methods:
|
||||
|
@@ -27,6 +27,7 @@ import eventlet.greenpool as greenpool
|
||||
import eventlet.queue as queue
|
||||
import eventlet.support.greendns
|
||||
from fnmatch import fnmatch
|
||||
import io
|
||||
import os
|
||||
import pwd
|
||||
import pyghmi.constants as pygconstants
|
||||
@@ -42,6 +43,14 @@ if not hasattr(ssl, 'SSLEOFError'):
|
||||
|
||||
pci_cache = {}
|
||||
|
||||
class RetainedIO(io.BytesIO):
|
||||
# Need to retain buffer after close
|
||||
def __init__(self):
|
||||
self.resultbuffer = None
|
||||
def close(self):
|
||||
self.resultbuffer = self.getbuffer()
|
||||
super().close()
|
||||
|
||||
def get_dns_txt(qstring):
|
||||
return eventlet.support.greendns.resolver.query(
|
||||
qstring, 'TXT')[0].strings[0].replace('i=', '')
|
||||
@@ -464,6 +473,8 @@ class IpmiHandler(object):
|
||||
self.handle_description()
|
||||
elif self.element == ['console', 'ikvm_methods']:
|
||||
self.handle_ikvm_methods()
|
||||
elif self.element == ['console', 'ikvm_screenshot']:
|
||||
self.handle_ikvm_screenshot()
|
||||
elif self.element == ['console', 'ikvm']:
|
||||
self.handle_ikvm()
|
||||
else:
|
||||
@@ -1498,6 +1509,14 @@ class IpmiHandler(object):
|
||||
dsc = {'ikvm_methods': dsc}
|
||||
self.output.put(msg.KeyValueData(dsc, self.node))
|
||||
|
||||
def handle_ikvm_screenshot(self):
|
||||
# good background for the webui, and kitty
|
||||
imgdata = RetainedIO()
|
||||
imgformat = self.ipmicmd.get_screenshot(imgdata)
|
||||
imgdata = imgdata.getvalue()
|
||||
if imgdata:
|
||||
self.output.put(msg.ScreenShot(imgdata, self.node, imgformat=imgformat))
|
||||
|
||||
def handle_ikvm(self):
|
||||
methods = self.ipmicmd.get_ikvm_methods()
|
||||
if 'openbmc' in methods:
|
||||
|
Reference in New Issue
Block a user