]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
node-proxy: migrate to cephadm-agent
authorGuillaume Abrioux <gabrioux@ibm.com>
Thu, 17 Aug 2023 09:21:00 +0000 (11:21 +0200)
committerGuillaume Abrioux <gabrioux@ibm.com>
Thu, 25 Jan 2024 14:53:31 +0000 (14:53 +0000)
This moves the existing files to the new directory 'cephadmlib' so
we can make the existing code for node-proxy run within the cephadm
agent. Indeed, we can leverage the existing code for the cephadm agent
given that both daemons would achieve the same thing.

Signed-off-by: Guillaume Abrioux <gabrioux@ibm.com>
(cherry picked from commit 83661b6c1a25b2d40f3cefa9f5de094c644a1e4e)

42 files changed:
src/cephadm/cephadm.py
src/cephadm/cephadmlib/node_proxy/baseclient.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/basesystem.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/data.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/fake_cephadm/cephadm_mgr_module.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/main.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish-test.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_client.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_dell.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/interface_sample [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/interfaces_sample [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/memory [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/memory_socket [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/processor [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/processors [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/storage_sample [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_json_samples/system [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/redfish_system.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/reporter.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/server.py [new file with mode: 0644]
src/cephadm/cephadmlib/node_proxy/util.py [new file with mode: 0644]
src/cephadm/node_proxy/baseclient.py [deleted file]
src/cephadm/node_proxy/basesystem.py [deleted file]
src/cephadm/node_proxy/data.py [deleted file]
src/cephadm/node_proxy/fake_cephadm/cephadm_mgr_module.py [deleted file]
src/cephadm/node_proxy/main.py [deleted file]
src/cephadm/node_proxy/redfish-test.py [deleted file]
src/cephadm/node_proxy/redfish_client.py [deleted file]
src/cephadm/node_proxy/redfish_dell.py [deleted file]
src/cephadm/node_proxy/redfish_json_samples/interface_sample [deleted file]
src/cephadm/node_proxy/redfish_json_samples/interfaces_sample [deleted file]
src/cephadm/node_proxy/redfish_json_samples/memory [deleted file]
src/cephadm/node_proxy/redfish_json_samples/memory_socket [deleted file]
src/cephadm/node_proxy/redfish_json_samples/processor [deleted file]
src/cephadm/node_proxy/redfish_json_samples/processors [deleted file]
src/cephadm/node_proxy/redfish_json_samples/storage_sample [deleted file]
src/cephadm/node_proxy/redfish_json_samples/system [deleted file]
src/cephadm/node_proxy/redfish_system.py [deleted file]
src/cephadm/node_proxy/reporter.py [deleted file]
src/cephadm/node_proxy/requirements.txt [deleted file]
src/cephadm/node_proxy/server.py [deleted file]
src/cephadm/node_proxy/util.py [deleted file]

index bcb82c4c4d4bf065063b21d764750aa76372c616..d8af9d3b9185ea20296f0b4b10d416cb6d9fd932 100755 (executable)
@@ -40,6 +40,7 @@ from threading import Thread, Event
 from urllib.error import HTTPError, URLError
 from urllib.request import urlopen, Request
 from pathlib import Path
+import cephadmlib.node_proxy.server
 
 FuncT = TypeVar('FuncT', bound=Callable)
 
@@ -4895,6 +4896,9 @@ WantedBy=ceph-{fsid}.target
     def run(self) -> None:
         self.pull_conf_settings()
 
+        t_node_proxy = Thread(target=cephadmlib.node_proxy.server.main)
+        t_node_proxy.start()
+
         try:
             for _ in range(1001):
                 if not port_in_use(self.ctx, EndPoint('0.0.0.0', self.starting_port)):
diff --git a/src/cephadm/cephadmlib/node_proxy/baseclient.py b/src/cephadm/cephadmlib/node_proxy/baseclient.py
new file mode 100644 (file)
index 0000000..735dd11
--- /dev/null
@@ -0,0 +1,20 @@
+from typing import Dict
+
+
+class BaseClient:
+    def __init__(self,
+                 host: str,
+                 username: str,
+                 password: str) -> None:
+        self.host = host
+        self.username = username
+        self.password = password
+
+    def login(self) -> None:
+        raise NotImplementedError()
+
+    def logout(self) -> None:
+        raise NotImplementedError()
+
+    def get_path(self, path: str) -> Dict:
+        raise NotImplementedError()
diff --git a/src/cephadm/cephadmlib/node_proxy/basesystem.py b/src/cephadm/cephadmlib/node_proxy/basesystem.py
new file mode 100644 (file)
index 0000000..d3571e9
--- /dev/null
@@ -0,0 +1,46 @@
+from .util import Config
+from typing import Dict, Any
+from .baseclient import BaseClient
+
+
+class BaseSystem:
+    def __init__(self, **kw: Any) -> None:
+        self._system: Dict = {}
+        self.config: Config = kw['config']
+        self.client: BaseClient
+
+    def get_system(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_status(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_metadata(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_processors(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_memory(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_power(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_network(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def get_storage(self) -> Dict[str, Dict[str, Dict]]:
+        raise NotImplementedError()
+
+    def start_update_loop(self) -> None:
+        raise NotImplementedError()
+
+    def stop_update_loop(self) -> None:
+        raise NotImplementedError()
+
+    def start_client(self) -> None:
+        raise NotImplementedError()
+
+    def flush(self) -> None:
+        raise NotImplementedError()
diff --git a/src/cephadm/cephadmlib/node_proxy/data.py b/src/cephadm/cephadmlib/node_proxy/data.py
new file mode 100644 (file)
index 0000000..7033901
--- /dev/null
@@ -0,0 +1,99 @@
+
+system_1 = {
+
+    'metadata': {
+        'name': 'xx',
+        'manufacturer': 'Dell',
+        'model': 'HP PowerEdge',
+        'chassis': 'xxx',
+        'xxx': '',
+    },
+
+    'status': {
+        'State': 'Enabled',
+        'Health': 'OK'
+    },
+
+    'processors': [{
+        'description': '',
+        'cores': '',
+        'threads': '',
+        'type': '',
+        'model': '',
+        'temperature': '',
+        'status': {
+            'State': 'Enabled',
+            'Health': 'OK'
+        }
+    }],
+
+    'memory': {
+        'description': '',
+        'total': 'xx',
+        'status': {
+            'State': 'Enabled',
+            'Health': 'OK'
+        },
+    },
+
+    'network': {
+        'interfaces': [
+            {
+                'type': 'ethernet',
+                'description': 'my ethertnet interface',
+                'name': 'name of the interface',
+                'description': 'description of the interface',
+                'speed_mbps': 'xxx',
+                'status': {
+                    'State': 'Enabled',
+                    'Health': 'OK'
+                },
+            }
+        ]
+    },
+
+    'storage': {
+        'drives': [
+            {
+                'device': 'devc',
+                'description': 'Milk, Cheese, Bread, Fruit, Vegetables',
+                'serial_number': 'xxxxx',
+                'location': '1I:x:y',
+                'interface_type': 'SATA',
+                'model': 'Buy groceries',
+                'type': 'ssd|rotate|nvme',
+                'capacity_bytes': '',
+                'usage_bytes': '',
+                'status': {
+                    'State': 'Enabled',
+                    'Health': 'OK'
+                },
+            }
+        ]
+    },
+
+    'power':
+        [{
+            'type': 'xx',
+            'manufacturer': 'xxx',
+            'model': 'xx',
+            'properties': {},
+            'power_control': 'xx',
+            'status': {
+                'State': 'Enabled',
+                'Health': 'OK'
+            }
+        }],
+
+    'thermal': {
+        'fans': [
+            {
+                'id': 1,
+                'status': {
+                    'State': 'Enabled',
+                    'Health': 'OK'
+                }
+            }
+        ]
+    },
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/fake_cephadm/cephadm_mgr_module.py b/src/cephadm/cephadmlib/node_proxy/fake_cephadm/cephadm_mgr_module.py
new file mode 100644 (file)
index 0000000..6d46de4
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/env python3
+"""
+License: MIT License
+Copyright (c) 2023 Miel Donkers
+
+Very simple HTTP server in python for logging requests
+Usage::
+    ./server.py [<port>]
+"""
+from http.server import BaseHTTPRequestHandler, HTTPServer
+import logging
+
+class S(BaseHTTPRequestHandler):
+    def _set_response(self):
+        self.send_response(200)
+        self.send_header('Content-type', 'text/html')
+        self.end_headers()
+
+    def do_GET(self):
+        logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
+        self._set_response()
+        self.wfile.write("GET request for {}".format(self.path).encode('utf-8'))
+
+    def do_POST(self):
+        content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
+        post_data = self.rfile.read(content_length) # <--- Gets the data itself
+        logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
+                str(self.path), str(self.headers), post_data.decode('utf-8'))
+
+        self._set_response()
+        self.wfile.write("POST request for {}".format(self.path).encode('utf-8'))
+
+def run(server_class=HTTPServer, handler_class=S, port=8000):
+    logging.basicConfig(level=logging.INFO)
+    server_address = ('', port)
+    httpd = server_class(server_address, handler_class)
+    logging.info(f'Starting httpd on port {port}...\n')
+    try:
+        httpd.serve_forever()
+    except KeyboardInterrupt:
+        pass
+    httpd.server_close()
+    logging.info('Stopping httpd...\n')
+
+if __name__ == '__main__':
+    from sys import argv
+
+    if len(argv) == 2:
+        run(port=int(argv[1]))
+    else:
+        run()
diff --git a/src/cephadm/cephadmlib/node_proxy/main.py b/src/cephadm/cephadmlib/node_proxy/main.py
new file mode 100644 (file)
index 0000000..45cd573
--- /dev/null
@@ -0,0 +1,12 @@
+from redfish_system import RedfishSystem
+import time
+
+host = "https://x.x.x.x:8443"
+username = "myuser"
+password = "mypassword"
+
+system = RedfishSystem(host, username, password)
+system.start_update_loop()
+time.sleep(20)
+print(system.get_status())
+system.stop_update_loop()
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish-test.py b/src/cephadm/cephadmlib/node_proxy/redfish-test.py
new file mode 100644 (file)
index 0000000..3aaab2d
--- /dev/null
@@ -0,0 +1,27 @@
+from redfish.rest.v1 import ServerDownOrUnreachableError
+import redfish
+import sys
+
+
+login_host = "https://x.x.x.x:8443"
+login_account = "myuser"
+login_password = "mypassword"
+
+REDFISH_OBJ = redfish.redfish_client(base_url=login_host, username=login_account, password=login_password, default_prefix='/redfish/v1/')
+
+# Login
+try:
+    REDFISH_OBJ.login(auth="session")
+except ServerDownOrUnreachableError as excp:
+    sys.stderr.write("Error: server not reachable or does not support RedFish.\n")
+    sys.exit()
+
+# Get the system information /redfish/v1/Systems/1/SmartStorage/
+# /redfish/v1/Systems/1/Processors/
+# /redfish/v1/Systems/1/Memory/proc1dimm1/
+response = REDFISH_OBJ.get(sys.argv[1])
+# Print the system information
+print(response.dict)
+
+# Logout
+REDFISH_OBJ.logout()
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_client.py b/src/cephadm/cephadmlib/node_proxy/redfish_client.py
new file mode 100644 (file)
index 0000000..26f7135
--- /dev/null
@@ -0,0 +1,54 @@
+from redfish.rest.v1 import ServerDownOrUnreachableError, \
+    SessionCreationError, \
+    InvalidCredentialsError
+import redfish
+import sys
+from .util import Logger
+from .baseclient import BaseClient
+from typing import Dict
+
+log = Logger(__name__)
+
+
+class RedFishClient(BaseClient):
+
+    PREFIX = '/redfish/v1'
+
+    def __init__(self,
+                 host: str,
+                 username: str,
+                 password: str) -> None:
+        log.logger.info("redfish client initialization...")
+        super().__init__(host, username, password)
+        self.redfish_obj: 'redfish.redfish_client' = None
+
+    def login(self) -> 'redfish.redfish_client':
+        self.redfish_obj = redfish.redfish_client(base_url=self.host,
+                                                  username=self.username,
+                                                  password=self.password,
+                                                  default_prefix=self.PREFIX)
+        try:
+            # TODO: add a retry? check for a timeout setting
+            self.redfish_obj.login(auth="session")
+            log.logger.info(f"Logging to redfish api at {self.host} with user: {self.username}")
+            return self.redfish_obj
+        except InvalidCredentialsError as e:
+            log.logger.error(f"Invalid credentials for {self.username} at {self.host}:\n{e}")
+        except (SessionCreationError, ServerDownOrUnreachableError) as e:
+            log.logger.error(f"Server not reachable or does not support RedFish:\n{e}")
+        sys.exit(1)
+
+    def get_path(self, path: str) -> Dict:
+        try:
+            if self.PREFIX not in path:
+                path = f"{self.PREFIX}{path}"
+            log.logger.debug(f"getting: {path}")
+            response = self.redfish_obj.get(path)
+            return response.dict
+        except Exception as e:
+            log.logger.error(f"Error getting path:\n{e}")
+            return {}
+
+    def logout(self) -> None:
+        log.logger.info('logging out...')
+        self.redfish_obj.logout()
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_dell.py b/src/cephadm/cephadmlib/node_proxy/redfish_dell.py
new file mode 100644 (file)
index 0000000..f1cd6f6
--- /dev/null
@@ -0,0 +1,64 @@
+from .redfish_system import RedfishSystem
+from .util import Logger, normalize_dict, to_snake_case
+from typing import Dict, Any
+
+log = Logger(__name__)
+
+
+class RedfishDell(RedfishSystem):
+    def __init__(self, **kw: Any) -> None:
+        if kw.get('system_endpoint') is None:
+            kw['system_endpoint'] = '/Systems/System.Embedded.1'
+        super().__init__(**kw)
+
+    def _update_network(self) -> None:
+        fields = ['Description', 'Name', 'SpeedMbps', 'Status']
+        log.logger.info("Updating network")
+        self._system['network'] = self.build_data(fields, 'EthernetInterfaces')
+
+    def _update_processors(self) -> None:
+        fields = ['Description',
+                  'TotalCores',
+                  'TotalThreads',
+                  'ProcessorType',
+                  'Model',
+                  'Status',
+                  'Manufacturer']
+        log.logger.info("Updating processors")
+        self._system['processors'] = self.build_data(fields, 'Processors')
+
+    def _update_storage(self) -> None:
+        fields = ['Description',
+                  'CapacityBytes',
+                  'Model', 'Protocol',
+                  'SerialNumber', 'Status',
+                  'PhysicalLocation']
+        entities = self.get_members('Storage')
+        log.logger.info("Updating storage")
+        result: Dict[str, Dict[str, Dict]] = dict()
+        for entity in entities:
+            for drive in entity['Drives']:
+                drive_path = drive['@odata.id']
+                drive_info = self._get_path(drive_path)
+                drive_id = drive_info['Id']
+                result[drive_id] = dict()
+                for field in fields:
+                    result[drive_id][to_snake_case(field)] = drive_info[field]
+                    result[drive_id]['entity'] = entity['Id']
+        self._system['storage'] = normalize_dict(result)
+
+    def _update_metadata(self) -> None:
+        log.logger.info("Updating metadata")
+        pass
+
+    def _update_memory(self) -> None:
+        fields = ['Description',
+                  'MemoryDeviceType',
+                  'CapacityMiB',
+                  'Status']
+        log.logger.info("Updating memory")
+        self._system['memory'] = self.build_data(fields, 'Memory')
+
+    def _update_power(self) -> None:
+        log.logger.info("Updating power")
+        pass
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/interface_sample b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/interface_sample
new file mode 100644 (file)
index 0000000..6d351cf
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/EthernetInterfaces/Members/$entity',
+       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/1/',
+       '@odata.type': '#EthernetInterface.1.0.0.EthernetInterface',
+       'Id': '1',
+       'Name': 'System Ethernet Interface',
+       'Oem': {
+               'Hp': {
+                       '@odata.type': '#HpiLOEthernetNetworkInterface.1.0.0.HpiLOEthernetNetworkInterface',
+                       'DHCPv4': None,
+                       'DHCPv6': None,
+                       'IPv4': None,
+                       'IPv6': None,
+                       'SharedNetworkPortOptions': None
+               }
+       },
+       'SettingsResult': None,
+       'Status': None
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/interfaces_sample b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/interfaces_sample
new file mode 100644 (file)
index 0000000..811a772
--- /dev/null
@@ -0,0 +1,21 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/EthernetInterfaces',
+       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/',
+       '@odata.type': '#EthernetInterfaceCollection.EthernetInterfaceCollection',
+       'Description': 'Collection of System Network Interfaces',
+       'Members': [{
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/1/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/2/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/3/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/4/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/5/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/6/'
+       }],
+       'Members@odata.count': 6,
+       'Name': 'System Network Interfaces'
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/memory b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/memory
new file mode 100644 (file)
index 0000000..fba0606
--- /dev/null
@@ -0,0 +1,65 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Memory',
+       '@odata.id': '/redfish/v1/Systems/1/Memory/',
+       '@odata.type': '#HpMemoryCollection.HpMemoryCollection',
+       'Description': 'Memory DIMM Collection',
+       'Members': [{
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm1/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm2/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm3/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm4/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm5/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm6/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm7/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm8/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm9/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm10/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm11/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm12/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm1/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm2/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm3/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm4/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm5/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm6/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm7/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm8/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm9/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm10/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm11/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm12/'
+       }],
+       'Members@odata.count': 24,
+       'Name': 'Memory DIMM Collection',
+       'Oem': {
+               'Hp': {
+                       '@odata.type': '#HpAdvancedMemoryProtection.1.0.0.HpAdvancedMemoryProtection',
+                       'AmpModeActive': 'AdvancedECC',
+                       'AmpModeStatus': 'AdvancedECC',
+                       'AmpModeSupported': ['AdvancedECC', 'OnlineSpareRank']
+               }
+       }
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/memory_socket b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/memory_socket
new file mode 100644 (file)
index 0000000..283c7d4
--- /dev/null
@@ -0,0 +1,21 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Memory/Members/$entity',
+       '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm1/',
+       '@odata.type': '#HpMemory.1.0.0.HpMemory',
+       'DIMMStatus': 'GoodInUse',
+       'DIMMTechnology': 'RDIMM',
+       'DIMMType': 'DDR3',
+       'DataWidth': 64,
+       'ErrorCorrection': 'SingleBitECC',
+       'HPMemoryType': 'HPSmartMemory',
+       'Id': 'proc1dimm1',
+       'Manufacturer': 'HP     ',
+       'MaximumFrequencyMHz': 1600,
+       'MinimumVoltageVoltsX10': 13,
+       'Name': 'proc1dimm1',
+       'PartNumber': '713756-081          ',
+       'Rank': 2,
+       'SizeMB': 16384,
+       'SocketLocator': 'PROC  1 DIMM  1 ',
+       'TotalWidth': 72
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/processor b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/processor
new file mode 100644 (file)
index 0000000..bc381fb
--- /dev/null
@@ -0,0 +1,117 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Processors/Members/$entity',
+       '@odata.id': '/redfish/v1/Systems/1/Processors/1/',
+       '@odata.type': '#Processor.1.0.0.Processor',
+       'Id': '1',
+       'InstructionSet': 'x86-64',
+       'Manufacturer': 'Intel',
+       'MaxSpeedMHz': 4800,
+       'Model': ' Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz      ',
+       'Name': 'Processors',
+       'Oem': {
+               'Hp': {
+                       '@odata.type': '#HpProcessorExt.1.0.0.HpProcessorExt',
+                       'AssetTag': '',
+                       'Cache': [{
+                               'Associativity': '8waySetAssociative',
+                               'CacheSpeedns': 0,
+                               'CurrentSRAMType': ['Burst'],
+                               'EccType': 'SingleBitECC',
+                               'InstalledSizeKB': 256,
+                               'Location': 'Internal',
+                               'MaximumSizeKB': 384,
+                               'Name': 'Processor 1 Internal L1 Cache',
+                               'Policy': 'WriteBack',
+                               'Socketed': False,
+                               'SupportedSRAMType': ['Burst'],
+                               'SystemCacheType': 'Data'
+                       }, {
+                               'Associativity': '8waySetAssociative',
+                               'CacheSpeedns': 0,
+                               'CurrentSRAMType': ['Burst'],
+                               'EccType': 'SingleBitECC',
+                               'InstalledSizeKB': 2048,
+                               'Location': 'Internal',
+                               'MaximumSizeKB': 3072,
+                               'Name': 'Processor 1 Internal L2 Cache',
+                               'Policy': 'WriteBack',
+                               'Socketed': False,
+                               'SupportedSRAMType': ['Burst'],
+                               'SystemCacheType': None
+                       }, {
+                               'Associativity': '20waySetAssociative',
+                               'CacheSpeedns': 0,
+                               'CurrentSRAMType': ['Burst'],
+                               'EccType': 'SingleBitECC',
+                               'InstalledSizeKB': 20480,
+                               'Location': 'Internal',
+                               'MaximumSizeKB': 30720,
+                               'Name': 'Processor 1 Internal L3 Cache',
+                               'Policy': 'WriteBack',
+                               'Socketed': False,
+                               'SupportedSRAMType': ['Burst'],
+                               'SystemCacheType': None
+                       }],
+                       'Characteristics': ['64Bit'],
+                       'ConfigStatus': {
+                               'Populated': True,
+                               'State': 'Enabled'
+                       },
+                       'CoresEnabled': 8,
+                       'ExternalClockMHz': 100,
+                       'MicrocodePatches': [{
+                               'CpuId': '0x000206D2',
+                               'Date': '2011-05-03T00:00:00Z',
+                               'PatchId': '0x8000020C'
+                       }, {
+                               'CpuId': '0x000206D3',
+                               'Date': '2011-04-20T00:00:00Z',
+                               'PatchId': '0x80000304'
+                       }, {
+                               'CpuId': '0x000206D5',
+                               'Date': '2011-10-13T00:00:00Z',
+                               'PatchId': '0x00000513'
+                       }, {
+                               'CpuId': '0x000206D6',
+                               'Date': '2018-01-30T00:00:00Z',
+                               'PatchId': '0x0000061C'
+                       }, {
+                               'CpuId': '0x000206D7',
+                               'Date': '2018-01-26T00:00:00Z',
+                               'PatchId': '0x00000713'
+                       }, {
+                               'CpuId': '0x000306E2',
+                               'Date': '2013-03-21T00:00:00Z',
+                               'PatchId': '0x0000020D'
+                       }, {
+                               'CpuId': '0x000306E3',
+                               'Date': '2013-03-21T00:00:00Z',
+                               'PatchId': '0x00000308'
+                       }, {
+                               'CpuId': '0x000306E4',
+                               'Date': '2018-01-25T00:00:00Z',
+                               'PatchId': '0x0000042C'
+                       }],
+                       'PartNumber': '',
+                       'RatedSpeedMHz': 2000,
+                       'SerialNumber': '',
+                       'VoltageVoltsX10': 14
+               }
+       },
+       'ProcessorArchitecture': 'x86',
+       'ProcessorId': {
+               'EffectiveFamily': '179',
+               'EffectiveModel': '14',
+               'IdentificationRegisters': '0x06e40003fbffbfeb',
+               'MicrocodeInfo': None,
+               'Step': '4',
+               'VendorId': 'Intel'
+       },
+       'ProcessorType': 'CPU',
+       'Socket': 'Proc 1',
+       'Status': {
+               'Health': 'OK'
+       },
+       'TotalCores': 8,
+       'TotalThreads': 16
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/processors b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/processors
new file mode 100644 (file)
index 0000000..c2fb740
--- /dev/null
@@ -0,0 +1,13 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Processors',
+       '@odata.id': '/redfish/v1/Systems/1/Processors/',
+       '@odata.type': '#ProcessorCollection.ProcessorCollection',
+       'Description': 'Processors view',
+       'Members': [{
+               '@odata.id': '/redfish/v1/Systems/1/Processors/1/'
+       }, {
+               '@odata.id': '/redfish/v1/Systems/1/Processors/2/'
+       }],
+       'Members@odata.count': 2,
+       'Name': 'Processors Collection'
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/storage_sample b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/storage_sample
new file mode 100644 (file)
index 0000000..6d351cf
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/EthernetInterfaces/Members/$entity',
+       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/1/',
+       '@odata.type': '#EthernetInterface.1.0.0.EthernetInterface',
+       'Id': '1',
+       'Name': 'System Ethernet Interface',
+       'Oem': {
+               'Hp': {
+                       '@odata.type': '#HpiLOEthernetNetworkInterface.1.0.0.HpiLOEthernetNetworkInterface',
+                       'DHCPv4': None,
+                       'DHCPv6': None,
+                       'IPv4': None,
+                       'IPv6': None,
+                       'SharedNetworkPortOptions': None
+               }
+       },
+       'SettingsResult': None,
+       'Status': None
+}
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/system b/src/cephadm/cephadmlib/node_proxy/redfish_json_samples/system
new file mode 100644 (file)
index 0000000..5bd2017
--- /dev/null
@@ -0,0 +1,144 @@
+{
+       '@odata.context': '/redfish/v1/$metadata#Systems/Members/$entity',
+       '@odata.id': '/redfish/v1/Systems/1/',
+       '@odata.type': '#ComputerSystem.1.0.1.ComputerSystem',
+       'Actions': {
+               '#ComputerSystem.Reset': {
+                       'ResetType@Redfish.AllowableValues': ['On', 'ForceOff', 'ForceRestart', 'Nmi', 'PushPowerButton'],
+                       'target': '/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/'
+               }
+       },
+       'AssetTag': '                                ',
+       'BiosVersion': 'P71 01/22/2018',
+       'Boot': {
+               'BootSourceOverrideEnabled': 'Disabled',
+               'BootSourceOverrideSupported': ['None', 'Floppy', 'Cd', 'Hdd', 'Usb', 'Utilities', 'BiosSetup', 'Pxe'],
+               'BootSourceOverrideTarget': 'None'
+       },
+       'Description': 'Computer System View',
+       'EthernetInterfaces': {
+               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/'
+       },
+       'HostName': 'hive1',
+       'Id': '1',
+       'IndicatorLED': 'Off',
+       'Links': {
+               'Chassis': [{
+                       '@odata.id': '/redfish/v1/Chassis/1/'
+               }],
+               'ManagedBy': [{
+                       '@odata.id': '/redfish/v1/Managers/1/'
+               }]
+       },
+       'LogServices': {
+               '@odata.id': '/redfish/v1/Systems/1/LogServices/'
+       },
+       'Manufacturer': 'HPE',
+       'MemorySummary': {
+               'Status': {
+                       'HealthRollup': 'OK'
+               },
+               'TotalSystemMemoryGiB': 384
+       },
+       'Model': 'ProLiant DL360p Gen8',
+       'Name': 'Computer System',
+       'Oem': {
+               'Hp': {
+                       '@odata.type': '#HpComputerSystemExt.1.2.2.HpComputerSystemExt',
+                       'Actions': {
+                               '#HpComputerSystemExt.PowerButton': {
+                                       'PushType@Redfish.AllowableValues': ['Press', 'PressAndHold'],
+                                       'target': '/redfish/v1/Systems/1/Actions/Oem/Hp/ComputerSystemExt.PowerButton/'
+                               },
+                               '#HpComputerSystemExt.SystemReset': {
+                                       'ResetType@Redfish.AllowableValues': ['ColdBoot', 'AuxCycle'],
+                                       'target': '/redfish/v1/Systems/1/Actions/Oem/Hp/ComputerSystemExt.SystemReset/'
+                               }
+                       },
+                       'Bios': {
+                               'Backup': {
+                                       'Date': '07/01/2015',
+                                       'Family': 'P71',
+                                       'VersionString': 'P71 07/01/2015'
+                               },
+                               'Bootblock': {
+                                       'Date': '03/05/2013',
+                                       'Family': 'P71',
+                                       'VersionString': 'P71 03/05/2013'
+                               },
+                               'Current': {
+                                       'Date': '01/22/2018',
+                                       'Family': 'P71',
+                                       'VersionString': 'P71 01/22/2018'
+                               },
+                               'UefiClass': 0
+                       },
+                       'DeviceDiscoveryComplete': {
+                               'AMSDeviceDiscovery': 'NoAMS',
+                               'DeviceDiscovery': 'vMainDeviceDiscoveryComplete',
+                               'SmartArrayDiscovery': 'Complete'
+                       },
+                       'IntelligentProvisioningIndex': 3,
+                       'IntelligentProvisioningLocation': 'System Board',
+                       'IntelligentProvisioningVersion': 'N/A',
+                       'Links': {
+                               'BIOS': {
+                                       '@odata.id': '/redfish/v1/Systems/1/Bios/'
+                               },
+                               'EthernetInterfaces': {
+                                       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/'
+                               },
+                               'FirmwareInventory': {
+                                       '@odata.id': '/redfish/v1/Systems/1/FirmwareInventory/'
+                               },
+                               'Memory': {
+                                       '@odata.id': '/redfish/v1/Systems/1/Memory/'
+                               },
+                               'NetworkAdapters': {
+                               '@odata.id': '/redfish/v1/Systems/1/NetworkAdapters/'
+                               },
+                               'PCIDevices': {
+                                       '@odata.id': '/redfish/v1/Systems/1/PCIDevices/'
+                               },
+                               'PCISlots': {
+                                       '@odata.id': '/redfish/v1/Systems/1/PCISlots/'
+                               },
+                               'SmartStorage': {
+                                       '@odata.id': '/redfish/v1/Systems/1/SmartStorage/'
+                               },
+                               'SoftwareInventory': {
+                                       '@odata.id': '/redfish/v1/Systems/1/SoftwareInventory/'
+                               }
+                       },
+                       'PostState': 'FinishedPost',
+                       'PowerAllocationLimit': 1500,
+                       'PowerAutoOn': 'Restore',
+                       'PowerOnDelay': 'Minimum',
+                       'PowerRegulatorMode': 'Dynamic',
+                       'PowerRegulatorModesSupported': ['OSControl', 'Dynamic', 'Max', 'Min'],
+                       'TrustedModules': [{
+                               'Status': 'NotPresent'
+                       }],
+                       'VirtualProfile': 'Inactive'
+               }
+       },
+       'PowerState': 'On',
+       'ProcessorSummary': {
+               'Count': 2,
+               'Model': ' Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz      ',
+               'Status': {
+                       'HealthRollup': 'OK'
+               }
+       },
+       'Processors': {
+               '@odata.id': '/redfish/v1/Systems/1/Processors/'
+       },
+       'SKU': '654081-B21      ',
+       'SerialNumber': 'CZJ4320228      ',
+       'Status': {
+               'Health': 'Warning',
+               'State': 'Enabled'
+       },
+       'SystemType': 'Physical',
+       'UUID': '30343536-3138-5A43-4A34-333230323238'
+}
\ No newline at end of file
diff --git a/src/cephadm/cephadmlib/node_proxy/redfish_system.py b/src/cephadm/cephadmlib/node_proxy/redfish_system.py
new file mode 100644 (file)
index 0000000..bb8286b
--- /dev/null
@@ -0,0 +1,161 @@
+from .basesystem import BaseSystem
+from .redfish_client import RedFishClient
+from threading import Thread, Lock
+from time import sleep
+from .util import Logger, retry, normalize_dict, to_snake_case
+from typing import Dict, Any, List
+
+log = Logger(__name__)
+
+
+class RedfishSystem(BaseSystem):
+    def __init__(self, **kw: Any) -> None:
+        super().__init__(**kw)
+        self.host: str = kw['host']
+        self.username: str = kw['username']
+        self.password: str = kw['password']
+        self.system_endpoint = kw.get('system_endpoint', '/Systems/1')
+        log.logger.info(f"redfish system initialization, host: {self.host}, user: {self.username}")
+        self.client = RedFishClient(self.host, self.username, self.password)
+
+        self._system: Dict[str, Dict[str, Any]] = {}
+        self.run: bool = False
+        self.thread: Thread
+        self.start_client()
+        self.data_ready: bool = False
+        self.previous_data: Dict = {}
+        self.lock: Lock = Lock()
+
+    @retry(retries=10, delay=2)
+    def _get_path(self, path: str) -> Dict:
+        result = self.client.get_path(path)
+        if result is None:
+            log.logger.error(f"The client reported an error when getting path: {path}")
+            raise RuntimeError(f"Could not get path: {path}")
+        return result
+
+    def get_members(self, path: str) -> List:
+        _path = self._system[path]['@odata.id']
+        data = self._get_path(_path)
+        return [self._get_path(member['@odata.id']) for member in data['Members']]
+
+    def build_data(self,
+                   fields: List,
+                   path: str) -> Dict[str, Dict[str, Dict]]:
+        result: Dict[str, Dict[str, Dict]] = dict()
+        for member_info in self.get_members(path):
+            member_id = member_info['Id']
+            result[member_id] = dict()
+            for field in fields:
+                try:
+                    result[member_id][to_snake_case(field)] = member_info[field]
+                except KeyError:
+                    log.logger.warning(f"Could not find field: {field} in member_info: {member_info}")
+
+        return normalize_dict(result)
+
+    def start_client(self) -> None:
+        if not self.client:
+            self.client = RedFishClient(self.host, self.username, self.password)
+        self.client.login()
+
+    def get_system(self) -> Dict[str, Dict[str, Dict]]:
+        result = {
+            'storage': self.get_storage(),
+            'processors': self.get_processors(),
+            'network': self.get_network(),
+            'memory': self.get_memory(),
+        }
+        return result
+
+    def get_status(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['status']
+
+    def get_metadata(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['metadata']
+
+    def get_memory(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['memory']
+
+    def get_power(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['power']
+
+    def get_processors(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['processors']
+
+    def get_network(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['network']
+
+    def get_storage(self) -> Dict[str, Dict[str, Dict]]:
+        return self._system['storage']
+
+    def _update_system(self) -> None:
+        redfish_system = self.client.get_path(self.system_endpoint)
+        self._system = {**redfish_system, **self._system}
+
+    def _update_metadata(self) -> None:
+        raise NotImplementedError()
+
+    def _update_memory(self) -> None:
+        raise NotImplementedError()
+
+    def _update_power(self) -> None:
+        raise NotImplementedError()
+
+    def _update_network(self) -> None:
+        raise NotImplementedError()
+
+    def _update_processors(self) -> None:
+        raise NotImplementedError()
+
+    def _update_storage(self) -> None:
+        raise NotImplementedError()
+
+    def start_update_loop(self) -> None:
+        self.run = True
+        self.thread = Thread(target=self.update)
+        self.thread.start()
+
+    def stop_update_loop(self) -> None:
+        self.run = False
+        self.thread.join()
+
+    def update(self) -> None:
+        #  this loop can have:
+        #  - caching logic
+        try:
+            while self.run:
+                log.logger.debug("waiting for a lock.")
+                self.lock.acquire()
+                log.logger.debug("lock acquired.")
+                try:
+                    self._update_system()
+                    # following calls in theory can be done in parallel
+                    self._update_metadata()
+                    self._update_memory()
+                    self._update_power()
+                    self._update_network()
+                    self._update_processors()
+                    self._update_storage()
+                    self.data_ready = True
+                    sleep(5)
+                finally:
+                    self.lock.release()
+                    log.logger.debug("lock released.")
+        # Catching 'Exception' is probably not a good idea (devel only)
+        except Exception as e:
+            log.logger.error(f"Error detected, logging out from redfish api.\n{e}")
+            self.client.logout()
+            raise
+
+    def flush(self) -> None:
+        log.logger.info("Acquiring lock to flush data.")
+        self.lock.acquire()
+        log.logger.info("Lock acquired, flushing data.")
+        self._system = {}
+        self.previous_data = {}
+        log.logger.info("Data flushed.")
+        self.data_ready = False
+        log.logger.info("Data marked as not ready.")
+        self.lock.release()
+        log.logger.info("Lock released.")
diff --git a/src/cephadm/cephadmlib/node_proxy/reporter.py b/src/cephadm/cephadmlib/node_proxy/reporter.py
new file mode 100644 (file)
index 0000000..43b5738
--- /dev/null
@@ -0,0 +1,52 @@
+from threading import Thread
+import requests
+import time
+from .util import Logger
+from typing import Any
+
+log = Logger(__name__)
+
+
+class Reporter:
+    def __init__(self, system: Any, observer_url: str) -> None:
+        self.system = system
+        self.observer_url = observer_url
+        self.finish = False
+
+    def stop(self) -> None:
+        self.finish = True
+        self.thread.join()
+
+    def run(self) -> None:
+        self.thread = Thread(target=self.loop)
+        self.thread.start()
+
+    def loop(self) -> None:
+        while not self.finish:
+            # Any logic to avoid sending the all the system
+            # information every loop can go here. In a real
+            # scenario probably we should just send the sub-parts
+            # that have changed to minimize the traffic in
+            # dense clusters
+            log.logger.debug("waiting for a lock.")
+            self.system.lock.acquire()
+            log.logger.debug("lock acquired.")
+            if self.system.data_ready:
+                log.logger.info('data ready to be sent to the mgr.')
+                if not self.system.get_system() == self.system.previous_data:
+                    log.logger.info('data has changed since last iteration.')
+                    d = self.system.get_system()
+                    try:
+                        # TODO: add a timeout parameter to the reporter in the config file
+                        requests.post(f"{self.observer_url}/", json=d, timeout=5)
+                    except requests.exceptions.RequestException as e:
+                        log.logger.error(f"The reporter couldn't send data to the mgr: {e}")
+                        # Need to add a new parameter 'max_retries' to the reporter if it can't
+                        # send the data for more than x times, maybe the daemon should stop altogether
+                    else:
+                        self.system.previous_data = self.system.get_system()
+                else:
+                    log.logger.info('no diff, not sending data to the mgr.')
+            self.system.lock.release()
+            log.logger.debug("lock released.")
+            time.sleep(5)
diff --git a/src/cephadm/cephadmlib/node_proxy/server.py b/src/cephadm/cephadmlib/node_proxy/server.py
new file mode 100644 (file)
index 0000000..714dafc
--- /dev/null
@@ -0,0 +1,244 @@
+import cherrypy
+from .redfish_dell import RedfishDell
+from .reporter import Reporter
+from .util import Config, Logger
+from typing import Dict
+from .basesystem import BaseSystem
+import sys
+import argparse
+
+# for devel purposes
+import os
+DEVEL_ENV_VARS = ['REDFISH_HOST',
+                  'REDFISH_USERNAME',
+                  'REDFISH_PASSWORD']
+
+DEFAULT_CONFIG = {
+    'reporter': {
+        'check_interval': 5,
+        'push_data_max_retries': 30,
+        'endpoint': 'http://127.0.0.1:8150',
+    },
+    'system': {
+        'refresh_interval': 5
+    },
+    'server': {
+        'port': 8080,
+    },
+    'logging': {
+        'level': 20,
+    }
+}
+
+#for env_var in DEVEL_ENV_VARS:
+#    if os.environ.get(env_var) is None:
+#        print(f"{env_var} environment variable must be set.")
+#        sys.exit(1)
+
+
+class Memory:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.backend = backend
+
+    @cherrypy.tools.json_out()
+    def GET(self) -> Dict[str, Dict[str, Dict]]:
+        return {'memory': self.backend.get_memory()}
+
+
+class Network:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.backend = backend
+
+    @cherrypy.tools.json_out()
+    def GET(self) -> Dict[str, Dict[str, Dict]]:
+        return {'network': self.backend.get_network()}
+
+
+class Processors:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.backend = backend
+
+    @cherrypy.tools.json_out()
+    def GET(self) -> Dict[str, Dict[str, Dict]]:
+        return {'processors': self.backend.get_processors()}
+
+
+class Storage:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.backend = backend
+
+    @cherrypy.tools.json_out()
+    def GET(self) -> Dict[str, Dict[str, Dict]]:
+        return {'storage': self.backend.get_storage()}
+
+
+class Status:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.backend = backend
+
+    @cherrypy.tools.json_out()
+    def GET(self) -> Dict[str, Dict[str, Dict]]:
+        return {'status': self.backend.get_status()}
+
+
+class System:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.memory = Memory(backend)
+        self.network = Network(backend)
+        self.processors = Processors(backend)
+        self.storage = Storage(backend)
+        self.status = Status(backend)
+        # actions = Actions()
+        # control = Control()
+
+
+class Shutdown:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem, reporter: Reporter) -> None:
+        self.backend = backend
+        self.reporter = reporter
+
+    def POST(self) -> str:
+        _stop(self.backend, self.reporter)
+        cherrypy.engine.exit()
+        return 'Server shutdown...'
+
+
+def _stop(backend: BaseSystem, reporter: Reporter) -> None:
+    backend.stop_update_loop()
+    backend.client.logout()
+    reporter.stop()
+
+
+class Start:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem, reporter: Reporter) -> None:
+        self.backend = backend
+        self.reporter = reporter
+
+    def POST(self) -> str:
+        self.backend.start_client()
+        self.backend.start_update_loop()
+        self.reporter.run()
+        return 'node-proxy daemon started'
+
+
+class Stop:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem, reporter: Reporter) -> None:
+        self.backend = backend
+        self.reporter = reporter
+
+    def POST(self) -> str:
+        _stop(self.backend, self.reporter)
+        return 'node-proxy daemon stopped'
+
+
+class ConfigReload:
+    exposed = True
+
+    def __init__(self, config: Config) -> None:
+        self.config = config
+
+    def POST(self) -> str:
+        self.config.reload()
+        return 'node-proxy config reloaded'
+
+
+class Flush:
+    exposed = True
+
+    def __init__(self, backend: BaseSystem) -> None:
+        self.backend = backend
+
+    def POST(self) -> str:
+        self.backend.flush()
+        return 'node-proxy data flushed'
+
+
+class Admin:
+    exposed = False
+
+    def __init__(self, backend: BaseSystem, config: Config, reporter: Reporter) -> None:
+        self.reload = ConfigReload(config)
+        self.flush = Flush(backend)
+        self.shutdown = Shutdown(backend, reporter)
+        self.start = Start(backend, reporter)
+        self.stop = Stop(backend, reporter)
+
+
+class API:
+    exposed = True
+
+    def __init__(self,
+                 backend: BaseSystem,
+                 reporter: Reporter,
+                 config: Config) -> None:
+
+        self.system = System(backend)
+        self.admin = Admin(backend, config, reporter)
+
+    def GET(self) -> str:
+        return 'use /system or /admin endpoints'
+
+
+def main() -> None:
+
+    # parser = argparse.ArgumentParser(
+    #     prog='node-proxy',
+    # )
+    # parser.add_argument(
+    #     '--config',
+    #     dest='config',
+    #     type=str,
+    #     required=False,
+    #     default='/etc/ceph/node-proxy.yml'
+    # )
+
+    # args = parser.parse_args()
+    config = Config('/etc/ceph/node-proxy.yml', default_config=DEFAULT_CONFIG)
+
+    log = Logger(__name__, level=config.__dict__['logging']['level'])
+    # must be passed as arguments
+    host = os.environ.get('REDFISH_HOST')
+    username = os.environ.get('REDFISH_USERNAME')
+    password = os.environ.get('REDFISH_PASSWORD')
+
+    # create the redfish system and the obsever
+    log.logger.info("Server initialization...")
+    system = RedfishDell(host=host,
+                         username=username,
+                         password=password,
+                         system_endpoint='/Systems/System.Embedded.1',
+                         config=config)
+    reporter_agent = Reporter(system, config.__dict__['reporter']['endpoint'])
+    cherrypy.config.update({
+        'node_proxy': config,
+        'server.socket_port': config.__dict__['server']['port']
+    })
+    c = {'/': {
+        'request.methods_with_bodies': ('POST', 'PUT', 'PATCH'),
+        'request.dispatch': cherrypy.dispatch.MethodDispatcher()
+    }}
+    system.start_update_loop()
+    reporter_agent.run()
+    cherrypy.quickstart(API(system, reporter_agent, config), config=c)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/src/cephadm/cephadmlib/node_proxy/util.py b/src/cephadm/cephadmlib/node_proxy/util.py
new file mode 100644 (file)
index 0000000..98c1a7d
--- /dev/null
@@ -0,0 +1,98 @@
+import logging
+import yaml
+import os
+import time
+import re
+from typing import Dict, List, Callable, Any
+
+
+class Logger:
+    _Logger: List['Logger'] = []
+
+    def __init__(self, name: str, level: int = logging.INFO):
+        self.name = name
+        self.level = level
+
+        Logger._Logger.append(self)
+        self.logger = self.get_logger()
+
+    def get_logger(self) -> logging.Logger:
+        logger = logging.getLogger(self.name)
+        logger.setLevel(self.level)
+        handler = logging.StreamHandler()
+        handler.setLevel(self.level)
+        fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+        handler.setFormatter(fmt)
+        logger.addHandler(handler)
+
+        return logger
+
+
+class Config:
+
+    def __init__(self,
+                 config_file: str = '/etc/ceph/node-proxy.yaml',
+                 default_config: Dict[str, Any] = {}) -> None:
+        self.config_file = config_file
+        self.default_config = default_config
+
+        self.load_config()
+
+    def load_config(self) -> None:
+        if os.path.exists(self.config_file):
+            with open(self.config_file, 'r') as f:
+                self.config = yaml.safe_load(f)
+        else:
+            self.config = self.default_config
+
+        for k, v in self.default_config.items():
+            if k not in self.config.keys():
+                self.config[k] = v
+
+        for k, v in self.config.items():
+            setattr(self, k, v)
+
+        # TODO: need to be improved
+        for _l in Logger._Logger:
+            _l.logger.setLevel(self.logging['level'])  # type: ignore
+            _l.logger.handlers[0].setLevel(self.logging['level'])  # type: ignore
+
+    def reload(self, config_file: str = '') -> None:
+        if config_file != '':
+            self.config_file = config_file
+        self.load_config()
+
+
+log = Logger(__name__)
+
+
+def to_snake_case(name: str) -> str:
+    name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
+
+
+def normalize_dict(test_dict: Dict) -> Dict:
+    res = dict()
+    for key in test_dict.keys():
+        if isinstance(test_dict[key], dict):
+            res[key.lower()] = normalize_dict(test_dict[key])
+        else:
+            res[key.lower()] = test_dict[key]
+    return res
+
+
+def retry(exceptions: Any = Exception, retries: int = 20, delay: int = 1) -> Callable:
+    def decorator(f: Callable) -> Callable:
+        def _retry(*args: str, **kwargs: Any) -> Callable:
+            _tries = retries
+            while _tries > 1:
+                try:
+                    log.logger.debug("{} {} attempt(s) left.".format(f, _tries - 1))
+                    return f(*args, **kwargs)
+                except exceptions:
+                    time.sleep(delay)
+                    _tries -= 1
+            log.logger.warn("{} has failed after {} tries".format(f, retries))
+            return f(*args, **kwargs)
+        return _retry
+    return decorator
diff --git a/src/cephadm/node_proxy/baseclient.py b/src/cephadm/node_proxy/baseclient.py
deleted file mode 100644 (file)
index 735dd11..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Dict
-
-
-class BaseClient:
-    def __init__(self,
-                 host: str,
-                 username: str,
-                 password: str) -> None:
-        self.host = host
-        self.username = username
-        self.password = password
-
-    def login(self) -> None:
-        raise NotImplementedError()
-
-    def logout(self) -> None:
-        raise NotImplementedError()
-
-    def get_path(self, path: str) -> Dict:
-        raise NotImplementedError()
diff --git a/src/cephadm/node_proxy/basesystem.py b/src/cephadm/node_proxy/basesystem.py
deleted file mode 100644 (file)
index a56ad7e..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-from util import Config
-from typing import Dict, Any
-from baseclient import BaseClient
-
-
-class BaseSystem:
-    def __init__(self, **kw: Any) -> None:
-        self._system: Dict = {}
-        self.config: Config = kw['config']
-        self.client: BaseClient
-
-    def get_system(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_status(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_metadata(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_processors(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_memory(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_power(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_network(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def get_storage(self) -> Dict[str, Dict[str, Dict]]:
-        raise NotImplementedError()
-
-    def start_update_loop(self) -> None:
-        raise NotImplementedError()
-
-    def stop_update_loop(self) -> None:
-        raise NotImplementedError()
-
-    def start_client(self) -> None:
-        raise NotImplementedError()
-
-    def flush(self) -> None:
-        raise NotImplementedError()
diff --git a/src/cephadm/node_proxy/data.py b/src/cephadm/node_proxy/data.py
deleted file mode 100644 (file)
index 7033901..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-
-system_1 = {
-
-    'metadata': {
-        'name': 'xx',
-        'manufacturer': 'Dell',
-        'model': 'HP PowerEdge',
-        'chassis': 'xxx',
-        'xxx': '',
-    },
-
-    'status': {
-        'State': 'Enabled',
-        'Health': 'OK'
-    },
-
-    'processors': [{
-        'description': '',
-        'cores': '',
-        'threads': '',
-        'type': '',
-        'model': '',
-        'temperature': '',
-        'status': {
-            'State': 'Enabled',
-            'Health': 'OK'
-        }
-    }],
-
-    'memory': {
-        'description': '',
-        'total': 'xx',
-        'status': {
-            'State': 'Enabled',
-            'Health': 'OK'
-        },
-    },
-
-    'network': {
-        'interfaces': [
-            {
-                'type': 'ethernet',
-                'description': 'my ethertnet interface',
-                'name': 'name of the interface',
-                'description': 'description of the interface',
-                'speed_mbps': 'xxx',
-                'status': {
-                    'State': 'Enabled',
-                    'Health': 'OK'
-                },
-            }
-        ]
-    },
-
-    'storage': {
-        'drives': [
-            {
-                'device': 'devc',
-                'description': 'Milk, Cheese, Bread, Fruit, Vegetables',
-                'serial_number': 'xxxxx',
-                'location': '1I:x:y',
-                'interface_type': 'SATA',
-                'model': 'Buy groceries',
-                'type': 'ssd|rotate|nvme',
-                'capacity_bytes': '',
-                'usage_bytes': '',
-                'status': {
-                    'State': 'Enabled',
-                    'Health': 'OK'
-                },
-            }
-        ]
-    },
-
-    'power':
-        [{
-            'type': 'xx',
-            'manufacturer': 'xxx',
-            'model': 'xx',
-            'properties': {},
-            'power_control': 'xx',
-            'status': {
-                'State': 'Enabled',
-                'Health': 'OK'
-            }
-        }],
-
-    'thermal': {
-        'fans': [
-            {
-                'id': 1,
-                'status': {
-                    'State': 'Enabled',
-                    'Health': 'OK'
-                }
-            }
-        ]
-    },
-}
diff --git a/src/cephadm/node_proxy/fake_cephadm/cephadm_mgr_module.py b/src/cephadm/node_proxy/fake_cephadm/cephadm_mgr_module.py
deleted file mode 100644 (file)
index 6d46de4..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/env python3
-"""
-License: MIT License
-Copyright (c) 2023 Miel Donkers
-
-Very simple HTTP server in python for logging requests
-Usage::
-    ./server.py [<port>]
-"""
-from http.server import BaseHTTPRequestHandler, HTTPServer
-import logging
-
-class S(BaseHTTPRequestHandler):
-    def _set_response(self):
-        self.send_response(200)
-        self.send_header('Content-type', 'text/html')
-        self.end_headers()
-
-    def do_GET(self):
-        logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
-        self._set_response()
-        self.wfile.write("GET request for {}".format(self.path).encode('utf-8'))
-
-    def do_POST(self):
-        content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
-        post_data = self.rfile.read(content_length) # <--- Gets the data itself
-        logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
-                str(self.path), str(self.headers), post_data.decode('utf-8'))
-
-        self._set_response()
-        self.wfile.write("POST request for {}".format(self.path).encode('utf-8'))
-
-def run(server_class=HTTPServer, handler_class=S, port=8000):
-    logging.basicConfig(level=logging.INFO)
-    server_address = ('', port)
-    httpd = server_class(server_address, handler_class)
-    logging.info(f'Starting httpd on port {port}...\n')
-    try:
-        httpd.serve_forever()
-    except KeyboardInterrupt:
-        pass
-    httpd.server_close()
-    logging.info('Stopping httpd...\n')
-
-if __name__ == '__main__':
-    from sys import argv
-
-    if len(argv) == 2:
-        run(port=int(argv[1]))
-    else:
-        run()
diff --git a/src/cephadm/node_proxy/main.py b/src/cephadm/node_proxy/main.py
deleted file mode 100644 (file)
index 45cd573..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-from redfish_system import RedfishSystem
-import time
-
-host = "https://x.x.x.x:8443"
-username = "myuser"
-password = "mypassword"
-
-system = RedfishSystem(host, username, password)
-system.start_update_loop()
-time.sleep(20)
-print(system.get_status())
-system.stop_update_loop()
diff --git a/src/cephadm/node_proxy/redfish-test.py b/src/cephadm/node_proxy/redfish-test.py
deleted file mode 100644 (file)
index 3aaab2d..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-from redfish.rest.v1 import ServerDownOrUnreachableError
-import redfish
-import sys
-
-
-login_host = "https://x.x.x.x:8443"
-login_account = "myuser"
-login_password = "mypassword"
-
-REDFISH_OBJ = redfish.redfish_client(base_url=login_host, username=login_account, password=login_password, default_prefix='/redfish/v1/')
-
-# Login
-try:
-    REDFISH_OBJ.login(auth="session")
-except ServerDownOrUnreachableError as excp:
-    sys.stderr.write("Error: server not reachable or does not support RedFish.\n")
-    sys.exit()
-
-# Get the system information /redfish/v1/Systems/1/SmartStorage/
-# /redfish/v1/Systems/1/Processors/
-# /redfish/v1/Systems/1/Memory/proc1dimm1/
-response = REDFISH_OBJ.get(sys.argv[1])
-# Print the system information
-print(response.dict)
-
-# Logout
-REDFISH_OBJ.logout()
diff --git a/src/cephadm/node_proxy/redfish_client.py b/src/cephadm/node_proxy/redfish_client.py
deleted file mode 100644 (file)
index 77353cd..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-from redfish.rest.v1 import ServerDownOrUnreachableError, \
-    SessionCreationError, \
-    InvalidCredentialsError
-import redfish
-import sys
-from util import Logger
-from baseclient import BaseClient
-from typing import Dict
-
-log = Logger(__name__)
-
-
-class RedFishClient(BaseClient):
-
-    PREFIX = '/redfish/v1'
-
-    def __init__(self,
-                 host: str,
-                 username: str,
-                 password: str) -> None:
-        log.logger.info("redfish client initialization...")
-        super().__init__(host, username, password)
-        self.redfish_obj: 'redfish.redfish_client' = None
-
-    def login(self) -> 'redfish.redfish_client':
-        self.redfish_obj = redfish.redfish_client(base_url=self.host,
-                                                  username=self.username,
-                                                  password=self.password,
-                                                  default_prefix=self.PREFIX)
-        try:
-            # TODO: add a retry? check for a timeout setting
-            self.redfish_obj.login(auth="session")
-            log.logger.info(f"Logging to redfish api at {self.host} with user: {self.username}")
-            return self.redfish_obj
-        except InvalidCredentialsError as e:
-            log.logger.error(f"Invalid credentials for {self.username} at {self.host}:\n{e}")
-        except (SessionCreationError, ServerDownOrUnreachableError) as e:
-            log.logger.error(f"Server not reachable or does not support RedFish:\n{e}")
-        sys.exit(1)
-
-    def get_path(self, path: str) -> Dict:
-        try:
-            if self.PREFIX not in path:
-                path = f"{self.PREFIX}{path}"
-            log.logger.debug(f"getting: {path}")
-            response = self.redfish_obj.get(path)
-            return response.dict
-        except Exception as e:
-            log.logger.error(f"Error getting path:\n{e}")
-            return {}
-
-    def logout(self) -> None:
-        log.logger.info('logging out...')
-        self.redfish_obj.logout()
diff --git a/src/cephadm/node_proxy/redfish_dell.py b/src/cephadm/node_proxy/redfish_dell.py
deleted file mode 100644 (file)
index 0f4467b..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-from redfish_system import RedfishSystem
-from util import Logger, normalize_dict, to_snake_case
-from typing import Dict, Any
-
-log = Logger(__name__)
-
-
-class RedfishDell(RedfishSystem):
-    def __init__(self, **kw: Any) -> None:
-        if kw.get('system_endpoint') is None:
-            kw['system_endpoint'] = '/Systems/System.Embedded.1'
-        super().__init__(**kw)
-
-    def _update_network(self) -> None:
-        fields = ['Description', 'Name', 'SpeedMbps', 'Status']
-        log.logger.info("Updating network")
-        self._system['network'] = self.build_data(fields, 'EthernetInterfaces')
-
-    def _update_processors(self) -> None:
-        fields = ['Description',
-                  'TotalCores',
-                  'TotalThreads',
-                  'ProcessorType',
-                  'Model',
-                  'Status',
-                  'Manufacturer']
-        log.logger.info("Updating processors")
-        self._system['processors'] = self.build_data(fields, 'Processors')
-
-    def _update_storage(self) -> None:
-        fields = ['Description',
-                  'CapacityBytes',
-                  'Model', 'Protocol',
-                  'SerialNumber', 'Status',
-                  'PhysicalLocation']
-        entities = self.get_members('Storage')
-        log.logger.info("Updating storage")
-        result: Dict[str, Dict[str, Dict]] = dict()
-        for entity in entities:
-            for drive in entity['Drives']:
-                drive_path = drive['@odata.id']
-                drive_info = self._get_path(drive_path)
-                drive_id = drive_info['Id']
-                result[drive_id] = dict()
-                for field in fields:
-                    result[drive_id][to_snake_case(field)] = drive_info[field]
-                    result[drive_id]['entity'] = entity['Id']
-        self._system['storage'] = normalize_dict(result)
-
-    def _update_metadata(self) -> None:
-        log.logger.info("Updating metadata")
-        pass
-
-    def _update_memory(self) -> None:
-        fields = ['Description',
-                  'MemoryDeviceType',
-                  'CapacityMiB',
-                  'Status']
-        log.logger.info("Updating memory")
-        self._system['memory'] = self.build_data(fields, 'Memory')
-
-    def _update_power(self) -> None:
-        log.logger.info("Updating power")
-        pass
diff --git a/src/cephadm/node_proxy/redfish_json_samples/interface_sample b/src/cephadm/node_proxy/redfish_json_samples/interface_sample
deleted file mode 100644 (file)
index 6d351cf..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/EthernetInterfaces/Members/$entity',
-       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/1/',
-       '@odata.type': '#EthernetInterface.1.0.0.EthernetInterface',
-       'Id': '1',
-       'Name': 'System Ethernet Interface',
-       'Oem': {
-               'Hp': {
-                       '@odata.type': '#HpiLOEthernetNetworkInterface.1.0.0.HpiLOEthernetNetworkInterface',
-                       'DHCPv4': None,
-                       'DHCPv6': None,
-                       'IPv4': None,
-                       'IPv6': None,
-                       'SharedNetworkPortOptions': None
-               }
-       },
-       'SettingsResult': None,
-       'Status': None
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/interfaces_sample b/src/cephadm/node_proxy/redfish_json_samples/interfaces_sample
deleted file mode 100644 (file)
index 811a772..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/EthernetInterfaces',
-       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/',
-       '@odata.type': '#EthernetInterfaceCollection.EthernetInterfaceCollection',
-       'Description': 'Collection of System Network Interfaces',
-       'Members': [{
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/1/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/2/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/3/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/4/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/5/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/6/'
-       }],
-       'Members@odata.count': 6,
-       'Name': 'System Network Interfaces'
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/memory b/src/cephadm/node_proxy/redfish_json_samples/memory
deleted file mode 100644 (file)
index fba0606..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Memory',
-       '@odata.id': '/redfish/v1/Systems/1/Memory/',
-       '@odata.type': '#HpMemoryCollection.HpMemoryCollection',
-       'Description': 'Memory DIMM Collection',
-       'Members': [{
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm1/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm2/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm3/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm4/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm5/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm6/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm7/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm8/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm9/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm10/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm11/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm12/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm1/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm2/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm3/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm4/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm5/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm6/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm7/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm8/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm9/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm10/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm11/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Memory/proc2dimm12/'
-       }],
-       'Members@odata.count': 24,
-       'Name': 'Memory DIMM Collection',
-       'Oem': {
-               'Hp': {
-                       '@odata.type': '#HpAdvancedMemoryProtection.1.0.0.HpAdvancedMemoryProtection',
-                       'AmpModeActive': 'AdvancedECC',
-                       'AmpModeStatus': 'AdvancedECC',
-                       'AmpModeSupported': ['AdvancedECC', 'OnlineSpareRank']
-               }
-       }
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/memory_socket b/src/cephadm/node_proxy/redfish_json_samples/memory_socket
deleted file mode 100644 (file)
index 283c7d4..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Memory/Members/$entity',
-       '@odata.id': '/redfish/v1/Systems/1/Memory/proc1dimm1/',
-       '@odata.type': '#HpMemory.1.0.0.HpMemory',
-       'DIMMStatus': 'GoodInUse',
-       'DIMMTechnology': 'RDIMM',
-       'DIMMType': 'DDR3',
-       'DataWidth': 64,
-       'ErrorCorrection': 'SingleBitECC',
-       'HPMemoryType': 'HPSmartMemory',
-       'Id': 'proc1dimm1',
-       'Manufacturer': 'HP     ',
-       'MaximumFrequencyMHz': 1600,
-       'MinimumVoltageVoltsX10': 13,
-       'Name': 'proc1dimm1',
-       'PartNumber': '713756-081          ',
-       'Rank': 2,
-       'SizeMB': 16384,
-       'SocketLocator': 'PROC  1 DIMM  1 ',
-       'TotalWidth': 72
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/processor b/src/cephadm/node_proxy/redfish_json_samples/processor
deleted file mode 100644 (file)
index bc381fb..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Processors/Members/$entity',
-       '@odata.id': '/redfish/v1/Systems/1/Processors/1/',
-       '@odata.type': '#Processor.1.0.0.Processor',
-       'Id': '1',
-       'InstructionSet': 'x86-64',
-       'Manufacturer': 'Intel',
-       'MaxSpeedMHz': 4800,
-       'Model': ' Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz      ',
-       'Name': 'Processors',
-       'Oem': {
-               'Hp': {
-                       '@odata.type': '#HpProcessorExt.1.0.0.HpProcessorExt',
-                       'AssetTag': '',
-                       'Cache': [{
-                               'Associativity': '8waySetAssociative',
-                               'CacheSpeedns': 0,
-                               'CurrentSRAMType': ['Burst'],
-                               'EccType': 'SingleBitECC',
-                               'InstalledSizeKB': 256,
-                               'Location': 'Internal',
-                               'MaximumSizeKB': 384,
-                               'Name': 'Processor 1 Internal L1 Cache',
-                               'Policy': 'WriteBack',
-                               'Socketed': False,
-                               'SupportedSRAMType': ['Burst'],
-                               'SystemCacheType': 'Data'
-                       }, {
-                               'Associativity': '8waySetAssociative',
-                               'CacheSpeedns': 0,
-                               'CurrentSRAMType': ['Burst'],
-                               'EccType': 'SingleBitECC',
-                               'InstalledSizeKB': 2048,
-                               'Location': 'Internal',
-                               'MaximumSizeKB': 3072,
-                               'Name': 'Processor 1 Internal L2 Cache',
-                               'Policy': 'WriteBack',
-                               'Socketed': False,
-                               'SupportedSRAMType': ['Burst'],
-                               'SystemCacheType': None
-                       }, {
-                               'Associativity': '20waySetAssociative',
-                               'CacheSpeedns': 0,
-                               'CurrentSRAMType': ['Burst'],
-                               'EccType': 'SingleBitECC',
-                               'InstalledSizeKB': 20480,
-                               'Location': 'Internal',
-                               'MaximumSizeKB': 30720,
-                               'Name': 'Processor 1 Internal L3 Cache',
-                               'Policy': 'WriteBack',
-                               'Socketed': False,
-                               'SupportedSRAMType': ['Burst'],
-                               'SystemCacheType': None
-                       }],
-                       'Characteristics': ['64Bit'],
-                       'ConfigStatus': {
-                               'Populated': True,
-                               'State': 'Enabled'
-                       },
-                       'CoresEnabled': 8,
-                       'ExternalClockMHz': 100,
-                       'MicrocodePatches': [{
-                               'CpuId': '0x000206D2',
-                               'Date': '2011-05-03T00:00:00Z',
-                               'PatchId': '0x8000020C'
-                       }, {
-                               'CpuId': '0x000206D3',
-                               'Date': '2011-04-20T00:00:00Z',
-                               'PatchId': '0x80000304'
-                       }, {
-                               'CpuId': '0x000206D5',
-                               'Date': '2011-10-13T00:00:00Z',
-                               'PatchId': '0x00000513'
-                       }, {
-                               'CpuId': '0x000206D6',
-                               'Date': '2018-01-30T00:00:00Z',
-                               'PatchId': '0x0000061C'
-                       }, {
-                               'CpuId': '0x000206D7',
-                               'Date': '2018-01-26T00:00:00Z',
-                               'PatchId': '0x00000713'
-                       }, {
-                               'CpuId': '0x000306E2',
-                               'Date': '2013-03-21T00:00:00Z',
-                               'PatchId': '0x0000020D'
-                       }, {
-                               'CpuId': '0x000306E3',
-                               'Date': '2013-03-21T00:00:00Z',
-                               'PatchId': '0x00000308'
-                       }, {
-                               'CpuId': '0x000306E4',
-                               'Date': '2018-01-25T00:00:00Z',
-                               'PatchId': '0x0000042C'
-                       }],
-                       'PartNumber': '',
-                       'RatedSpeedMHz': 2000,
-                       'SerialNumber': '',
-                       'VoltageVoltsX10': 14
-               }
-       },
-       'ProcessorArchitecture': 'x86',
-       'ProcessorId': {
-               'EffectiveFamily': '179',
-               'EffectiveModel': '14',
-               'IdentificationRegisters': '0x06e40003fbffbfeb',
-               'MicrocodeInfo': None,
-               'Step': '4',
-               'VendorId': 'Intel'
-       },
-       'ProcessorType': 'CPU',
-       'Socket': 'Proc 1',
-       'Status': {
-               'Health': 'OK'
-       },
-       'TotalCores': 8,
-       'TotalThreads': 16
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/processors b/src/cephadm/node_proxy/redfish_json_samples/processors
deleted file mode 100644 (file)
index c2fb740..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/Processors',
-       '@odata.id': '/redfish/v1/Systems/1/Processors/',
-       '@odata.type': '#ProcessorCollection.ProcessorCollection',
-       'Description': 'Processors view',
-       'Members': [{
-               '@odata.id': '/redfish/v1/Systems/1/Processors/1/'
-       }, {
-               '@odata.id': '/redfish/v1/Systems/1/Processors/2/'
-       }],
-       'Members@odata.count': 2,
-       'Name': 'Processors Collection'
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/storage_sample b/src/cephadm/node_proxy/redfish_json_samples/storage_sample
deleted file mode 100644 (file)
index 6d351cf..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/1/EthernetInterfaces/Members/$entity',
-       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/1/',
-       '@odata.type': '#EthernetInterface.1.0.0.EthernetInterface',
-       'Id': '1',
-       'Name': 'System Ethernet Interface',
-       'Oem': {
-               'Hp': {
-                       '@odata.type': '#HpiLOEthernetNetworkInterface.1.0.0.HpiLOEthernetNetworkInterface',
-                       'DHCPv4': None,
-                       'DHCPv6': None,
-                       'IPv4': None,
-                       'IPv6': None,
-                       'SharedNetworkPortOptions': None
-               }
-       },
-       'SettingsResult': None,
-       'Status': None
-}
diff --git a/src/cephadm/node_proxy/redfish_json_samples/system b/src/cephadm/node_proxy/redfish_json_samples/system
deleted file mode 100644 (file)
index 5bd2017..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-{
-       '@odata.context': '/redfish/v1/$metadata#Systems/Members/$entity',
-       '@odata.id': '/redfish/v1/Systems/1/',
-       '@odata.type': '#ComputerSystem.1.0.1.ComputerSystem',
-       'Actions': {
-               '#ComputerSystem.Reset': {
-                       'ResetType@Redfish.AllowableValues': ['On', 'ForceOff', 'ForceRestart', 'Nmi', 'PushPowerButton'],
-                       'target': '/redfish/v1/Systems/1/Actions/ComputerSystem.Reset/'
-               }
-       },
-       'AssetTag': '                                ',
-       'BiosVersion': 'P71 01/22/2018',
-       'Boot': {
-               'BootSourceOverrideEnabled': 'Disabled',
-               'BootSourceOverrideSupported': ['None', 'Floppy', 'Cd', 'Hdd', 'Usb', 'Utilities', 'BiosSetup', 'Pxe'],
-               'BootSourceOverrideTarget': 'None'
-       },
-       'Description': 'Computer System View',
-       'EthernetInterfaces': {
-               '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/'
-       },
-       'HostName': 'hive1',
-       'Id': '1',
-       'IndicatorLED': 'Off',
-       'Links': {
-               'Chassis': [{
-                       '@odata.id': '/redfish/v1/Chassis/1/'
-               }],
-               'ManagedBy': [{
-                       '@odata.id': '/redfish/v1/Managers/1/'
-               }]
-       },
-       'LogServices': {
-               '@odata.id': '/redfish/v1/Systems/1/LogServices/'
-       },
-       'Manufacturer': 'HPE',
-       'MemorySummary': {
-               'Status': {
-                       'HealthRollup': 'OK'
-               },
-               'TotalSystemMemoryGiB': 384
-       },
-       'Model': 'ProLiant DL360p Gen8',
-       'Name': 'Computer System',
-       'Oem': {
-               'Hp': {
-                       '@odata.type': '#HpComputerSystemExt.1.2.2.HpComputerSystemExt',
-                       'Actions': {
-                               '#HpComputerSystemExt.PowerButton': {
-                                       'PushType@Redfish.AllowableValues': ['Press', 'PressAndHold'],
-                                       'target': '/redfish/v1/Systems/1/Actions/Oem/Hp/ComputerSystemExt.PowerButton/'
-                               },
-                               '#HpComputerSystemExt.SystemReset': {
-                                       'ResetType@Redfish.AllowableValues': ['ColdBoot', 'AuxCycle'],
-                                       'target': '/redfish/v1/Systems/1/Actions/Oem/Hp/ComputerSystemExt.SystemReset/'
-                               }
-                       },
-                       'Bios': {
-                               'Backup': {
-                                       'Date': '07/01/2015',
-                                       'Family': 'P71',
-                                       'VersionString': 'P71 07/01/2015'
-                               },
-                               'Bootblock': {
-                                       'Date': '03/05/2013',
-                                       'Family': 'P71',
-                                       'VersionString': 'P71 03/05/2013'
-                               },
-                               'Current': {
-                                       'Date': '01/22/2018',
-                                       'Family': 'P71',
-                                       'VersionString': 'P71 01/22/2018'
-                               },
-                               'UefiClass': 0
-                       },
-                       'DeviceDiscoveryComplete': {
-                               'AMSDeviceDiscovery': 'NoAMS',
-                               'DeviceDiscovery': 'vMainDeviceDiscoveryComplete',
-                               'SmartArrayDiscovery': 'Complete'
-                       },
-                       'IntelligentProvisioningIndex': 3,
-                       'IntelligentProvisioningLocation': 'System Board',
-                       'IntelligentProvisioningVersion': 'N/A',
-                       'Links': {
-                               'BIOS': {
-                                       '@odata.id': '/redfish/v1/Systems/1/Bios/'
-                               },
-                               'EthernetInterfaces': {
-                                       '@odata.id': '/redfish/v1/Systems/1/EthernetInterfaces/'
-                               },
-                               'FirmwareInventory': {
-                                       '@odata.id': '/redfish/v1/Systems/1/FirmwareInventory/'
-                               },
-                               'Memory': {
-                                       '@odata.id': '/redfish/v1/Systems/1/Memory/'
-                               },
-                               'NetworkAdapters': {
-                               '@odata.id': '/redfish/v1/Systems/1/NetworkAdapters/'
-                               },
-                               'PCIDevices': {
-                                       '@odata.id': '/redfish/v1/Systems/1/PCIDevices/'
-                               },
-                               'PCISlots': {
-                                       '@odata.id': '/redfish/v1/Systems/1/PCISlots/'
-                               },
-                               'SmartStorage': {
-                                       '@odata.id': '/redfish/v1/Systems/1/SmartStorage/'
-                               },
-                               'SoftwareInventory': {
-                                       '@odata.id': '/redfish/v1/Systems/1/SoftwareInventory/'
-                               }
-                       },
-                       'PostState': 'FinishedPost',
-                       'PowerAllocationLimit': 1500,
-                       'PowerAutoOn': 'Restore',
-                       'PowerOnDelay': 'Minimum',
-                       'PowerRegulatorMode': 'Dynamic',
-                       'PowerRegulatorModesSupported': ['OSControl', 'Dynamic', 'Max', 'Min'],
-                       'TrustedModules': [{
-                               'Status': 'NotPresent'
-                       }],
-                       'VirtualProfile': 'Inactive'
-               }
-       },
-       'PowerState': 'On',
-       'ProcessorSummary': {
-               'Count': 2,
-               'Model': ' Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz      ',
-               'Status': {
-                       'HealthRollup': 'OK'
-               }
-       },
-       'Processors': {
-               '@odata.id': '/redfish/v1/Systems/1/Processors/'
-       },
-       'SKU': '654081-B21      ',
-       'SerialNumber': 'CZJ4320228      ',
-       'Status': {
-               'Health': 'Warning',
-               'State': 'Enabled'
-       },
-       'SystemType': 'Physical',
-       'UUID': '30343536-3138-5A43-4A34-333230323238'
-}
\ No newline at end of file
diff --git a/src/cephadm/node_proxy/redfish_system.py b/src/cephadm/node_proxy/redfish_system.py
deleted file mode 100644 (file)
index f23e41a..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-from basesystem import BaseSystem
-from redfish_client import RedFishClient
-from threading import Thread, Lock
-from time import sleep
-from util import Logger, retry, normalize_dict, to_snake_case
-from typing import Dict, Any, List
-
-log = Logger(__name__)
-
-
-class RedfishSystem(BaseSystem):
-    def __init__(self, **kw: Any) -> None:
-        super().__init__(**kw)
-        self.host: str = kw['host']
-        self.username: str = kw['username']
-        self.password: str = kw['password']
-        self.system_endpoint = kw.get('system_endpoint', '/Systems/1')
-        log.logger.info(f"redfish system initialization, host: {self.host}, user: {self.username}")
-        self.client = RedFishClient(self.host, self.username, self.password)
-
-        self._system: Dict[str, Dict[str, Any]] = {}
-        self.run: bool = False
-        self.thread: Thread
-        self.start_client()
-        self.data_ready: bool = False
-        self.previous_data: Dict = {}
-        self.lock: Lock = Lock()
-
-    @retry(retries=10, delay=2)
-    def _get_path(self, path: str) -> Dict:
-        result = self.client.get_path(path)
-        if result is None:
-            log.logger.error(f"The client reported an error when getting path: {path}")
-            raise RuntimeError(f"Could not get path: {path}")
-        return result
-
-    def get_members(self, path: str) -> List:
-        _path = self._system[path]['@odata.id']
-        data = self._get_path(_path)
-        return [self._get_path(member['@odata.id']) for member in data['Members']]
-
-    def build_data(self,
-                   fields: List,
-                   path: str) -> Dict[str, Dict[str, Dict]]:
-        result: Dict[str, Dict[str, Dict]] = dict()
-        for member_info in self.get_members(path):
-            member_id = member_info['Id']
-            result[member_id] = dict()
-            for field in fields:
-                try:
-                    result[member_id][to_snake_case(field)] = member_info[field]
-                except KeyError:
-                    log.logger.warning(f"Could not find field: {field} in member_info: {member_info}")
-
-        return normalize_dict(result)
-
-    def start_client(self) -> None:
-        if not self.client:
-            self.client = RedFishClient(self.host, self.username, self.password)
-        self.client.login()
-
-    def get_system(self) -> Dict[str, Dict[str, Dict]]:
-        result = {
-            'storage': self.get_storage(),
-            'processors': self.get_processors(),
-            'network': self.get_network(),
-            'memory': self.get_memory(),
-        }
-        return result
-
-    def get_status(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['status']
-
-    def get_metadata(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['metadata']
-
-    def get_memory(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['memory']
-
-    def get_power(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['power']
-
-    def get_processors(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['processors']
-
-    def get_network(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['network']
-
-    def get_storage(self) -> Dict[str, Dict[str, Dict]]:
-        return self._system['storage']
-
-    def _update_system(self) -> None:
-        redfish_system = self.client.get_path(self.system_endpoint)
-        self._system = {**redfish_system, **self._system}
-
-    def _update_metadata(self) -> None:
-        raise NotImplementedError()
-
-    def _update_memory(self) -> None:
-        raise NotImplementedError()
-
-    def _update_power(self) -> None:
-        raise NotImplementedError()
-
-    def _update_network(self) -> None:
-        raise NotImplementedError()
-
-    def _update_processors(self) -> None:
-        raise NotImplementedError()
-
-    def _update_storage(self) -> None:
-        raise NotImplementedError()
-
-    def start_update_loop(self) -> None:
-        self.run = True
-        self.thread = Thread(target=self.update)
-        self.thread.start()
-
-    def stop_update_loop(self) -> None:
-        self.run = False
-        self.thread.join()
-
-    def update(self) -> None:
-        #  this loop can have:
-        #  - caching logic
-        try:
-            while self.run:
-                log.logger.debug("waiting for a lock.")
-                self.lock.acquire()
-                log.logger.debug("lock acquired.")
-                try:
-                    self._update_system()
-                    # following calls in theory can be done in parallel
-                    self._update_metadata()
-                    self._update_memory()
-                    self._update_power()
-                    self._update_network()
-                    self._update_processors()
-                    self._update_storage()
-                    self.data_ready = True
-                    sleep(5)
-                finally:
-                    self.lock.release()
-                    log.logger.debug("lock released.")
-        # Catching 'Exception' is probably not a good idea (devel only)
-        except Exception as e:
-            log.logger.error(f"Error detected, logging out from redfish api.\n{e}")
-            self.client.logout()
-            raise
-
-    def flush(self) -> None:
-        log.logger.info("Acquiring lock to flush data.")
-        self.lock.acquire()
-        log.logger.info("Lock acquired, flushing data.")
-        self._system = {}
-        self.previous_data = {}
-        log.logger.info("Data flushed.")
-        self.data_ready = False
-        log.logger.info("Data marked as not ready.")
-        self.lock.release()
-        log.logger.info("Lock released.")
diff --git a/src/cephadm/node_proxy/reporter.py b/src/cephadm/node_proxy/reporter.py
deleted file mode 100644 (file)
index 07ac637..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-from threading import Thread
-import requests
-import time
-from util import Logger
-from typing import Any
-
-log = Logger(__name__)
-
-
-class Reporter:
-    def __init__(self, system: Any, observer_url: str) -> None:
-        self.system = system
-        self.observer_url = observer_url
-        self.finish = False
-
-    def stop(self) -> None:
-        self.finish = True
-        self.thread.join()
-
-    def run(self) -> None:
-        self.thread = Thread(target=self.loop)
-        self.thread.start()
-
-    def loop(self) -> None:
-        while not self.finish:
-            # Any logic to avoid sending the all the system
-            # information every loop can go here. In a real
-            # scenario probably we should just send the sub-parts
-            # that have changed to minimize the traffic in
-            # dense clusters
-            log.logger.debug("waiting for a lock.")
-            self.system.lock.acquire()
-            log.logger.debug("lock acquired.")
-            if self.system.data_ready:
-                log.logger.info('data ready to be sent to the mgr.')
-                if not self.system.get_system() == self.system.previous_data:
-                    log.logger.info('data has changed since last iteration.')
-                    d = self.system.get_system()
-                    try:
-                        # TODO: add a timeout parameter to the reporter in the config file
-                        requests.post(f"{self.observer_url}/", json=d, timeout=5)
-                    except requests.exceptions.RequestException as e:
-                        log.logger.error(f"The reporter couldn't send data to the mgr: {e}")
-                        # Need to add a new parameter 'max_retries' to the reporter if it can't
-                        # send the data for more than x times, maybe the daemon should stop altogether
-                    else:
-                        self.system.previous_data = self.system.get_system()
-                else:
-                    log.logger.info('no diff, not sending data to the mgr.')
-            self.system.lock.release()
-            log.logger.debug("lock released.")
-            time.sleep(5)
diff --git a/src/cephadm/node_proxy/requirements.txt b/src/cephadm/node_proxy/requirements.txt
deleted file mode 100644 (file)
index ab20bcc..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-pyyaml
-types-PyYAML
-types-requests
-mypy
-flake8
-redfish
-CherryPy
-requests
diff --git a/src/cephadm/node_proxy/server.py b/src/cephadm/node_proxy/server.py
deleted file mode 100644 (file)
index d05dbeb..0000000
+++ /dev/null
@@ -1,244 +0,0 @@
-import cherrypy
-from redfish_dell import RedfishDell
-from reporter import Reporter
-from util import Config, Logger
-from typing import Dict
-from basesystem import BaseSystem
-import sys
-import argparse
-
-# for devel purposes
-import os
-DEVEL_ENV_VARS = ['REDFISH_HOST',
-                  'REDFISH_USERNAME',
-                  'REDFISH_PASSWORD']
-
-DEFAULT_CONFIG = {
-    'reporter': {
-        'check_interval': 5,
-        'push_data_max_retries': 30,
-        'endpoint': 'http://127.0.0.1:8150',
-    },
-    'system': {
-        'refresh_interval': 5
-    },
-    'server': {
-        'port': 8080,
-    },
-    'logging': {
-        'level': 20,
-    }
-}
-
-for env_var in DEVEL_ENV_VARS:
-    if os.environ.get(env_var) is None:
-        print(f"{env_var} environment variable must be set.")
-        sys.exit(1)
-
-
-class Memory:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.backend = backend
-
-    @cherrypy.tools.json_out()
-    def GET(self) -> Dict[str, Dict[str, Dict]]:
-        return {'memory': self.backend.get_memory()}
-
-
-class Network:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.backend = backend
-
-    @cherrypy.tools.json_out()
-    def GET(self) -> Dict[str, Dict[str, Dict]]:
-        return {'network': self.backend.get_network()}
-
-
-class Processors:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.backend = backend
-
-    @cherrypy.tools.json_out()
-    def GET(self) -> Dict[str, Dict[str, Dict]]:
-        return {'processors': self.backend.get_processors()}
-
-
-class Storage:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.backend = backend
-
-    @cherrypy.tools.json_out()
-    def GET(self) -> Dict[str, Dict[str, Dict]]:
-        return {'storage': self.backend.get_storage()}
-
-
-class Status:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.backend = backend
-
-    @cherrypy.tools.json_out()
-    def GET(self) -> Dict[str, Dict[str, Dict]]:
-        return {'status': self.backend.get_status()}
-
-
-class System:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.memory = Memory(backend)
-        self.network = Network(backend)
-        self.processors = Processors(backend)
-        self.storage = Storage(backend)
-        self.status = Status(backend)
-        # actions = Actions()
-        # control = Control()
-
-
-class Shutdown:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem, reporter: Reporter) -> None:
-        self.backend = backend
-        self.reporter = reporter
-
-    def POST(self) -> str:
-        _stop(self.backend, self.reporter)
-        cherrypy.engine.exit()
-        return 'Server shutdown...'
-
-
-def _stop(backend: BaseSystem, reporter: Reporter) -> None:
-    backend.stop_update_loop()
-    backend.client.logout()
-    reporter.stop()
-
-
-class Start:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem, reporter: Reporter) -> None:
-        self.backend = backend
-        self.reporter = reporter
-
-    def POST(self) -> str:
-        self.backend.start_client()
-        self.backend.start_update_loop()
-        self.reporter.run()
-        return 'node-proxy daemon started'
-
-
-class Stop:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem, reporter: Reporter) -> None:
-        self.backend = backend
-        self.reporter = reporter
-
-    def POST(self) -> str:
-        _stop(self.backend, self.reporter)
-        return 'node-proxy daemon stopped'
-
-
-class ConfigReload:
-    exposed = True
-
-    def __init__(self, config: Config) -> None:
-        self.config = config
-
-    def POST(self) -> str:
-        self.config.reload()
-        return 'node-proxy config reloaded'
-
-
-class Flush:
-    exposed = True
-
-    def __init__(self, backend: BaseSystem) -> None:
-        self.backend = backend
-
-    def POST(self) -> str:
-        self.backend.flush()
-        return 'node-proxy data flushed'
-
-
-class Admin:
-    exposed = False
-
-    def __init__(self, backend: BaseSystem, config: Config, reporter: Reporter) -> None:
-        self.reload = ConfigReload(config)
-        self.flush = Flush(backend)
-        self.shutdown = Shutdown(backend, reporter)
-        self.start = Start(backend, reporter)
-        self.stop = Stop(backend, reporter)
-
-
-class API:
-    exposed = True
-
-    def __init__(self,
-                 backend: BaseSystem,
-                 reporter: Reporter,
-                 config: Config) -> None:
-
-        self.system = System(backend)
-        self.admin = Admin(backend, config, reporter)
-
-    def GET(self) -> str:
-        return 'use /system or /admin endpoints'
-
-
-def main() -> None:
-
-    parser = argparse.ArgumentParser(
-        prog='node-proxy',
-    )
-    parser.add_argument(
-        '--config',
-        dest='config',
-        type=str,
-        required=False,
-        default='/etc/ceph/node-proxy.yml'
-    )
-
-    args = parser.parse_args()
-    config = Config(args.config, default_config=DEFAULT_CONFIG)
-
-    log = Logger(__name__, level=config.__dict__['logging']['level'])
-    # must be passed as arguments
-    host = os.environ.get('REDFISH_HOST')
-    username = os.environ.get('REDFISH_USERNAME')
-    password = os.environ.get('REDFISH_PASSWORD')
-
-    # create the redfish system and the obsever
-    log.logger.info("Server initialization...")
-    system = RedfishDell(host=host,
-                         username=username,
-                         password=password,
-                         system_endpoint='/Systems/System.Embedded.1',
-                         config=config)
-    reporter_agent = Reporter(system, config.__dict__['reporter']['endpoint'])
-    cherrypy.config.update({
-        'node_proxy': config,
-        'server.socket_port': config.__dict__['server']['port']
-    })
-    c = {'/': {
-        'request.methods_with_bodies': ('POST', 'PUT', 'PATCH'),
-        'request.dispatch': cherrypy.dispatch.MethodDispatcher()
-    }}
-    system.start_update_loop()
-    reporter_agent.run()
-    cherrypy.quickstart(API(system, reporter_agent, config), config=c)
-
-
-if __name__ == '__main__':
-    main()
diff --git a/src/cephadm/node_proxy/util.py b/src/cephadm/node_proxy/util.py
deleted file mode 100644 (file)
index 98c1a7d..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-import logging
-import yaml
-import os
-import time
-import re
-from typing import Dict, List, Callable, Any
-
-
-class Logger:
-    _Logger: List['Logger'] = []
-
-    def __init__(self, name: str, level: int = logging.INFO):
-        self.name = name
-        self.level = level
-
-        Logger._Logger.append(self)
-        self.logger = self.get_logger()
-
-    def get_logger(self) -> logging.Logger:
-        logger = logging.getLogger(self.name)
-        logger.setLevel(self.level)
-        handler = logging.StreamHandler()
-        handler.setLevel(self.level)
-        fmt = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
-        handler.setFormatter(fmt)
-        logger.addHandler(handler)
-
-        return logger
-
-
-class Config:
-
-    def __init__(self,
-                 config_file: str = '/etc/ceph/node-proxy.yaml',
-                 default_config: Dict[str, Any] = {}) -> None:
-        self.config_file = config_file
-        self.default_config = default_config
-
-        self.load_config()
-
-    def load_config(self) -> None:
-        if os.path.exists(self.config_file):
-            with open(self.config_file, 'r') as f:
-                self.config = yaml.safe_load(f)
-        else:
-            self.config = self.default_config
-
-        for k, v in self.default_config.items():
-            if k not in self.config.keys():
-                self.config[k] = v
-
-        for k, v in self.config.items():
-            setattr(self, k, v)
-
-        # TODO: need to be improved
-        for _l in Logger._Logger:
-            _l.logger.setLevel(self.logging['level'])  # type: ignore
-            _l.logger.handlers[0].setLevel(self.logging['level'])  # type: ignore
-
-    def reload(self, config_file: str = '') -> None:
-        if config_file != '':
-            self.config_file = config_file
-        self.load_config()
-
-
-log = Logger(__name__)
-
-
-def to_snake_case(name: str) -> str:
-    name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
-    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
-
-
-def normalize_dict(test_dict: Dict) -> Dict:
-    res = dict()
-    for key in test_dict.keys():
-        if isinstance(test_dict[key], dict):
-            res[key.lower()] = normalize_dict(test_dict[key])
-        else:
-            res[key.lower()] = test_dict[key]
-    return res
-
-
-def retry(exceptions: Any = Exception, retries: int = 20, delay: int = 1) -> Callable:
-    def decorator(f: Callable) -> Callable:
-        def _retry(*args: str, **kwargs: Any) -> Callable:
-            _tries = retries
-            while _tries > 1:
-                try:
-                    log.logger.debug("{} {} attempt(s) left.".format(f, _tries - 1))
-                    return f(*args, **kwargs)
-                except exceptions:
-                    time.sleep(delay)
-                    _tries -= 1
-            log.logger.warn("{} has failed after {} tries".format(f, retries))
-            return f(*args, **kwargs)
-        return _retry
-    return decorator