osism 0.20250628.0__tar.gz → 0.20250701.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- osism-0.20250701.0/ChangeLog +7 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/Containerfile +6 -1
- {osism-0.20250628.0 → osism-0.20250701.0}/Dockerfile +6 -1
- {osism-0.20250628.0/osism.egg-info → osism-0.20250701.0}/PKG-INFO +2 -1
- {osism-0.20250628.0 → osism-0.20250701.0}/Pipfile +1 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/Pipfile.lock +48 -7
- osism-0.20250701.0/files/redfishMockupCreate.py +499 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/baremetal.py +37 -6
- osism-0.20250701.0/osism/commands/redfish.py +219 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/settings.py +3 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/__init__.py +7 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/config.py +52 -35
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/ironic.py +95 -99
- osism-0.20250701.0/osism/tasks/conductor/redfish.py +300 -0
- osism-0.20250701.0/osism/tasks/conductor/utils.py +227 -0
- {osism-0.20250628.0 → osism-0.20250701.0/osism.egg-info}/PKG-INFO +2 -1
- {osism-0.20250628.0 → osism-0.20250701.0}/osism.egg-info/SOURCES.txt +3 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism.egg-info/entry_points.txt +1 -0
- osism-0.20250701.0/osism.egg-info/pbr.json +1 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism.egg-info/requires.txt +1 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/requirements.txt +1 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/setup.cfg +1 -0
- osism-0.20250628.0/ChangeLog +0 -7
- osism-0.20250628.0/osism/tasks/conductor/utils.py +0 -79
- osism-0.20250628.0/osism.egg-info/pbr.json +0 -1
- {osism-0.20250628.0 → osism-0.20250701.0}/.flake8 +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/.github/renovate.json +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/.github/workflows/publish.yml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/.hadolint.yaml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/.zuul.yaml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/AUTHORS +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/LICENSE +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/README.md +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/change.sh +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/cleanup-ansible-collections.sh +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/clustershell/clush.conf +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/clustershell/groups.conf +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/data/SCS-Spec.MandatoryFlavors.verbose.yaml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/netbox-manager/settings.toml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/run-ansible-console.sh +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/config_db.json +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/port_config/Accton-AS4625-54T.ini +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/port_config/Accton-AS5835-54T.ini +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/port_config/Accton-AS5835-54X.ini +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/port_config/Accton-AS7326-56X.ini +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/port_config/Accton-AS7726-32X.ini +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/files/sonic/port_config/Accton-AS9716-32D.ini +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/__main__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/api.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/apply.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/compose.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/compute.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/configuration.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/console.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/container.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/get.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/log.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/manage.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/netbox.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/noset.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/reconciler.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/server.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/service.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/set.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/status.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/sync.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/task.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/validate.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/vault.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/volume.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/wait.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/commands/worker.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/data/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/data/enums.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/data/playbooks.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/main.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/services/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/services/listener.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/ansible.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/ceph.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/netbox.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/bgp.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/cache.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/config_generator.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/connections.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/constants.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/device.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/exporter.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/interface.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor/sonic/sync.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/conductor.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/kolla.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/kubernetes.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/netbox.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/openstack.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/tasks/reconciler.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism/utils/__init__.py +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism.egg-info/dependency_links.txt +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism.egg-info/not-zip-safe +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/osism.egg-info/top_level.txt +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/playbooks/build.yml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/playbooks/pre.yml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/playbooks/test-setup.yml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/requirements.ansible.txt +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/requirements.netbox-manager.txt +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/requirements.openstack-flavor-manager.txt +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/requirements.openstack-image-manager.txt +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/requirements.yml +0 -0
- {osism-0.20250628.0 → osism-0.20250701.0}/setup.py +0 -0
@@ -7,7 +7,7 @@ FROM ${IMAGE}:${PYTHON_VERSION}-alpine${ALPINE_VERSION}
|
|
7
7
|
ENV PYTHONWARNINGS="ignore::UserWarning"
|
8
8
|
|
9
9
|
COPY . /src
|
10
|
-
COPY --from=ghcr.io/astral-sh/uv:0.7.
|
10
|
+
COPY --from=ghcr.io/astral-sh/uv:0.7.17 /uv /usr/local/bin/uv
|
11
11
|
|
12
12
|
COPY files/data /data
|
13
13
|
COPY files/change.sh /change.sh
|
@@ -23,6 +23,8 @@ COPY files/sonic/config_db.json /etc/sonic/config_db.json
|
|
23
23
|
|
24
24
|
COPY files/netbox-manager/settings.toml /usr/local/config/settings.toml
|
25
25
|
|
26
|
+
COPY files/redfishMockupCreate.py /
|
27
|
+
|
26
28
|
RUN apk add --no-cache bash
|
27
29
|
|
28
30
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
@@ -53,6 +55,9 @@ uv pip install --no-cache --system -r /src/requirements.openstack-image-manager.
|
|
53
55
|
uv pip install --no-cache --system -r /src/requirements.openstack-flavor-manager.txt
|
54
56
|
uv pip install --no-cache --system -r /src/requirements.netbox-manager.txt
|
55
57
|
|
58
|
+
# required by redfishMockupCreate.py
|
59
|
+
uv pip install --no-cache --system "redfish==3.3.1"
|
60
|
+
|
56
61
|
# install python-osism
|
57
62
|
uv pip install --no-cache --system /src
|
58
63
|
|
@@ -7,7 +7,7 @@ FROM ${IMAGE}:${PYTHON_VERSION}-alpine${ALPINE_VERSION}
|
|
7
7
|
ENV PYTHONWARNINGS="ignore::UserWarning"
|
8
8
|
|
9
9
|
COPY . /src
|
10
|
-
COPY --from=ghcr.io/astral-sh/uv:0.7.
|
10
|
+
COPY --from=ghcr.io/astral-sh/uv:0.7.17 /uv /usr/local/bin/uv
|
11
11
|
|
12
12
|
COPY files/data /data
|
13
13
|
COPY files/change.sh /change.sh
|
@@ -23,6 +23,8 @@ COPY files/sonic/config_db.json /etc/sonic/config_db.json
|
|
23
23
|
|
24
24
|
COPY files/netbox-manager/settings.toml /usr/local/config/settings.toml
|
25
25
|
|
26
|
+
COPY files/redfishMockupCreate.py /
|
27
|
+
|
26
28
|
RUN apk add --no-cache bash
|
27
29
|
|
28
30
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
@@ -53,6 +55,9 @@ uv pip install --no-cache --system -r /src/requirements.openstack-image-manager.
|
|
53
55
|
uv pip install --no-cache --system -r /src/requirements.openstack-flavor-manager.txt
|
54
56
|
uv pip install --no-cache --system -r /src/requirements.netbox-manager.txt
|
55
57
|
|
58
|
+
# required by redfishMockupCreate.py
|
59
|
+
uv pip install --no-cache --system "redfish==3.3.1"
|
60
|
+
|
56
61
|
# install python-osism
|
57
62
|
uv pip install --no-cache --system /src
|
58
63
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: osism
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.20250701.0
|
4
4
|
Summary: OSISM manager interface
|
5
5
|
Home-page: https://github.com/osism/python-osism
|
6
6
|
Author: OSISM GmbH
|
@@ -53,6 +53,7 @@ Requires-Dist: sushy==5.6.0
|
|
53
53
|
Requires-Dist: tabulate==0.9.0
|
54
54
|
Requires-Dist: transitions==0.9.2
|
55
55
|
Requires-Dist: uvicorn[standard]==0.35.0
|
56
|
+
Requires-Dist: validators==0.35.0
|
56
57
|
Requires-Dist: watchdog==6.0.0
|
57
58
|
Provides-Extra: ansible
|
58
59
|
Requires-Dist: ansible-runner==2.4.1; extra == "ansible"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"_meta": {
|
3
3
|
"hash": {
|
4
|
-
"sha256": "
|
4
|
+
"sha256": "072d329d514cdaa8d46395aeee9fc0a440813e50314abfabce1c8c382f05ecf7"
|
5
5
|
},
|
6
6
|
"pipfile-spec": 6,
|
7
7
|
"requires": {},
|
@@ -337,10 +337,10 @@
|
|
337
337
|
},
|
338
338
|
"click-plugins": {
|
339
339
|
"hashes": [
|
340
|
-
"sha256:
|
341
|
-
"sha256:
|
340
|
+
"sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6",
|
341
|
+
"sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261"
|
342
342
|
],
|
343
|
-
"version": "==1.1.1"
|
343
|
+
"version": "==1.1.1.2"
|
344
344
|
},
|
345
345
|
"click-repl": {
|
346
346
|
"hashes": [
|
@@ -368,11 +368,11 @@
|
|
368
368
|
},
|
369
369
|
"cmd2": {
|
370
370
|
"hashes": [
|
371
|
-
"sha256:
|
372
|
-
"sha256:
|
371
|
+
"sha256:81d8135b46210e1d03a5a810baf859069a62214788ceeec3588f44eed86fbeeb",
|
372
|
+
"sha256:c85faf603e8cfeb4302206f49c0530a83d63386b0d90ff6a957f2c816eb767d7"
|
373
373
|
],
|
374
374
|
"markers": "python_version >= '3.9'",
|
375
|
-
"version": "==2.
|
375
|
+
"version": "==2.7.0"
|
376
376
|
},
|
377
377
|
"cryptography": {
|
378
378
|
"hashes": [
|
@@ -780,6 +780,14 @@
|
|
780
780
|
"markers": "python_version >= '3.5' and python_version < '4.0'",
|
781
781
|
"version": "==0.7.3"
|
782
782
|
},
|
783
|
+
"markdown-it-py": {
|
784
|
+
"hashes": [
|
785
|
+
"sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1",
|
786
|
+
"sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"
|
787
|
+
],
|
788
|
+
"markers": "python_version >= '3.8'",
|
789
|
+
"version": "==3.0.0"
|
790
|
+
},
|
783
791
|
"markupsafe": {
|
784
792
|
"hashes": [
|
785
793
|
"sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4",
|
@@ -847,6 +855,14 @@
|
|
847
855
|
"markers": "python_version >= '3.9'",
|
848
856
|
"version": "==3.0.2"
|
849
857
|
},
|
858
|
+
"mdurl": {
|
859
|
+
"hashes": [
|
860
|
+
"sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
|
861
|
+
"sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"
|
862
|
+
],
|
863
|
+
"markers": "python_version >= '3.7'",
|
864
|
+
"version": "==0.1.2"
|
865
|
+
},
|
850
866
|
"mmh3": {
|
851
867
|
"hashes": [
|
852
868
|
"sha256:00f810647c22c179b6821079f7aa306d51953ac893587ee09cf1afb35adf87cb",
|
@@ -1393,6 +1409,22 @@
|
|
1393
1409
|
],
|
1394
1410
|
"version": "==1.4.0"
|
1395
1411
|
},
|
1412
|
+
"rich": {
|
1413
|
+
"hashes": [
|
1414
|
+
"sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0",
|
1415
|
+
"sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"
|
1416
|
+
],
|
1417
|
+
"markers": "python_full_version >= '3.8.0'",
|
1418
|
+
"version": "==14.0.0"
|
1419
|
+
},
|
1420
|
+
"rich-argparse": {
|
1421
|
+
"hashes": [
|
1422
|
+
"sha256:a8650b42e4a4ff72127837632fba6b7da40784842f08d7395eb67a9cbd7b4bf9",
|
1423
|
+
"sha256:d7a493cde94043e41ea68fb43a74405fa178de981bf7b800f7a3bd02ac5c27be"
|
1424
|
+
],
|
1425
|
+
"markers": "python_version >= '3.8'",
|
1426
|
+
"version": "==1.7.1"
|
1427
|
+
},
|
1396
1428
|
"rsa": {
|
1397
1429
|
"hashes": [
|
1398
1430
|
"sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762",
|
@@ -1633,6 +1665,15 @@
|
|
1633
1665
|
"markers": "python_version >= '3.9'",
|
1634
1666
|
"version": "==2.5.0"
|
1635
1667
|
},
|
1668
|
+
"validators": {
|
1669
|
+
"hashes": [
|
1670
|
+
"sha256:992d6c48a4e77c81f1b4daba10d16c3a9bb0dbb79b3a19ea847ff0928e70497a",
|
1671
|
+
"sha256:e8c947097eae7892cb3d26868d637f79f47b4a0554bc6b80065dfe5aac3705dd"
|
1672
|
+
],
|
1673
|
+
"index": "pypi",
|
1674
|
+
"markers": "python_version >= '3.9'",
|
1675
|
+
"version": "==0.35.0"
|
1676
|
+
},
|
1636
1677
|
"vine": {
|
1637
1678
|
"hashes": [
|
1638
1679
|
"sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc",
|
@@ -0,0 +1,499 @@
|
|
1
|
+
# Copyright Notice:
|
2
|
+
# Copyright 2016-2020 DMTF. All rights reserved.
|
3
|
+
# License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/Redfish-Mockup-Creator/blob/main/LICENSE.md
|
4
|
+
# source: https://github.com/DMTF/Redfish-Mockup-Creator
|
5
|
+
|
6
|
+
"""
|
7
|
+
Redfish Mockup Creator
|
8
|
+
|
9
|
+
File : redfishMockupCreate.py
|
10
|
+
|
11
|
+
Brief : This tool walks a service and creates a mockup from all resources
|
12
|
+
"""
|
13
|
+
|
14
|
+
import argparse
|
15
|
+
import datetime
|
16
|
+
import json
|
17
|
+
import os
|
18
|
+
import redfish
|
19
|
+
import sys
|
20
|
+
import time
|
21
|
+
import xml.etree.ElementTree as ET
|
22
|
+
import logging
|
23
|
+
import copy
|
24
|
+
import gc
|
25
|
+
from redfish import redfish_logger
|
26
|
+
|
27
|
+
# Version info
|
28
|
+
tool_version = "1.2.0"
|
29
|
+
|
30
|
+
# For Windows, there are restricted characters in folder names that could be used in URIs
|
31
|
+
disallowed_folder_characters_win = [":", "*", "?", '"', "<", ">", "|"]
|
32
|
+
folder_name_fix = False
|
33
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
34
|
+
folder_name_fix = True
|
35
|
+
|
36
|
+
|
37
|
+
def main():
|
38
|
+
"""
|
39
|
+
Main entry point for the script
|
40
|
+
"""
|
41
|
+
|
42
|
+
# Get the input arguments
|
43
|
+
argget = argparse.ArgumentParser(
|
44
|
+
description="A tool to walk a Redfish service and create a mockup from all resources"
|
45
|
+
)
|
46
|
+
argget.add_argument(
|
47
|
+
"--user", "-u", type=str, required=True, help="The user name for authentication"
|
48
|
+
)
|
49
|
+
argget.add_argument(
|
50
|
+
"--password",
|
51
|
+
"-p",
|
52
|
+
type=str,
|
53
|
+
required=True,
|
54
|
+
help="The password for authentication",
|
55
|
+
)
|
56
|
+
argget.add_argument(
|
57
|
+
"--rhost",
|
58
|
+
"-r",
|
59
|
+
type=str,
|
60
|
+
required=True,
|
61
|
+
help="The IP address (and port) of the Redfish service",
|
62
|
+
)
|
63
|
+
argget.add_argument(
|
64
|
+
"--Dir",
|
65
|
+
"-D",
|
66
|
+
type=str,
|
67
|
+
help="Output directory for the mockup; defaults to 'rfMockUpDfltDir'",
|
68
|
+
default="rfMockUpDfltDir",
|
69
|
+
)
|
70
|
+
argget.add_argument(
|
71
|
+
"--Secure", "-S", action="store_true", help="Use HTTPS for all operations"
|
72
|
+
)
|
73
|
+
argget.add_argument(
|
74
|
+
"--Auth",
|
75
|
+
"-A",
|
76
|
+
type=str,
|
77
|
+
help="Authentication mode",
|
78
|
+
choices=["None", "Basic", "Session"],
|
79
|
+
default="Session",
|
80
|
+
)
|
81
|
+
argget.add_argument(
|
82
|
+
"--Headers",
|
83
|
+
"-H",
|
84
|
+
action="store_true",
|
85
|
+
help="Captures the response headers in the mockup",
|
86
|
+
)
|
87
|
+
argget.add_argument(
|
88
|
+
"--Time",
|
89
|
+
"-T",
|
90
|
+
action="store_true",
|
91
|
+
help="Capture the time of each GET in the mockup",
|
92
|
+
)
|
93
|
+
argget.add_argument(
|
94
|
+
"--Copyright",
|
95
|
+
"-C",
|
96
|
+
type=str,
|
97
|
+
help="Copyright string to add to each resource",
|
98
|
+
default=None,
|
99
|
+
)
|
100
|
+
argget.add_argument(
|
101
|
+
"--description",
|
102
|
+
"-d",
|
103
|
+
type=str,
|
104
|
+
help="Mockup description to add to the output readme file",
|
105
|
+
default="",
|
106
|
+
)
|
107
|
+
argget.add_argument(
|
108
|
+
"--quiet",
|
109
|
+
"-q",
|
110
|
+
action="store_true",
|
111
|
+
help="Quiet mode; progress messages suppressed",
|
112
|
+
)
|
113
|
+
argget.add_argument(
|
114
|
+
"--trace",
|
115
|
+
"-trace",
|
116
|
+
action="store_true",
|
117
|
+
help="Enable tracing; creates the file rf-mockup-create.log in the output directory to capture Redfish traces with the service",
|
118
|
+
)
|
119
|
+
argget.add_argument(
|
120
|
+
"--maxlogentries",
|
121
|
+
"-maxlogentries",
|
122
|
+
type=int,
|
123
|
+
help="The maximum number of log entries to collect in each log service",
|
124
|
+
)
|
125
|
+
argget.add_argument(
|
126
|
+
"--forcefolderrename",
|
127
|
+
"-forcefolderrename",
|
128
|
+
action="store_true",
|
129
|
+
help="Indicates if URIs containing characters that are disallowed in Windows folder names are renamed to replace the characters with underscores",
|
130
|
+
)
|
131
|
+
args, unknown = argget.parse_known_args()
|
132
|
+
|
133
|
+
# Convert the authentication method to something usable with the Redfish library
|
134
|
+
# This is needed for backwards compatibility with older versions of the tool
|
135
|
+
if args.Auth == "Session":
|
136
|
+
args.Auth = "session"
|
137
|
+
else:
|
138
|
+
args.Auth = "basic"
|
139
|
+
|
140
|
+
# Build the base URL for the service
|
141
|
+
# More backwards compatibility
|
142
|
+
if "://" not in args.rhost:
|
143
|
+
if args.Secure:
|
144
|
+
args.rhost = "https://{}".format(args.rhost)
|
145
|
+
else:
|
146
|
+
args.rhost = "http://{}".format(args.rhost)
|
147
|
+
|
148
|
+
# Set up the output
|
149
|
+
if not os.path.isdir(args.Dir):
|
150
|
+
# Does not exist; make the directory
|
151
|
+
try:
|
152
|
+
os.makedirs(args.Dir)
|
153
|
+
except Exception as err:
|
154
|
+
print(
|
155
|
+
"ERROR: Aborting; could not create output directory '{}': {}".format(
|
156
|
+
args.Dir, err
|
157
|
+
)
|
158
|
+
)
|
159
|
+
sys.exit(1)
|
160
|
+
else:
|
161
|
+
if len(os.listdir(args.Dir)) != 0:
|
162
|
+
print("ERROR: Aborting; output directory not empty...")
|
163
|
+
sys.exit(1)
|
164
|
+
|
165
|
+
print("Redfish Mockup Creator, Version {}".format(tool_version))
|
166
|
+
print("Address: {}".format(args.rhost))
|
167
|
+
print("Full Output Path: {}".format(os.path.abspath(args.Dir)))
|
168
|
+
print("Description: {}".format(args.description))
|
169
|
+
print("Starting mockup creation...")
|
170
|
+
if args.quiet:
|
171
|
+
print("Quiet mode enabled; please wait...")
|
172
|
+
|
173
|
+
# Create the readme file
|
174
|
+
try:
|
175
|
+
with open(os.path.join(args.Dir, "README"), "w") as readf:
|
176
|
+
readf.write(
|
177
|
+
"Redfish service captured by the Redfish Mockup Creator, Version {}\n".format(
|
178
|
+
tool_version
|
179
|
+
)
|
180
|
+
)
|
181
|
+
readf.write(
|
182
|
+
"Created: {}\n".format(
|
183
|
+
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
184
|
+
)
|
185
|
+
)
|
186
|
+
readf.write("Service: {}\n".format(args.rhost))
|
187
|
+
readf.write("User: {}\n".format(args.user))
|
188
|
+
readf.write("Description: {}\n".format(args.description))
|
189
|
+
except Exception as err:
|
190
|
+
print(
|
191
|
+
"ERROR: Aborting; could not create README file in output directory: {}".format(
|
192
|
+
err
|
193
|
+
)
|
194
|
+
)
|
195
|
+
sys.exit(1)
|
196
|
+
|
197
|
+
# Set up the trace file if requested
|
198
|
+
if args.trace:
|
199
|
+
redfish_logger(
|
200
|
+
os.path.join(args.Dir, "rf-mockup-create.log"),
|
201
|
+
"%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
202
|
+
logging.DEBUG,
|
203
|
+
)
|
204
|
+
|
205
|
+
# Set up the Redfish object
|
206
|
+
try:
|
207
|
+
redfish_obj = redfish.redfish_client(
|
208
|
+
base_url=args.rhost, username=args.user, password=args.password
|
209
|
+
)
|
210
|
+
redfish_obj.login(auth=args.Auth)
|
211
|
+
except Exception as err:
|
212
|
+
print(
|
213
|
+
"ERROR: Aborting; could not authenticate with the Redfish service: {}".format(
|
214
|
+
err
|
215
|
+
)
|
216
|
+
)
|
217
|
+
sys.exit(1)
|
218
|
+
|
219
|
+
# Scan the service
|
220
|
+
response_times = {}
|
221
|
+
scan_resource(redfish_obj, args, response_times, "/redfish")
|
222
|
+
scan_resource(redfish_obj, args, response_times, "/redfish/v1/odata")
|
223
|
+
scan_resource(
|
224
|
+
redfish_obj, args, response_times, "/redfish/v1/$metadata", is_csdl=True
|
225
|
+
)
|
226
|
+
scan_resource(redfish_obj, args, response_times, "/redfish/v1")
|
227
|
+
redfish_obj.logout()
|
228
|
+
|
229
|
+
# Add time statistics to the readme
|
230
|
+
total_response_time = sum(response_times.values())
|
231
|
+
average_response_time = total_response_time / len(response_times)
|
232
|
+
min_response_uri = min(response_times, key=response_times.get)
|
233
|
+
max_response_uri = max(response_times, key=response_times.get)
|
234
|
+
with open(os.path.join(args.Dir, "README"), "a") as readf:
|
235
|
+
readf.write("Total response time: {}\n".format(total_response_time))
|
236
|
+
readf.write("Average response time: {}\n".format(average_response_time))
|
237
|
+
readf.write(
|
238
|
+
"Minimum response time: {}, {}\n".format(
|
239
|
+
response_times[min_response_uri], min_response_uri
|
240
|
+
)
|
241
|
+
)
|
242
|
+
readf.write(
|
243
|
+
"Maximum response time: {}, {}\n".format(
|
244
|
+
response_times[max_response_uri], max_response_uri
|
245
|
+
)
|
246
|
+
)
|
247
|
+
|
248
|
+
print("Completed mockup creation!")
|
249
|
+
|
250
|
+
|
251
|
+
def scan_resource(redfish_obj, args, response_times, uri, is_csdl=False):
|
252
|
+
"""
|
253
|
+
Scans a resource and saves its response
|
254
|
+
|
255
|
+
Args:
|
256
|
+
redfish_obj: The Redfish client object with an open session
|
257
|
+
args: The command line arguments
|
258
|
+
response_times: The response times database
|
259
|
+
uri: The URI to get
|
260
|
+
is_csdl: Indicates if the resource is a CSDL file
|
261
|
+
"""
|
262
|
+
|
263
|
+
# Check if the URI is a relative URI
|
264
|
+
if not uri.startswith("/"):
|
265
|
+
return
|
266
|
+
|
267
|
+
# Set up the output folder
|
268
|
+
try:
|
269
|
+
path = uri[1:]
|
270
|
+
if folder_name_fix or args.forcefolderrename:
|
271
|
+
for character in disallowed_folder_characters_win:
|
272
|
+
path = path.replace(character, "_")
|
273
|
+
path = os.path.join(args.Dir, path)
|
274
|
+
if not os.path.isdir(path):
|
275
|
+
# Does not exist; make the directory
|
276
|
+
os.makedirs(path)
|
277
|
+
except Exception as err:
|
278
|
+
print("ERROR: Could not create directory for '{}': {}".format(uri, err))
|
279
|
+
return
|
280
|
+
|
281
|
+
# Check if the index file already exists
|
282
|
+
index_name = "index.json"
|
283
|
+
if is_csdl:
|
284
|
+
index_name = "index.xml"
|
285
|
+
index_path = os.path.join(path, index_name)
|
286
|
+
if os.path.isfile(index_path):
|
287
|
+
# File exists; already scanned this resource
|
288
|
+
return
|
289
|
+
|
290
|
+
# Get the resource
|
291
|
+
if not args.quiet:
|
292
|
+
print("Getting {}...".format(uri))
|
293
|
+
try:
|
294
|
+
start_time = time.time()
|
295
|
+
resource = redfish_obj.get(uri, headers={"Accept-Encoding": "*"})
|
296
|
+
end_time = time.time()
|
297
|
+
except Exception as err:
|
298
|
+
print("ERROR: Could not get '{}': {}".format(uri, err))
|
299
|
+
return
|
300
|
+
|
301
|
+
# Save the resource and other information
|
302
|
+
try:
|
303
|
+
# Save the resource itself
|
304
|
+
if is_csdl:
|
305
|
+
with open(index_path, "w", encoding="utf-8") as file:
|
306
|
+
file.write(resource.text)
|
307
|
+
else:
|
308
|
+
save_dict = resource.dict
|
309
|
+
|
310
|
+
# Prune the log entry collection if needed
|
311
|
+
if (
|
312
|
+
save_dict.get("@odata.type", None)
|
313
|
+
== "#LogEntryCollection.LogEntryCollection"
|
314
|
+
and args.maxlogentries is not None
|
315
|
+
):
|
316
|
+
if args.maxlogentries < 0:
|
317
|
+
args.maxlogentries = 0
|
318
|
+
if "Members@odata.nextLink" in save_dict:
|
319
|
+
save_dict.pop("Members@odata.nextLink")
|
320
|
+
if "Members" in save_dict:
|
321
|
+
if isinstance(save_dict["Members"], list):
|
322
|
+
for i in range(
|
323
|
+
0, len(save_dict["Members"]) - args.maxlogentries
|
324
|
+
):
|
325
|
+
save_dict["Members"].pop()
|
326
|
+
save_dict["Members@odata.count"] = len(save_dict["Members"])
|
327
|
+
|
328
|
+
# The saved copy might contain URI fixes and other changes that aren't reflective of the service, but are
|
329
|
+
# needed to ensure compatibility with the system creating the mockup
|
330
|
+
scan_dict = copy.deepcopy(save_dict)
|
331
|
+
|
332
|
+
# Add the copyright statement if needed
|
333
|
+
if args.Copyright:
|
334
|
+
save_dict["@Redfish.Copyright"] = args.Copyright
|
335
|
+
|
336
|
+
# Update the payload's URIs if they need to be corrected based on allowable folder names for the system
|
337
|
+
if folder_name_fix or args.forcefolderrename:
|
338
|
+
fix_uris(save_dict)
|
339
|
+
|
340
|
+
with open(index_path, "w", encoding="utf-8") as file:
|
341
|
+
json.dump(save_dict, file, indent=4, separators=(",", ": "))
|
342
|
+
|
343
|
+
# Deep copies of all payloads gets expensive; force garbage collection to avoid stack overflows
|
344
|
+
del save_dict
|
345
|
+
gc.collect()
|
346
|
+
except Exception as err:
|
347
|
+
print("ERROR: Could not save '{}': {}".format(uri, err))
|
348
|
+
print("Attempting to save response data in error.txt...")
|
349
|
+
try:
|
350
|
+
with open(os.path.join(path, "error.txt"), "w", encoding="utf-8") as file:
|
351
|
+
file.write("HTTP {}\n".format(resource.status))
|
352
|
+
for header in resource.getheaders():
|
353
|
+
file.write("{}: {}\n".format(header[0], header[1]))
|
354
|
+
file.write("\n")
|
355
|
+
file.write(resource.text)
|
356
|
+
except:
|
357
|
+
print("Could not save response data; moving on... ")
|
358
|
+
return
|
359
|
+
|
360
|
+
# Save additional info
|
361
|
+
try:
|
362
|
+
# Save headers
|
363
|
+
if args.Headers:
|
364
|
+
with open(
|
365
|
+
os.path.join(path, "headers.json"), "w", encoding="utf-8"
|
366
|
+
) as file:
|
367
|
+
headers_dict = {}
|
368
|
+
for header in resource.getheaders():
|
369
|
+
headers_dict[header[0]] = header[1]
|
370
|
+
json.dump({"GET": headers_dict}, file, indent=4, separators=(",", ": "))
|
371
|
+
|
372
|
+
# Save timing info
|
373
|
+
response_times[uri] = end_time - start_time
|
374
|
+
if args.Time:
|
375
|
+
with open(os.path.join(path, "time.json"), "w", encoding="utf-8") as file:
|
376
|
+
json.dump(
|
377
|
+
{"GET_Time": "{0:.2f}".format(response_times[uri])},
|
378
|
+
file,
|
379
|
+
indent=4,
|
380
|
+
separators=(",", ": "),
|
381
|
+
)
|
382
|
+
except Exception as err:
|
383
|
+
print(
|
384
|
+
"ERROR: Could not save header or timing data for '{}': {}".format(uri, err)
|
385
|
+
)
|
386
|
+
return
|
387
|
+
|
388
|
+
# Scan the response to see where to go next
|
389
|
+
try:
|
390
|
+
if is_csdl:
|
391
|
+
scan_csdl(redfish_obj, args, response_times, resource.text)
|
392
|
+
else:
|
393
|
+
scan_object(redfish_obj, args, response_times, scan_dict)
|
394
|
+
except Exception as err:
|
395
|
+
print("ERROR: Could not scan '{}': {}".format(uri, err))
|
396
|
+
return
|
397
|
+
|
398
|
+
|
399
|
+
def scan_object(redfish_obj, args, response_times, object):
|
400
|
+
"""
|
401
|
+
Scans an object or array to find links to other resources
|
402
|
+
|
403
|
+
Args:
|
404
|
+
redfish_obj: The Redfish client object with an open session
|
405
|
+
args: The command line arguments
|
406
|
+
response_times: The response times database
|
407
|
+
object: The object to scan
|
408
|
+
"""
|
409
|
+
|
410
|
+
for item in object:
|
411
|
+
# If the object is a dictionary, inspect the properties found
|
412
|
+
if isinstance(object, dict):
|
413
|
+
# If the item is a reference, go to the resource
|
414
|
+
if (
|
415
|
+
item == "@odata.id"
|
416
|
+
or item == "Uri"
|
417
|
+
or item == "Members@odata.nextLink"
|
418
|
+
or item == "@Redfish.ActionInfo"
|
419
|
+
):
|
420
|
+
if isinstance(object[item], str):
|
421
|
+
if object[item].startswith("/") and "#" not in object[item]:
|
422
|
+
scan_resource(redfish_obj, args, response_times, object[item])
|
423
|
+
|
424
|
+
# If the item is an object or array, scan one level deeper
|
425
|
+
elif isinstance(object[item], dict) or isinstance(object[item], list):
|
426
|
+
scan_object(redfish_obj, args, response_times, object[item])
|
427
|
+
|
428
|
+
# If the object is a list, see if the member needs to be scanned
|
429
|
+
elif isinstance(object, list):
|
430
|
+
if isinstance(item, dict) or isinstance(item, list):
|
431
|
+
scan_object(redfish_obj, args, response_times, item)
|
432
|
+
|
433
|
+
|
434
|
+
def scan_csdl(redfish_obj, args, response_times, csdl):
|
435
|
+
"""
|
436
|
+
Scans a CSDL string to find links to other CSDL files
|
437
|
+
|
438
|
+
Args:
|
439
|
+
redfish_obj: The Redfish client object with an open session
|
440
|
+
args: The command line arguments
|
441
|
+
response_times: The response times database
|
442
|
+
csdl: The CSDL string to scan
|
443
|
+
"""
|
444
|
+
|
445
|
+
# Convert to an element tree object
|
446
|
+
tree = ET.ElementTree(ET.fromstring(csdl))
|
447
|
+
root = tree.getroot()
|
448
|
+
|
449
|
+
# Find references
|
450
|
+
for reference in root:
|
451
|
+
if "reference" in str(reference.tag).lower():
|
452
|
+
if "Reference" not in str(reference.tag):
|
453
|
+
print(
|
454
|
+
"Warning: Found invalid reference tag '{}'; tags are case sensitive!".format(
|
455
|
+
str(reference.tag)
|
456
|
+
)
|
457
|
+
)
|
458
|
+
for tag in ["Uri", "uri", "URI"]:
|
459
|
+
uri = reference.attrib.get(tag)
|
460
|
+
if uri is not None:
|
461
|
+
if tag != "Uri":
|
462
|
+
print(
|
463
|
+
"Warning: Found invalid Uri attribute '{}'; attributes are case sensitive!".format(
|
464
|
+
tag
|
465
|
+
)
|
466
|
+
)
|
467
|
+
# Scan the reference
|
468
|
+
scan_resource(redfish_obj, args, response_times, uri, is_csdl=True)
|
469
|
+
|
470
|
+
|
471
|
+
def fix_uris(payload):
|
472
|
+
"""
|
473
|
+
Updates URIs in a payload to ensure they do not conflict with local system folder name rules
|
474
|
+
|
475
|
+
Args:
|
476
|
+
payload: The payload to update
|
477
|
+
"""
|
478
|
+
|
479
|
+
for item in payload:
|
480
|
+
# If the payload is a dictionary, inspect the properties found
|
481
|
+
if isinstance(payload, dict):
|
482
|
+
# If the item is a reference, go to the resource
|
483
|
+
if item == "@odata.id" or item == "Uri" or item == "Members@odata.nextLink":
|
484
|
+
if isinstance(payload[item], str):
|
485
|
+
for character in disallowed_folder_characters_win:
|
486
|
+
payload[item] = payload[item].replace(character, "_")
|
487
|
+
|
488
|
+
# If the item is an object or array, scan one level deeper
|
489
|
+
elif isinstance(payload[item], dict) or isinstance(payload[item], list):
|
490
|
+
fix_uris(payload[item])
|
491
|
+
|
492
|
+
# If the object is a list, see if the member needs to be scanned
|
493
|
+
elif isinstance(payload, list):
|
494
|
+
if isinstance(item, dict) or isinstance(item, list):
|
495
|
+
fix_uris(item)
|
496
|
+
|
497
|
+
|
498
|
+
if __name__ == "__main__":
|
499
|
+
sys.exit(main())
|