Skip to content

Commit 94930b3

Browse files
authored
Merge pull request #74 from stfc/Use_vm_personality_first
Use vm personality first
2 parents ca61faf + 79cd2c4 commit 94930b3

File tree

10 files changed

+195
-60
lines changed

10 files changed

+195
-60
lines changed

OpenStack-Rabbit-Consumer/rabbit_consumer/aq_api.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from urllib3.util.retry import Retry
99

1010
from rabbit_consumer.consumer_config import ConsumerConfig
11-
from rabbit_consumer.image_metadata import ImageMetadata
11+
from rabbit_consumer.aq_metadata import AqMetadata
1212
from rabbit_consumer.openstack_address import OpenstackAddress
1313
from rabbit_consumer.rabbit_message import RabbitMessage
1414
from rabbit_consumer.vm_data import VmData
@@ -84,7 +84,7 @@ def setup_requests(
8484
return response.text
8585

8686

87-
def aq_make(addresses: List[OpenstackAddress], image_meta: ImageMetadata) -> None:
87+
def aq_make(addresses: List[OpenstackAddress], image_meta: AqMetadata) -> None:
8888
"""
8989
Runs AQ make against a list of addresses passed to build the default personality
9090
"""
@@ -111,7 +111,7 @@ def aq_make(addresses: List[OpenstackAddress], image_meta: ImageMetadata) -> Non
111111
setup_requests(url, "post", "Make Template: ", params)
112112

113113

114-
def aq_manage(addresses: List[OpenstackAddress], image_meta: ImageMetadata) -> None:
114+
def aq_manage(addresses: List[OpenstackAddress], image_meta: AqMetadata) -> None:
115115
"""
116116
Manages the list of Aquilon addresses passed to it back to the production domain
117117
"""
@@ -159,7 +159,7 @@ def delete_machine(machine_name: str) -> None:
159159

160160

161161
def create_host(
162-
image_meta: ImageMetadata, addresses: List[OpenstackAddress], machine_name: str
162+
image_meta: AqMetadata, addresses: List[OpenstackAddress], machine_name: str
163163
) -> None:
164164
"""
165165
Creates a host in Aquilon
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import logging
2+
from dataclasses import dataclass
3+
from typing import Dict
4+
5+
from mashumaro import DataClassDictMixin
6+
from mashumaro.config import BaseConfig
7+
8+
logger = logging.getLogger(__name__)
9+
10+
11+
@dataclass
12+
class AqMetadata(DataClassDictMixin):
13+
"""
14+
Deserialised metadata that is set either on an Openstack image
15+
or a VM's metadata
16+
"""
17+
18+
aq_archetype: str
19+
# Aq domain can hold either a domain or sandbox reference
20+
aq_domain: str
21+
22+
aq_personality: str
23+
aq_os_version: str
24+
aq_os: str
25+
26+
# pylint: disable=too-few-public-methods
27+
class Config(BaseConfig):
28+
"""
29+
Sets the aliases for the metadata keys
30+
"""
31+
32+
aliases = {
33+
"aq_archetype": "AQ_ARCHETYPE",
34+
"aq_domain": "AQ_DOMAIN",
35+
"aq_personality": "AQ_PERSONALITY",
36+
"aq_os_version": "AQ_OSVERSION",
37+
"aq_os": "AQ_OS",
38+
}
39+
40+
def override_from_vm_meta(self, vm_meta: Dict[str, str]):
41+
"""
42+
Overrides the values in the metadata with the values from the VM's
43+
metadata
44+
"""
45+
for attr, alias in self.Config.aliases.items():
46+
if alias in vm_meta:
47+
setattr(self, attr, vm_meta[alias])
48+
49+
if "AQ_SANDBOX" in vm_meta:
50+
self.aq_domain = vm_meta["AQ_SANDBOX"]

OpenStack-Rabbit-Consumer/rabbit_consumer/image_metadata.py

Lines changed: 0 additions & 20 deletions
This file was deleted.

OpenStack-Rabbit-Consumer/rabbit_consumer/message_consumer.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from rabbit_consumer import openstack_api
1010
from rabbit_consumer.aq_api import verify_kerberos_ticket
1111
from rabbit_consumer.consumer_config import ConsumerConfig
12-
from rabbit_consumer.image_metadata import ImageMetadata
12+
from rabbit_consumer.aq_metadata import AqMetadata
1313
from rabbit_consumer.openstack_address import OpenstackAddress
1414
from rabbit_consumer.rabbit_message import RabbitMessage, MessageEventType
1515
from rabbit_consumer.vm_data import VmData
@@ -21,17 +21,28 @@
2121
}
2222

2323

24-
def is_aq_managed_image(rabbit_message: RabbitMessage) -> Optional[ImageMetadata]:
24+
def is_aq_managed_image(vm_data: VmData) -> bool:
2525
"""
2626
Check to see if the metadata in the message contains entries that suggest it
2727
is for an Aquilon VM.
2828
"""
29-
image = openstack_api.get_image(VmData.from_message(rabbit_message))
29+
image = openstack_api.get_image(vm_data)
3030
if "AQ_OS" not in image.metadata:
3131
logger.debug("Skipping non-Aquilon image: %s", image.name)
32-
return None
32+
return False
33+
return True
34+
35+
36+
def get_aq_build_metadata(vm_data: VmData) -> AqMetadata:
37+
"""
38+
Gets the Aq Metadata from either the image or VM (where
39+
VM metadata takes precedence) to determine the AQ params
40+
"""
41+
image = openstack_api.get_image(vm_data)
42+
image_meta = AqMetadata.from_dict(image.metadata)
3343

34-
image_meta = ImageMetadata.from_dict(image.metadata)
44+
vm_metadata = openstack_api.get_server_metadata(vm_data)
45+
image_meta.override_from_vm_meta(vm_metadata)
3546
return image_meta
3647

3748

@@ -111,7 +122,7 @@ def check_machine_valid(rabbit_message: RabbitMessage) -> bool:
111122
)
112123
return False
113124

114-
if not is_aq_managed_image(rabbit_message):
125+
if not is_aq_managed_image(vm_data):
115126
logger.debug("Ignoring non AQ Image: %s", rabbit_message)
116127
return False
117128

@@ -131,7 +142,7 @@ def handle_create_machine(rabbit_message: RabbitMessage) -> None:
131142

132143
vm_data = VmData.from_message(rabbit_message)
133144

134-
image_meta = is_aq_managed_image(rabbit_message)
145+
image_meta = get_aq_build_metadata(vm_data)
135146
network_details = openstack_api.get_server_networks(vm_data)
136147

137148
if not network_details or not network_details[0].hostname:

OpenStack-Rabbit-Consumer/rabbit_consumer/openstack_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def get_server_networks(vm_data: VmData) -> List[OpenstackAddress]:
7171
return OpenstackAddress.get_internal_networks(server.addresses)
7272

7373

74-
def get_metadata(vm_data: VmData) -> dict:
74+
def get_server_metadata(vm_data: VmData) -> dict:
7575
"""
7676
Gets the metadata from Openstack for the virtual machine.
7777
"""

OpenStack-Rabbit-Consumer/test/fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import pytest
44

5-
from rabbit_consumer.image_metadata import ImageMetadata
5+
from rabbit_consumer.aq_metadata import AqMetadata
66
from rabbit_consumer.openstack_address import OpenstackAddress
77
from rabbit_consumer.rabbit_message import RabbitMessage, RabbitMeta, RabbitPayload
88
from rabbit_consumer.vm_data import VmData
@@ -14,7 +14,7 @@ def fixture_image_metadata():
1414
Creates an ImageMetadata object with mock data
1515
which represent an example OpenStack image
1616
"""
17-
return ImageMetadata(
17+
return AqMetadata(
1818
aq_archetype="archetype_mock",
1919
aq_domain="domain_mock",
2020
aq_personality="personality_mock",
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from typing import Dict
2+
3+
import pytest
4+
5+
from rabbit_consumer.aq_metadata import AqMetadata
6+
7+
8+
@pytest.fixture(name="image_metadata")
9+
def fixture_image_metadata() -> Dict[str, str]:
10+
"""
11+
Creates a dictionary with mock data
12+
which represents an example OpenStack image's metadata
13+
"""
14+
return {
15+
"AQ_ARCHETYPE": "archetype_mock",
16+
"AQ_DOMAIN": "domain_mock",
17+
"AQ_PERSONALITY": "personality_mock",
18+
"AQ_OS": "os_mock",
19+
"AQ_OSVERSION": "osversion_mock",
20+
}
21+
22+
23+
def test_aq_metadata_from_initial_dict(image_metadata):
24+
"""
25+
Tests creating an AQ metadata object from an initial dictionary
26+
"""
27+
returned = AqMetadata.from_dict(image_metadata)
28+
29+
assert returned.aq_archetype == "archetype_mock"
30+
assert returned.aq_domain == "domain_mock"
31+
assert returned.aq_personality == "personality_mock"
32+
assert returned.aq_os == "os_mock"
33+
assert returned.aq_os_version == "osversion_mock"
34+
35+
36+
def test_aq_metadata_override_all(image_metadata):
37+
"""
38+
Tests overriding all values in an AQ metadata object
39+
"""
40+
returned = AqMetadata.from_dict(image_metadata)
41+
returned.override_from_vm_meta(
42+
{
43+
"AQ_ARCHETYPE": "archetype_mock_override",
44+
"AQ_DOMAIN": "domain_mock_override",
45+
"AQ_PERSONALITY": "personality_mock_override",
46+
}
47+
)
48+
49+
assert returned.aq_archetype == "archetype_mock_override"
50+
assert returned.aq_domain == "domain_mock_override"
51+
assert returned.aq_personality == "personality_mock_override"
52+
53+
# Check the original values are still there
54+
assert returned.aq_os == "os_mock"
55+
assert returned.aq_os_version == "osversion_mock"
56+
57+
58+
def test_aq_metadata_override_sandbox(image_metadata):
59+
"""
60+
Tests overriding the sandbox value in an AQ metadata object
61+
maps correctly onto the domain value
62+
"""
63+
returned = AqMetadata.from_dict(image_metadata)
64+
returned.override_from_vm_meta(
65+
{
66+
"AQ_SANDBOX": "sandbox_mock",
67+
}
68+
)
69+
# This should be the only value that has changed
70+
assert returned.aq_domain == "sandbox_mock"
71+
72+
assert returned.aq_archetype == "archetype_mock"
73+
assert returned.aq_personality == "personality_mock"
74+
assert returned.aq_os == "os_mock"
75+
assert returned.aq_os_version == "osversion_mock"

OpenStack-Rabbit-Consumer/test/test_message_consumer.py

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from unittest.mock import Mock, NonCallableMock, patch, call
1+
from unittest.mock import Mock, NonCallableMock, patch, call, MagicMock
22

33
import pytest
44

@@ -21,6 +21,7 @@
2121
SUPPORTED_MESSAGE_TYPES,
2222
check_machine_valid,
2323
is_aq_managed_image,
24+
get_aq_build_metadata,
2425
)
2526
from rabbit_consumer.vm_data import VmData
2627

@@ -217,11 +218,13 @@ def test_consume_create_machine_hostnames_good_path(
217218
with (
218219
patch("rabbit_consumer.message_consumer.VmData") as data_patch,
219220
patch("rabbit_consumer.message_consumer.check_machine_valid") as check_machine,
220-
patch("rabbit_consumer.message_consumer.is_aq_managed_image") as is_managed,
221+
patch(
222+
"rabbit_consumer.message_consumer.get_aq_build_metadata"
223+
) as get_image_meta,
221224
patch("rabbit_consumer.message_consumer.delete_machine") as delete_machine,
222225
):
223226
check_machine.return_value = True
224-
is_managed.return_value = image_metadata
227+
get_image_meta.return_value = image_metadata
225228

226229
handle_create_machine(rabbit_message)
227230

@@ -272,13 +275,14 @@ def test_check_machine_valid(openstack_api, is_aq_managed):
272275
"""
273276
mock_message = NonCallableMock()
274277
is_aq_managed.return_value = True
278+
279+
vm_data = VmData.from_message(mock_message)
280+
275281
openstack_api.check_machine_exists.return_value = True
276282

277283
assert check_machine_valid(mock_message)
278-
is_aq_managed.assert_called_once_with(mock_message)
279-
openstack_api.check_machine_exists.assert_called_once_with(
280-
VmData.from_message(mock_message)
281-
)
284+
is_aq_managed.assert_called_once_with(vm_data)
285+
openstack_api.check_machine_exists.assert_called_once_with(vm_data)
282286

283287

284288
@patch("rabbit_consumer.message_consumer.is_aq_managed_image")
@@ -290,13 +294,12 @@ def test_check_machine_invalid_image(openstack_api, is_aq_managed):
290294
mock_message = NonCallableMock()
291295
is_aq_managed.return_value = False
292296
openstack_api.check_machine_exists.return_value = True
297+
vm_data = VmData.from_message(mock_message)
293298

294299
assert not check_machine_valid(mock_message)
295300

296-
openstack_api.check_machine_exists.assert_called_once_with(
297-
VmData.from_message(mock_message)
298-
)
299-
is_aq_managed.assert_called_once_with(mock_message)
301+
openstack_api.check_machine_exists.assert_called_once_with(vm_data)
302+
is_aq_managed.assert_called_once_with(vm_data)
300303

301304

302305
@patch("rabbit_consumer.message_consumer.is_aq_managed_image")
@@ -316,30 +319,45 @@ def test_check_machine_invalid_machine(openstack_api, is_aq_managed):
316319
)
317320

318321

319-
@patch("rabbit_consumer.message_consumer.VmData")
320-
@patch("rabbit_consumer.message_consumer.ImageMetadata")
321322
@patch("rabbit_consumer.message_consumer.openstack_api")
322-
def test_is_aq_managed_image(openstack_api, image_meta, vm_data):
323+
def test_is_aq_managed_image(openstack_api, vm_data):
323324
"""
324325
Test that the function returns True when the image is AQ managed
325326
"""
326-
mock_message = NonCallableMock()
327327
openstack_api.get_image.return_value.metadata = {"AQ_OS": "True"}
328328

329-
assert is_aq_managed_image(mock_message) == image_meta.from_dict.return_value
330-
openstack_api.get_image.assert_called_once_with(vm_data.from_message.return_value)
329+
assert is_aq_managed_image(vm_data)
330+
openstack_api.get_image.assert_called_once_with(vm_data)
331331

332332

333333
@patch("rabbit_consumer.message_consumer.VmData")
334-
@patch("rabbit_consumer.message_consumer.ImageMetadata")
335334
@patch("rabbit_consumer.message_consumer.openstack_api")
336-
def test_is_aq_managed_image_missing_key(openstack_api, image_meta, vm_data):
335+
def test_is_aq_managed_image_missing_key(openstack_api, vm_data):
337336
"""
338337
Test that the function returns False when the image is not AQ managed
339338
"""
340-
mock_message = NonCallableMock()
341339
openstack_api.get_image.return_value.metadata = {}
342340

343-
assert not is_aq_managed_image(mock_message)
344-
openstack_api.get_image.assert_called_once_with(vm_data.from_message.return_value)
345-
image_meta.from_dict.assert_not_called()
341+
assert not is_aq_managed_image(vm_data)
342+
openstack_api.get_image.assert_called_once_with(vm_data)
343+
344+
345+
@patch("rabbit_consumer.message_consumer.AqMetadata")
346+
@patch("rabbit_consumer.message_consumer.openstack_api")
347+
def test_get_aq_build_metadata(openstack_api, aq_metadata_class, vm_data):
348+
"""
349+
Test that the function returns the correct metadata
350+
"""
351+
aq_metadata_obj: MagicMock = get_aq_build_metadata(vm_data)
352+
353+
# We should first construct from an image
354+
assert aq_metadata_obj == aq_metadata_class.from_dict.return_value
355+
aq_metadata_class.from_dict.assert_called_once_with(
356+
openstack_api.get_image.return_value.metadata
357+
)
358+
359+
# Then override with an object
360+
openstack_api.get_server_metadata.assert_called_once_with(vm_data)
361+
aq_metadata_obj.override_from_vm_meta.assert_called_once_with(
362+
openstack_api.get_server_metadata.return_value
363+
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.2.1
1+
2.3.0

0 commit comments

Comments
 (0)