diff --git a/confluent_server/confluent/core.py b/confluent_server/confluent/core.py index e5003ef9..a6492611 100644 --- a/confluent_server/confluent/core.py +++ b/confluent_server/confluent/core.py @@ -233,6 +233,14 @@ def _init_core(): 'pluginattrs': ['hardwaremanagement.method'], 'default': 'ipmi', }), + 'updates': { + 'active': { + PluginCollection({ + 'pluginattrs': ['hardwaremanagement.method'], + 'default': 'ipmi', + }), + }, + }, }, }, 'power': { diff --git a/confluent_server/confluent/firmwaremanager.py b/confluent_server/confluent/firmwaremanager.py new file mode 100644 index 00000000..50aad2b8 --- /dev/null +++ b/confluent_server/confluent/firmwaremanager.py @@ -0,0 +1,62 @@ +# 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. + +# provide managing firmware update process and firmware repository if/when +# the time comes + +import confluent.messages as msg +import eventlet + +updatesbytarget = {} + +def execupdate(handler, filename, updateobj): + try: + handler(filename, progress=updateobj.handle_progress) + updateobj.handle_progress({'phase': 'complete', 'progress': 100.0}) + except Exception as e: + updateobj.handle_progress({'phase': 'error', 'progress': 0.0, + 'detail': repr(e)}) + +class Updater(object): + def __init__(self, node, handler, filename, tenant=None, name=None): + self.node = node + self.phase = 'initializing' + self.percent = 0.0 + self.updateproc = eventlet.spawn(execupdate, handler, filename, self) + if (node, tenant) not in updatesbytarget: + updatesbytarget[(node, tenant)] = {} + if name is None: + name = 1 + while name in updatesbytarget[(node, tenant)]: + name += 1 + updatesbytarget[(node, tenant)][name] = self + + def handle_progress(self, progress): + self.phase = progress['phase'] + self.percent = float(progress['progress']) + self.detail = progress.get('detail', '') + + @property + def progress(self): + return {'phase': self.phase, 'progress': self.percent, + 'detail': self.detail} + + +def list_updates(nodes, tenant=None): + for node in nodes: + for updateid in updatesbytarget.get((node, None), {}): + yield msg.ChildCollection(updateid) diff --git a/confluent_server/confluent/messages.py b/confluent_server/confluent/messages.py index ac26d3da..a4c14144 100644 --- a/confluent_server/confluent/messages.py +++ b/confluent_server/confluent/messages.py @@ -389,8 +389,17 @@ def get_input_message(path, operation, inputdata, nodes=None, multinode=False): elif (path[:4] == ['configuration', 'management_controller', 'ntp', 'servers'] and operation != 'retrieve' and len(path) == 5): return InputNTPServer(path, nodes, inputdata) + elif 'inventory/firmware/updates/active' in '/'.join(path) and inputdata: + return InputFirmwareUpdate(path, nodes, inputdata) elif inputdata: - raise exc.InvalidArgumentException() + raise exc.InvalidArgumentException( + 'No known input handler for request') + +class InputFirmwareUpdate(ConfluentMessage): + + def __init__(self, path, nodes, inputdata): + self.filename = inputdata['filename'] + self.nodes = nodes class InputAlertData(ConfluentMessage): diff --git a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py index c5c1d72d..b724e1eb 100644 --- a/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py +++ b/confluent_server/confluent/plugins/hardwaremanagement/ipmi.py @@ -15,6 +15,7 @@ import atexit import confluent.exceptions as exc +import confluent.firmwaremanager as firmwaremanager import confluent.interface.console as conapi import confluent.messages as msg import confluent.util as util @@ -350,6 +351,7 @@ class IpmiHandler(object): connparams = get_conn_params(node, self.cfg) self.ipmicmd = None self.inputdata = inputdata + self.tenant = cfg.tenant tenant = cfg.tenant if ((node, tenant) not in persistent_ipmicmds or not persistent_ipmicmds[(node, tenant)].ipmi_session.logged): @@ -417,6 +419,8 @@ class IpmiHandler(object): self.handle_sensors() elif self.element[0] == 'configuration': self.handle_configuration() + elif self.element[:3] == ['inventory', 'firmware', 'updates']: + self.handle_update() elif self.element[0] == 'inventory': self.handle_inventory() elif self.element == ['events', 'hardware', 'log']: @@ -428,6 +432,11 @@ class IpmiHandler(object): else: raise Exception('Not Implemented') + def handle_update(self): + firmwaremanager.Updater(self.node, self.ipmicmd.update_firmware, + self.inputdata.filename, self.tenant) + + def handle_configuration(self): if self.element[1:3] == ['management_controller', 'alerts']: return self.handle_alerts() @@ -961,9 +970,16 @@ def update(nodes, element, configmanager, inputdata): return create(nodes, element, configmanager, inputdata) +def list_active_updates(nodes, configmanager): + raise Exception('Not Implemented') + + def retrieve(nodes, element, configmanager, inputdata): initthread() - return perform_requests('read', nodes, element, configmanager, inputdata) + if '/'.join(element).startswith('inventory/firmware/updates/active'): + return firmwaremanager.list_updates(nodes, configmanager.tenant) + else: + return perform_requests('read', nodes, element, configmanager, inputdata) def delete(nodes, element, configmanager, inputdata): initthread()