Skip to content

Commit

Permalink
Merge pull request #289 from stfc/enable-disable-HV-action
Browse files Browse the repository at this point in the history
Enable and Disable Hypervisor in Openstack StacksStorm Action
  • Loading branch information
Dmitry-Popovichev authored Dec 12, 2024
2 parents f947ef1 + c7e3ae5 commit 1e22fb8
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 1 deletion.
38 changes: 38 additions & 0 deletions actions/hv.compute.service.disable.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
description: Disables the nova compute service on a hypervisor
enabled: true
entry_point: src/openstack_actions.py
name: hv.compute.service.disable
parameters:
timeout:
default: 5400
lib_entry_point:
default: workflows.hv_service_actions.hv_service_disable
immutable: true
type: string
requires_openstack:
default: true
immutable: true
type: boolean
cloud_account:
description: "The clouds.yaml account to use whilst performing this action"
required: true
type: string
default: "dev"
enum:
- "dev"
- "prod"
hypervisor_name:
description: Hypervisor to run action on, this should also be the host name on icinga.
required: true
type: string
disabled_reason:
description: The reason this service needs to be disabled.
required: true
type: string
service_binary:
description: The name of the service, i.e. "nova-compute"
type: string
immutable: true
default: "nova-compute"
runner_type: python-script
34 changes: 34 additions & 0 deletions actions/hv.compute.service.enable.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
description: Enables the nova compute service on a hypervisor
enabled: true
entry_point: src/openstack_actions.py
name: hv.compute.service.enable
parameters:
timeout:
default: 5400
lib_entry_point:
default: workflows.hv_service_actions.hv_service_enable
immutable: true
type: string
requires_openstack:
default: true
immutable: true
type: boolean
cloud_account:
description: "The clouds.yaml account to use whilst performing this action"
required: true
type: string
default: "dev"
enum:
- "dev"
- "prod"
hypervisor_name:
description: Hypervisor to run action on, this should also be the host name on icinga.
required: true
type: string
service_binary:
description: The name of the service, i.e. "nova-compute"
type: string
immutable: true
default: "nova-compute"
runner_type: python-script
56 changes: 56 additions & 0 deletions lib/openstack_api/openstack_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from typing import Optional
from openstack.connection import Connection
from openstack.compute.v2.service import Service


def disable_service(
conn: Connection,
service: Service,
hypervisor_name: str,
service_binary: str,
disabled_reason: str,
) -> Optional[Service]:
"""
Disables an Openstack service
:param conn: Openstack connection
:param service: The instance of the service class.
:param hypervisor_name: The name or ID of the hypervisor.
:param service_binary: The name of the service.
:param disabled_reason: The reason for disabling the service.
:return: Returns the Service object.
"""

if service.status == "enabled":
return conn.compute.disable_service(
service,
host=hypervisor_name,
binary=service_binary,
disabled_reason=disabled_reason,
)
raise RuntimeError(
f"Failed to disable {service_binary} on {hypervisor_name}. Already disabled."
)


def enable_service(
conn: Connection,
service: Service,
hypervisor_name: str,
service_binary: str,
) -> Optional[Service]:
"""
Enables an Openstack service
:param conn: Openstack connection
:param service: The instance of the service class.
:param hypervisor_name: The name or ID of the hypervisor.
:param service_binary: The name of the service.
:return: Returns the Service object.
"""

if service.status == "disabled":
return conn.compute.enable_service(
service, host=hypervisor_name, binary=service_binary
)
raise RuntimeError(
f"Failed to enable {service_binary} on {hypervisor_name}. Already enabled."
)
42 changes: 42 additions & 0 deletions lib/workflows/hv_service_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from openstack.connection import Connection
from openstack_api.openstack_service import disable_service, enable_service


def hv_service_disable(
conn: Connection,
hypervisor_name: str,
service_binary: str,
disabled_reason: str,
) -> None:
"""
Disables an Openstack service
:param conn: Openstack connection
:param hypervisor_name: The name or ID of the hypervisor.
:param service_binary: The name of the service.
:param disabled_reason: The reason for disabling the service.
"""

service = conn.compute.find_service(
service_binary, ignore_missing=False, host=hypervisor_name
)

disable_service(conn, service, hypervisor_name, service_binary, disabled_reason)


def hv_service_enable(
conn: Connection,
hypervisor_name: str,
service_binary: str,
) -> None:
"""
Enables an Openstack service
:param conn: Openstack connection
:param hypervisor_name: The name or ID of the hypervisor.
:param service_binary: The name of the service.
"""

service = conn.compute.find_service(
service_binary, ignore_missing=False, host=hypervisor_name
)

enable_service(conn, service, hypervisor_name, service_binary)
2 changes: 1 addition & 1 deletion rules/webhook.server.migrate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ enabled: true

trigger:
type: "core.st2.webhook"
parameters:
parameters:
url: "migrate-server"

action:
Expand Down
92 changes: 92 additions & 0 deletions tests/lib/openstack_api/test_openstack_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from unittest.mock import MagicMock, NonCallableMagicMock
import pytest
from openstack_api.openstack_service import disable_service, enable_service


def test_disable_service_function_disables_service_when_enabled():
"""tests that openstack disables the service when it is currently enabled"""

mock_conn = MagicMock()
mock_service = MagicMock()
mock_hypervisor_name = NonCallableMagicMock()
mock_service_binary = NonCallableMagicMock()
mock_disable_reason = NonCallableMagicMock()

mock_service.status = "enabled"

res = disable_service(
mock_conn,
mock_service,
mock_hypervisor_name,
mock_service_binary,
mock_disable_reason,
)

mock_conn.compute.disable_service.assert_called_once_with(
mock_service,
host=mock_hypervisor_name,
binary=mock_service_binary,
disabled_reason=mock_disable_reason,
)

assert res == mock_conn.compute.disable_service.return_value


def test_disable_service_function_returns_none_when_disabled():
"""tests that openstack returns None when the service is currently disabled"""

mock_conn = MagicMock()
mock_service = MagicMock()
mock_hypervisor_name = NonCallableMagicMock()
mock_service_binary = NonCallableMagicMock()
mock_disable_reason = NonCallableMagicMock()

mock_service.status = "disabled"

with pytest.raises(RuntimeError):
disable_service(
mock_conn,
mock_service,
mock_hypervisor_name,
mock_service_binary,
mock_disable_reason,
)


def test_enable_service_function_enables_service_when_disabled():
"""tests that openstack enables the service when it is currently disabled"""

mock_conn = MagicMock()
mock_service = MagicMock()
mock_hypervisor_name = NonCallableMagicMock()
mock_service_binary = NonCallableMagicMock()

mock_service.status = "disabled"

res = enable_service(
mock_conn, mock_service, mock_hypervisor_name, mock_service_binary
)

mock_conn.compute.enable_service.assert_called_once_with(
mock_service,
host=mock_hypervisor_name,
binary=mock_service_binary,
)

assert res == mock_conn.compute.enable_service.return_value


def test_enable_service_function_returns_none_when_enabled():
"""tests that openstack returns none when the service is currently enabled"""

mock_conn = MagicMock()
mock_service = MagicMock()
mock_hypervisor_name = NonCallableMagicMock()
mock_service_binary = NonCallableMagicMock()

mock_service.status = "enabled"

with pytest.raises(RuntimeError):
enable_service(
mock_conn, mock_service, mock_hypervisor_name, mock_service_binary
)
50 changes: 50 additions & 0 deletions tests/lib/workflows/test_hv_service_actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from unittest.mock import MagicMock, patch, NonCallableMagicMock
from workflows.hv_service_actions import hv_service_disable, hv_service_enable


@patch("workflows.hv_service_actions.disable_service")
def test_service_disabled_successfully(mock_hv_service_disable):
"""test that hv_service_disable disables an openstack service"""

mock_conn = MagicMock()
mock_hypervisor_name = NonCallableMagicMock()
mock_service_binary = NonCallableMagicMock()
mock_disabled_reason = NonCallableMagicMock()

hv_service_disable(
mock_conn, mock_hypervisor_name, mock_service_binary, mock_disabled_reason
)

mock_conn.compute.find_service.assert_called_once_with(
mock_service_binary, ignore_missing=False, host=mock_hypervisor_name
)

mock_hv_service_disable.assert_called_once_with(
mock_conn,
mock_conn.compute.find_service.return_value,
mock_hypervisor_name,
mock_service_binary,
mock_disabled_reason,
)


@patch("workflows.hv_service_actions.enable_service")
def test_hv_service_enabled_successfully(mock_hv_service_enable):
"""test that hv_service_enable enables an openstack service"""

mock_conn = MagicMock()
mock_hypervisor_name = NonCallableMagicMock()
mock_service_binary = NonCallableMagicMock()

hv_service_enable(mock_conn, mock_hypervisor_name, mock_service_binary)

mock_conn.compute.find_service.assert_called_once_with(
mock_service_binary, ignore_missing=False, host=mock_hypervisor_name
)

mock_hv_service_enable.assert_called_once_with(
mock_conn,
mock_conn.compute.find_service.return_value,
mock_hypervisor_name,
mock_service_binary,
)

0 comments on commit 1e22fb8

Please sign in to comment.