diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py index 741db4fa..d7c98187 100644 --- a/confluent_server/confluent/core.py +++ b/confluent_server/confluent/core.py @@ -382,6 +382,11 @@ def _init_core(): 'pluginattrs': ['hardwaremanagement.method'], 'default': 'ipmi', }), + 'deployment': { + 'ident_img': PluginRoute({ + 'handler': 'identimage' + }) + }, 'events': { 'hardware': { 'log': PluginRoute({ diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index f08743f6..396cd58f 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -526,6 +526,8 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False, elif '/'.join(path).startswith( 'configuration/management_controller/licenses') and inputdata: return InputLicense(path, nodes, inputdata, configmanager) + elif path == ['deployment', 'ident_image']: + return InputIdentImage(path, nodes, inputdata) elif inputdata: raise exc.InvalidArgumentException( 'No known input handler for request') @@ -887,6 +889,12 @@ class ConfluentInputMessage(ConfluentMessage): def is_valid_key(self, key): return key in self.valid_values + +class InputIdentImage(ConfluentInputMessage): + keyname = 'ident_image' + valid_values = ['create'] + + class InputIdentifyMessage(ConfluentInputMessage): valid_values = set([ 'on', diff --git a/confluent_server/confluent/netutil.py b/confluent_server/confluent/netutil.py index 41138e4c..8f8254ee 100644 --- a/confluent_server/confluent/netutil.py +++ b/confluent_server/confluent/netutil.py @@ -274,6 +274,19 @@ class NetManager(object): myattribs['current_nic'] = False +def get_flat_net_config(configmanager, node): + fnc = get_full_net_config(configmanager, node) + dft = fnc.get('default', {}) + if dft: + ret = [dft] + else: + ret = [] + for nc in fnc.get('extranets', {}): + nc = fnc['extranets'][nc] + if nc: + ret.append(nc) + return ret + def get_full_net_config(configmanager, node, serverip=None): cfd = configmanager.get_node_attributes(node, ['net.*']) cfd = cfd.get(node, {}) diff --git a/confluent_server/confluent/plugins/deployment/identimage.py b/confluent_server/confluent/plugins/deployment/identimage.py new file mode 100644 index 00000000..4959a63e --- /dev/null +++ b/confluent_server/confluent/plugins/deployment/identimage.py @@ -0,0 +1,76 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2022 Lenovo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This plugin provides an ssh implementation comforming to the 'console' +# specification. consoleserver or shellserver would be equally likely +# to use this. +import confluent.messages as msg +import confluent.netutil as netutil +import eventlet.green.subprocess as subprocess +import os +import shutil +import tempfile +import socket +import yaml +import json + +def mkdirp(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != 17: + raise + +def create_apikey(): + alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./' + newpass = ''.join([alpha[x >> 2] for x in bytearray(os.urandom(32))]) + return newpass + + +def create_ident_image(node, configmanager): + tmpd = tempfile.mkdtemp() + ident = { 'nodename': node } + apikey = create_apikey() + configmanager.set_node_attributes({node: {'secret.selfapiarmtoken': apikey}}) + ident['apitoken'] = apikey + # This particular mechanism does not (yet) do anything smart with collective + # It would be a reasonable enhancement to list all collective server addresses + # restricted by 'managercandidates' + ident['deploy_servers'] = [] + for myaddr in netutil.get_my_addresses(): + myaddr = socket.inet_ntop(myaddr[0], myaddr[1]) + ident['deploy_servers'].append(myaddr) + ident['net_cfgs'] = netutil.get_flat_net_config(configmanager, node) + with open(os.path.join(tmpd, 'cnflnt.yml'), 'w') as yamlout: + yaml.safe_dump(ident, yamlout, default_flow_style=False) + with open(os.path.join(tmpd, 'cnflnt.jsn'), 'w') as jsonout: + json.dump(ident, jsonout) + mkdirp('/var/lib/confluent/private/identity_images/') + imgname = '/var/lib/confluent/private/identity_images/{0}.img'.format(node) + if os.path.exists(imgname): + os.remove(imgname) + subprocess.check_call(['/opt/confluent/bin/dir2img', tmpd, imgname, 'cnflnt_idnt']) + shutil.rmtree(tmpd) + + +def update(nodes, element, configmanager, inputdata): + for node in nodes: + create_ident_image(node, configmanager) + yield msg.CreatedResource( + 'nodes/{0}/deployment/ident_image'.format(node)) + +