osism 0.20250827.0__tar.gz → 0.20250902.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.20250902.0/AUTHORS +1 -0
- osism-0.20250902.0/ChangeLog +7 -0
- {osism-0.20250827.0/osism.egg-info → osism-0.20250902.0}/PKG-INFO +1 -1
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/components/EventsFilters.tsx +2 -2
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/components/EventsList.tsx +8 -8
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/events/page.tsx +3 -3
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/nodes/page.tsx +105 -20
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/page.tsx +2 -2
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/lib/hooks/useWebSocket.ts +16 -16
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/package-lock.json +4 -4
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/package.json +1 -1
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/utils.py +10 -0
- {osism-0.20250827.0 → osism-0.20250902.0/osism.egg-info}/PKG-INFO +1 -1
- osism-0.20250902.0/osism.egg-info/pbr.json +1 -0
- osism-0.20250827.0/AUTHORS +0 -1
- osism-0.20250827.0/ChangeLog +0 -7
- osism-0.20250827.0/osism.egg-info/pbr.json +0 -1
- {osism-0.20250827.0 → osism-0.20250902.0}/.flake8 +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/.github/renovate.json +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/.github/workflows/publish.yml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/.hadolint.yaml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/.zuul.yaml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/Containerfile +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/Dockerfile +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/LICENSE +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/Pipfile +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/Pipfile.lock +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/README.md +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/change.sh +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/cleanup-ansible-collections.sh +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/clustershell/clush.conf +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/clustershell/groups.conf +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/data/SCS-Spec.MandatoryFlavors.verbose.yaml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/data/flavors.yaml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/netbox-manager/settings.toml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/redfishMockupCreate.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/run-ansible-console.sh +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/config_db.json +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/port_config/Accton-AS4625-54T.ini +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/port_config/Accton-AS5835-54T.ini +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/port_config/Accton-AS5835-54X.ini +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/port_config/Accton-AS7326-56X.ini +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/port_config/Accton-AS7726-32X.ini +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/files/sonic/port_config/Accton-AS9716-32D.ini +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/.dockerignore +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/.gitignore +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/Containerfile +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/Dockerfile +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/api/config/route.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/api/health/route.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/components/ConnectionStatus.tsx +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/favicon.ico +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/globals.css +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/layout.tsx +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/app/services/page.tsx +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/components.json +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/eslint.config.mjs +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/lib/api.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/lib/types.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/lib/utils.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/next.config.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/postcss.config.mjs +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/public/file.svg +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/public/globe.svg +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/public/next.svg +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/public/vercel.svg +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/public/window.svg +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/tailwind.config.ts +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/frontend/tsconfig.json +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/__main__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/api.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/apply.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/baremetal.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/compose.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/compute.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/configuration.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/console.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/container.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/get.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/lock.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/log.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/manage.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/netbox.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/noset.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/reconciler.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/redfish.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/server.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/service.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/set.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/sonic.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/status.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/sync.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/task.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/validate.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/vault.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/volume.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/wait.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/commands/worker.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/data/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/data/enums.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/data/playbooks.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/main.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/services/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/services/event_bridge.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/services/listener.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/services/websocket_manager.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/settings.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/ansible.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/ceph.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/config.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/ironic.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/netbox.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/redfish.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/bgp.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/cache.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/config_generator.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/connections.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/constants.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/device.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/exporter.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/interface.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor/sonic/sync.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/conductor.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/kolla.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/kubernetes.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/netbox.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/openstack.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/tasks/reconciler.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/utils/__init__.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism/utils/ssh.py +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism.egg-info/SOURCES.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism.egg-info/dependency_links.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism.egg-info/entry_points.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism.egg-info/not-zip-safe +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism.egg-info/requires.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/osism.egg-info/top_level.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/playbooks/build.yml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/playbooks/pre.yml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/playbooks/test-setup.yml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/requirements.ansible.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/requirements.netbox-manager.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/requirements.openstack-flavor-manager.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/requirements.openstack-image-manager.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/requirements.txt +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/requirements.yml +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/setup.cfg +0 -0
- {osism-0.20250827.0 → osism-0.20250902.0}/setup.py +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
Christian Berendt <berendt@osism.tech>
|
@@ -12,7 +12,7 @@ interface EventsFiltersProps {
|
|
12
12
|
const BAREMETAL_EVENT_TYPES = [
|
13
13
|
"baremetal.node.power_set.end",
|
14
14
|
"baremetal.node.provision_set.start",
|
15
|
-
"baremetal.node.provision_set.end",
|
15
|
+
"baremetal.node.provision_set.end",
|
16
16
|
"baremetal.node.provision_set.success",
|
17
17
|
"baremetal.node.power_state_corrected.success",
|
18
18
|
"baremetal.node.maintenance_set.end",
|
@@ -49,7 +49,7 @@ export default function EventsFilters({ onFiltersChange, className = "" }: Event
|
|
49
49
|
}, [onFiltersChange]);
|
50
50
|
|
51
51
|
const toggleEventType = useCallback((eventType: string) => {
|
52
|
-
setSelectedEventTypes(prev =>
|
52
|
+
setSelectedEventTypes(prev =>
|
53
53
|
prev.includes(eventType)
|
54
54
|
? prev.filter(t => t !== eventType)
|
55
55
|
: [...prev, eventType]
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
import { useMemo, useState } from "react";
|
4
4
|
import { format } from "date-fns";
|
5
|
-
import {
|
6
|
-
Server,
|
7
|
-
Power,
|
8
|
-
Settings,
|
9
|
-
Trash2,
|
5
|
+
import {
|
6
|
+
Server,
|
7
|
+
Power,
|
8
|
+
Settings,
|
9
|
+
Trash2,
|
10
10
|
Plus,
|
11
11
|
CheckCircle,
|
12
12
|
XCircle,
|
@@ -52,17 +52,17 @@ const formatEventData = (event: OpenStackEvent) => {
|
|
52
52
|
if (event.data.service_type === "baremetal" && "ironic_object" in event.data) {
|
53
53
|
const baremetalEvent = event as BaremetalEvent;
|
54
54
|
const ironicData = baremetalEvent.data.ironic_object?.data;
|
55
|
-
|
55
|
+
|
56
56
|
if (!ironicData) return null;
|
57
57
|
|
58
58
|
const details = [];
|
59
59
|
if (ironicData.power_state) details.push(`Power: ${ironicData.power_state}`);
|
60
60
|
if (ironicData.provision_state) details.push(`Provision: ${ironicData.provision_state}`);
|
61
61
|
if (ironicData.maintenance !== undefined) details.push(`Maintenance: ${ironicData.maintenance ? "Yes" : "No"}`);
|
62
|
-
|
62
|
+
|
63
63
|
return details.join(" | ");
|
64
64
|
}
|
65
|
-
|
65
|
+
|
66
66
|
return null;
|
67
67
|
};
|
68
68
|
|
@@ -109,7 +109,7 @@ export default function EventsPage() {
|
|
109
109
|
Real-time Baremetal events from OpenStack Ironic
|
110
110
|
</p>
|
111
111
|
</div>
|
112
|
-
|
112
|
+
|
113
113
|
{/* Connection Status */}
|
114
114
|
<ConnectionStatus status={connectionStatus} />
|
115
115
|
</div>
|
@@ -131,7 +131,7 @@ export default function EventsPage() {
|
|
131
131
|
)}
|
132
132
|
</div>
|
133
133
|
</div>
|
134
|
-
|
134
|
+
|
135
135
|
<EventsFilters onFiltersChange={handleFiltersChange} />
|
136
136
|
</div>
|
137
137
|
|
@@ -144,7 +144,7 @@ export default function EventsPage() {
|
|
144
144
|
<RefreshCw className="h-4 w-4 mr-2" />
|
145
145
|
Reconnect
|
146
146
|
</button>
|
147
|
-
|
147
|
+
|
148
148
|
<button
|
149
149
|
onClick={handleClearEvents}
|
150
150
|
className="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50"
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
import { useState } from "react";
|
4
4
|
import { useQuery } from "@tanstack/react-query";
|
5
|
-
import { RefreshCw, Search, AlertCircle, ArrowUpDown } from "lucide-react";
|
5
|
+
import { RefreshCw, Search, AlertCircle, ArrowUpDown, ChevronLeft, ChevronRight } from "lucide-react";
|
6
6
|
import api from "@/lib/api";
|
7
7
|
import { BaremetalNode } from "@/lib/types";
|
8
8
|
|
@@ -12,6 +12,8 @@ export default function NodesPage() {
|
|
12
12
|
const [filterPowerState, setFilterPowerState] = useState("all");
|
13
13
|
const [filterMaintenance, setFilterMaintenance] = useState("all");
|
14
14
|
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
|
15
|
+
const [currentPage, setCurrentPage] = useState(1);
|
16
|
+
const itemsPerPage = 10;
|
15
17
|
|
16
18
|
const { data, isLoading, error, refetch, isRefetching } = useQuery({
|
17
19
|
queryKey: ["baremetal-nodes"],
|
@@ -21,14 +23,14 @@ export default function NodesPage() {
|
|
21
23
|
|
22
24
|
const filteredAndSortedNodes = data?.nodes
|
23
25
|
.filter((node: BaremetalNode) => {
|
24
|
-
const matchesSearch = searchTerm === "" ||
|
26
|
+
const matchesSearch = searchTerm === "" ||
|
25
27
|
(node.name && node.name.toLowerCase().includes(searchTerm.toLowerCase())) ||
|
26
28
|
(node.uuid && node.uuid.toLowerCase().includes(searchTerm.toLowerCase()));
|
27
|
-
|
28
|
-
const matchesProvisionState = filterProvisionState === "all" ||
|
29
|
+
|
30
|
+
const matchesProvisionState = filterProvisionState === "all" ||
|
29
31
|
node.provision_state === filterProvisionState;
|
30
|
-
|
31
|
-
const matchesPowerState = filterPowerState === "all" ||
|
32
|
+
|
33
|
+
const matchesPowerState = filterPowerState === "all" ||
|
32
34
|
node.power_state === filterPowerState;
|
33
35
|
|
34
36
|
const matchesMaintenance = filterMaintenance === "all" ||
|
@@ -40,7 +42,7 @@ export default function NodesPage() {
|
|
40
42
|
.sort((a: BaremetalNode, b: BaremetalNode) => {
|
41
43
|
const nameA = a.name || a.uuid || '';
|
42
44
|
const nameB = b.name || b.uuid || '';
|
43
|
-
|
45
|
+
|
44
46
|
if (sortDirection === "asc") {
|
45
47
|
return nameA.localeCompare(nameB);
|
46
48
|
} else {
|
@@ -48,14 +50,25 @@ export default function NodesPage() {
|
|
48
50
|
}
|
49
51
|
});
|
50
52
|
|
53
|
+
const totalFilteredNodes = filteredAndSortedNodes?.length || 0;
|
54
|
+
const totalPages = Math.ceil(totalFilteredNodes / itemsPerPage);
|
55
|
+
const startIndex = (currentPage - 1) * itemsPerPage;
|
56
|
+
const endIndex = startIndex + itemsPerPage;
|
57
|
+
const paginatedNodes = filteredAndSortedNodes?.slice(startIndex, endIndex);
|
58
|
+
|
59
|
+
// Reset to first page when filters change
|
60
|
+
const resetToFirstPage = () => {
|
61
|
+
setCurrentPage(1);
|
62
|
+
};
|
63
|
+
|
51
64
|
const toggleSortDirection = () => {
|
52
65
|
setSortDirection(prev => prev === "asc" ? "desc" : "asc");
|
53
66
|
};
|
54
67
|
|
55
|
-
const uniqueProvisionStates = data ?
|
68
|
+
const uniqueProvisionStates = data ?
|
56
69
|
[...new Set(data.nodes.map(n => n.provision_state).filter((state): state is string => Boolean(state)))] : [];
|
57
|
-
|
58
|
-
const uniquePowerStates = data ?
|
70
|
+
|
71
|
+
const uniquePowerStates = data ?
|
59
72
|
[...new Set(data.nodes.map(n => n.power_state).filter((state): state is string => Boolean(state)))] : [];
|
60
73
|
|
61
74
|
return (
|
@@ -95,7 +108,10 @@ export default function NodesPage() {
|
|
95
108
|
className="focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 pr-3 py-2 sm:text-sm border-gray-300 rounded-md"
|
96
109
|
placeholder="Name or UUID..."
|
97
110
|
value={searchTerm}
|
98
|
-
onChange={(e) =>
|
111
|
+
onChange={(e) => {
|
112
|
+
setSearchTerm(e.target.value);
|
113
|
+
resetToFirstPage();
|
114
|
+
}}
|
99
115
|
/>
|
100
116
|
</div>
|
101
117
|
</div>
|
@@ -109,7 +125,10 @@ export default function NodesPage() {
|
|
109
125
|
name="provision-state"
|
110
126
|
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
|
111
127
|
value={filterProvisionState}
|
112
|
-
onChange={(e) =>
|
128
|
+
onChange={(e) => {
|
129
|
+
setFilterProvisionState(e.target.value);
|
130
|
+
resetToFirstPage();
|
131
|
+
}}
|
113
132
|
>
|
114
133
|
<option value="all">All</option>
|
115
134
|
{uniqueProvisionStates.map((state, index) => (
|
@@ -127,7 +146,10 @@ export default function NodesPage() {
|
|
127
146
|
name="power-state"
|
128
147
|
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
|
129
148
|
value={filterPowerState}
|
130
|
-
onChange={(e) =>
|
149
|
+
onChange={(e) => {
|
150
|
+
setFilterPowerState(e.target.value);
|
151
|
+
resetToFirstPage();
|
152
|
+
}}
|
131
153
|
>
|
132
154
|
<option value="all">All</option>
|
133
155
|
{uniquePowerStates.map((state, index) => (
|
@@ -145,7 +167,10 @@ export default function NodesPage() {
|
|
145
167
|
name="maintenance-state"
|
146
168
|
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md"
|
147
169
|
value={filterMaintenance}
|
148
|
-
onChange={(e) =>
|
170
|
+
onChange={(e) => {
|
171
|
+
setFilterMaintenance(e.target.value);
|
172
|
+
resetToFirstPage();
|
173
|
+
}}
|
149
174
|
>
|
150
175
|
<option value="all">All</option>
|
151
176
|
<option value="active">Active</option>
|
@@ -185,7 +210,7 @@ export default function NodesPage() {
|
|
185
210
|
</div>
|
186
211
|
</div>
|
187
212
|
</div>
|
188
|
-
) :
|
213
|
+
) : paginatedNodes && paginatedNodes.length > 0 ? (
|
189
214
|
<div className="bg-white shadow overflow-hidden sm:rounded-md">
|
190
215
|
<div className="px-4 py-3 bg-gray-50 border-b border-gray-200">
|
191
216
|
<div className="flex items-center">
|
@@ -202,7 +227,7 @@ export default function NodesPage() {
|
|
202
227
|
</div>
|
203
228
|
</div>
|
204
229
|
<ul className="divide-y divide-gray-200">
|
205
|
-
{
|
230
|
+
{paginatedNodes.map((node: BaremetalNode, index) => (
|
206
231
|
<li key={node.uuid || `node-${index}`}>
|
207
232
|
<div className="px-4 py-4 sm:px-6">
|
208
233
|
<div className="flex items-center justify-between">
|
@@ -220,7 +245,7 @@ export default function NodesPage() {
|
|
220
245
|
</div>
|
221
246
|
<div className="flex items-center justify-end gap-2">
|
222
247
|
<span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${
|
223
|
-
node.power_state === "power on"
|
248
|
+
node.power_state === "power on"
|
224
249
|
? "bg-green-100 text-green-800"
|
225
250
|
: "bg-gray-100 text-gray-800"
|
226
251
|
}`}>
|
@@ -283,9 +308,69 @@ export default function NodesPage() {
|
|
283
308
|
</div>
|
284
309
|
)}
|
285
310
|
|
286
|
-
{data && (
|
287
|
-
<div className="mt-
|
288
|
-
|
311
|
+
{data && totalFilteredNodes > 0 && (
|
312
|
+
<div className="mt-6">
|
313
|
+
<div className="flex items-center justify-between mb-4">
|
314
|
+
<div className="text-sm text-gray-600">
|
315
|
+
Showing {startIndex + 1} to {Math.min(endIndex, totalFilteredNodes)} of {totalFilteredNodes} filtered nodes
|
316
|
+
{totalFilteredNodes !== data.count && (
|
317
|
+
<span className="text-gray-500"> ({data.count} total)</span>
|
318
|
+
)}
|
319
|
+
</div>
|
320
|
+
{totalPages > 1 && (
|
321
|
+
<div className="flex items-center space-x-2">
|
322
|
+
<button
|
323
|
+
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
|
324
|
+
disabled={currentPage === 1}
|
325
|
+
className="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
326
|
+
>
|
327
|
+
<ChevronLeft className="h-4 w-4 mr-1" />
|
328
|
+
Previous
|
329
|
+
</button>
|
330
|
+
|
331
|
+
<div className="flex items-center space-x-1">
|
332
|
+
{Array.from({ length: totalPages }, (_, i) => i + 1)
|
333
|
+
.filter(page => {
|
334
|
+
// Show first page, last page, current page, and pages around current
|
335
|
+
return page === 1 ||
|
336
|
+
page === totalPages ||
|
337
|
+
Math.abs(page - currentPage) <= 1;
|
338
|
+
})
|
339
|
+
.map((page, index, array) => {
|
340
|
+
// Add ellipsis if there's a gap
|
341
|
+
const showEllipsisBefore = index > 0 && page - array[index - 1] > 1;
|
342
|
+
return (
|
343
|
+
<div key={page} className="flex items-center">
|
344
|
+
{showEllipsisBefore && (
|
345
|
+
<span className="px-2 py-1 text-gray-500">...</span>
|
346
|
+
)}
|
347
|
+
<button
|
348
|
+
onClick={() => setCurrentPage(page)}
|
349
|
+
className={`px-3 py-2 text-sm font-medium rounded-md ${
|
350
|
+
currentPage === page
|
351
|
+
? "bg-blue-500 text-white"
|
352
|
+
: "text-gray-700 bg-white border border-gray-300 hover:bg-gray-50"
|
353
|
+
} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500`}
|
354
|
+
>
|
355
|
+
{page}
|
356
|
+
</button>
|
357
|
+
</div>
|
358
|
+
);
|
359
|
+
})
|
360
|
+
}
|
361
|
+
</div>
|
362
|
+
|
363
|
+
<button
|
364
|
+
onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
|
365
|
+
disabled={currentPage === totalPages}
|
366
|
+
className="inline-flex items-center px-3 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
367
|
+
>
|
368
|
+
Next
|
369
|
+
<ChevronRight className="h-4 w-4 ml-1" />
|
370
|
+
</button>
|
371
|
+
</div>
|
372
|
+
)}
|
373
|
+
</div>
|
289
374
|
</div>
|
290
375
|
)}
|
291
376
|
</div>
|
@@ -28,7 +28,7 @@ export default function Home() {
|
|
28
28
|
loading: nodesLoading,
|
29
29
|
},
|
30
30
|
{
|
31
|
-
id: "active-nodes",
|
31
|
+
id: "active-nodes",
|
32
32
|
name: "Active Nodes",
|
33
33
|
value: nodesData?.nodes.filter(n => n.provision_state === "active").length || 0,
|
34
34
|
icon: Activity,
|
@@ -37,7 +37,7 @@ export default function Home() {
|
|
37
37
|
},
|
38
38
|
{
|
39
39
|
id: "services",
|
40
|
-
name: "Services",
|
40
|
+
name: "Services",
|
41
41
|
value: "N/A",
|
42
42
|
icon: Settings,
|
43
43
|
href: "/services",
|
@@ -58,7 +58,7 @@ export const useWebSocket = (
|
|
58
58
|
}
|
59
59
|
|
60
60
|
isConnectingRef.current = true;
|
61
|
-
|
61
|
+
|
62
62
|
try {
|
63
63
|
// Convert http/https URL to ws/wss for WebSocket
|
64
64
|
const wsUrl = url.replace(/^http/, 'ws');
|
@@ -69,25 +69,25 @@ export const useWebSocket = (
|
|
69
69
|
console.log('WebSocket connected:', wsUrl);
|
70
70
|
isConnectingRef.current = false;
|
71
71
|
reconnectAttemptsRef.current = 0;
|
72
|
-
|
72
|
+
|
73
73
|
setConnectionStatus({
|
74
74
|
connected: true,
|
75
75
|
lastConnected: new Date()
|
76
76
|
});
|
77
|
-
|
77
|
+
|
78
78
|
onConnect?.();
|
79
79
|
};
|
80
80
|
|
81
81
|
websocket.onmessage = (event) => {
|
82
82
|
try {
|
83
83
|
const data = JSON.parse(event.data);
|
84
|
-
|
84
|
+
|
85
85
|
// Handle filter acknowledgment messages
|
86
86
|
if (data.type === 'filter_update') {
|
87
87
|
console.log('Filters updated:', data);
|
88
88
|
return;
|
89
89
|
}
|
90
|
-
|
90
|
+
|
91
91
|
// Handle OpenStack events
|
92
92
|
if (data.event_type) {
|
93
93
|
addEvent(data as OpenStackEvent);
|
@@ -101,19 +101,19 @@ export const useWebSocket = (
|
|
101
101
|
console.log('WebSocket disconnected:', event.code, event.reason);
|
102
102
|
isConnectingRef.current = false;
|
103
103
|
wsRef.current = null;
|
104
|
-
|
104
|
+
|
105
105
|
setConnectionStatus({
|
106
106
|
connected: false,
|
107
107
|
error: event.reason || 'Connection closed'
|
108
108
|
});
|
109
|
-
|
109
|
+
|
110
110
|
onDisconnect?.();
|
111
|
-
|
111
|
+
|
112
112
|
// Attempt reconnection if not manually closed
|
113
113
|
if (event.code !== 1000 && reconnectAttemptsRef.current < reconnectAttempts) {
|
114
114
|
reconnectAttemptsRef.current++;
|
115
115
|
console.log(`Attempting reconnect ${reconnectAttemptsRef.current}/${reconnectAttempts}`);
|
116
|
-
|
116
|
+
|
117
117
|
reconnectTimeoutRef.current = setTimeout(() => {
|
118
118
|
connect();
|
119
119
|
}, reconnectInterval);
|
@@ -124,21 +124,21 @@ export const useWebSocket = (
|
|
124
124
|
console.error('WebSocket error:', error);
|
125
125
|
console.error('WebSocket URL was:', wsUrl);
|
126
126
|
isConnectingRef.current = false;
|
127
|
-
|
127
|
+
|
128
128
|
setConnectionStatus({
|
129
129
|
connected: false,
|
130
130
|
error: 'Connection error - check console for details'
|
131
131
|
});
|
132
|
-
|
132
|
+
|
133
133
|
onError?.(error);
|
134
134
|
};
|
135
135
|
|
136
136
|
wsRef.current = websocket;
|
137
|
-
|
137
|
+
|
138
138
|
} catch (error) {
|
139
139
|
console.error('Failed to create WebSocket connection:', error);
|
140
140
|
isConnectingRef.current = false;
|
141
|
-
|
141
|
+
|
142
142
|
setConnectionStatus({
|
143
143
|
connected: false,
|
144
144
|
error: 'Failed to connect'
|
@@ -150,15 +150,15 @@ export const useWebSocket = (
|
|
150
150
|
if (reconnectTimeoutRef.current) {
|
151
151
|
clearTimeout(reconnectTimeoutRef.current);
|
152
152
|
}
|
153
|
-
|
153
|
+
|
154
154
|
if (wsRef.current) {
|
155
155
|
wsRef.current.close(1000, 'Manual disconnect');
|
156
156
|
wsRef.current = null;
|
157
157
|
}
|
158
|
-
|
158
|
+
|
159
159
|
reconnectAttemptsRef.current = reconnectAttempts; // Prevent reconnection
|
160
160
|
isConnectingRef.current = false;
|
161
|
-
|
161
|
+
|
162
162
|
setConnectionStatus({
|
163
163
|
connected: false
|
164
164
|
});
|
@@ -13,7 +13,7 @@
|
|
13
13
|
"axios": "^1.11.0",
|
14
14
|
"clsx": "^2.1.1",
|
15
15
|
"date-fns": "^4.1.0",
|
16
|
-
"lucide-react": "^0.
|
16
|
+
"lucide-react": "^0.542.0",
|
17
17
|
"next": "15.5.0",
|
18
18
|
"react": "19.1.1",
|
19
19
|
"react-dom": "19.1.1",
|
@@ -4664,9 +4664,9 @@
|
|
4664
4664
|
}
|
4665
4665
|
},
|
4666
4666
|
"node_modules/lucide-react": {
|
4667
|
-
"version": "0.
|
4668
|
-
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.
|
4669
|
-
"integrity": "sha512-
|
4667
|
+
"version": "0.542.0",
|
4668
|
+
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.542.0.tgz",
|
4669
|
+
"integrity": "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw==",
|
4670
4670
|
"license": "ISC",
|
4671
4671
|
"peerDependencies": {
|
4672
4672
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
@@ -232,3 +232,13 @@ def _get_conductor_redfish_address(device):
|
|
232
232
|
logger.warning(f"Could not get conductor Redfish address: {exc}")
|
233
233
|
|
234
234
|
return None
|
235
|
+
|
236
|
+
|
237
|
+
def check_task_lock_and_exit():
|
238
|
+
"""
|
239
|
+
Check if tasks are locked and exit with error message if they are.
|
240
|
+
Used by commands that should not run when tasks are locked.
|
241
|
+
|
242
|
+
This is a convenience wrapper around the main utils function.
|
243
|
+
"""
|
244
|
+
return utils.check_task_lock_and_exit()
|
@@ -0,0 +1 @@
|
|
1
|
+
{"git_version": "8ffeee4", "is_release": false}
|
osism-0.20250827.0/AUTHORS
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
|
osism-0.20250827.0/ChangeLog
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"git_version": "bbad13f", "is_release": false}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|