Skip to content

Commit b840997

Browse files
Auto scaling find instance (#158)
Auto scaling find instance Reviewed-by: None <None> Reviewed-by: Tino Schr <None> Reviewed-by: Irina Pereiaslavskaia <None>
1 parent 51d2f76 commit b840997

File tree

4 files changed

+335
-0
lines changed

4 files changed

+335
-0
lines changed

otcextensions/sdk/auto_scaling/v1/_proxy.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,26 @@ def instances(self, group, **query):
379379
base_path='/scaling_group_instance/{id}/list'.format(id=group.id),
380380
**query)
381381

382+
def find_instance(self, name_or_id, group, ignore_missing=True):
383+
"""Find a single instance
384+
385+
:param name_or_id: The name or ID of a instance.
386+
:param group: ID of a group
387+
:param bool ignore_missing: When set to ``False``
388+
:class:`~openstack.exceptions.ResourceNotFound` will be
389+
raised when the resource does not exist.
390+
When set to ``True``, None will be returned when
391+
attempting to find a nonexistent resource.
392+
393+
:returns:
394+
One :class:`~otcextensions.sdk.auto_scaling.v1.instance.Instance`
395+
or None.
396+
"""
397+
group = self._get_resource(_group.Group, group)
398+
return self._find(_instance.Instance, name_or_id,
399+
ignore_missing=ignore_missing,
400+
group_id=group.id)
401+
382402
def remove_instance(self, instance, delete_instance=False,
383403
ignore_missing=True):
384404
"""Remove an instance of auto scaling group

otcextensions/sdk/auto_scaling/v1/instance.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ class Instance(_base.Resource):
3232
allow_delete = True
3333

3434
_query_mapping = resource.QueryParameters(
35+
'id', 'name',
3536
'health_status', 'limit',
37+
scaling_group_id='group_id',
3638
lifecycle_status='life_cycle_state',
3739
marker=query_marker_key
3840
)
@@ -59,6 +61,49 @@ class Instance(_base.Resource):
5961
#: AutoScaling instance create time
6062
create_time = resource.Body('create_time')
6163

64+
@classmethod
65+
def find(cls, session, name_or_id, ignore_missing=True, **params):
66+
"""Find a resource by its name or id.
67+
68+
This method was added in order to be able to get an object of the
69+
current class if it exists. This object will be used in
70+
some other proxy methods.
71+
72+
:param session: The session to use for making this request.
73+
:type session: :class:`~keystoneauth1.adapter.Adapter`
74+
:param name_or_id: This resource's identifier, if needed by
75+
the request. The default is ``None``.
76+
:param bool ignore_missing: When set to ``False``
77+
:class:`~openstack.exceptions.ResourceNotFound` will be
78+
raised when the resource does not exist.
79+
When set to ``True``, None will be returned when
80+
attempting to find a nonexistent resource.
81+
:param dict params: Any additional parameters to be passed into
82+
underlying methods, such as to
83+
:meth:`~openstack.resource.Resource.existing`
84+
in order to pass on URI parameters.
85+
86+
:return: The :class:`Resource` object matching the given name or id
87+
or None if nothing matches.
88+
:raises: :class:`openstack.exceptions.DuplicateResource` if more
89+
than one resource is found for this request.
90+
:raises: :class:`openstack.exceptions.ResourceNotFound` if nothing
91+
is found and ignore_missing is ``False``.
92+
"""
93+
session = cls._get_session(session)
94+
group_id = params.pop('group_id', None)
95+
96+
base_path = '/scaling_group_instance/{id}/list'.format(id=group_id)
97+
data = cls.list(session, base_path=base_path, **params)
98+
result = cls._get_one_match(name_or_id, data)
99+
if result is not None:
100+
return result
101+
102+
if ignore_missing:
103+
return None
104+
raise exceptions.ResourceNotFound(
105+
"No %s found for %s" % (cls.__name__, name_or_id))
106+
62107
def remove(self, session, delete_instance=False, ignore_missing=True):
63108
"""Remove an instance of auto scaling group
64109
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
2+
# not use this file except in compliance with the License. You may obtain
3+
# a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10+
# License for the specific language governing permissions and limitations
11+
# under the License.
12+
import uuid
13+
import os
14+
15+
import fixtures
16+
import openstack
17+
18+
from openstack import utils
19+
from openstack import _log
20+
21+
from otcextensions.tests.functional import base
22+
23+
_logger = _log.setup_logging('openstack')
24+
25+
26+
class TestInstance(base.BaseFunctionalTest):
27+
UUID = uuid.uuid4().hex[:9]
28+
NETWORK_NAME = "test-network-" + UUID
29+
SUBNET_NAME = "test-subnet-" + UUID
30+
ROUTER_NAME = "test-router-" + UUID
31+
AS_GROUP_NAME = "test-as-group-" + UUID
32+
AS_CONFIG_NAME = "test-as-config-" + UUID
33+
SG_NAME = "test-sec-group-" + UUID
34+
KP_NAME = "test-kp-" + UUID
35+
IP_VERSION = 4
36+
CIDR = "192.168.0.0/16"
37+
DESIRE_INSTANCE_NUMBER = 1
38+
MIN_INSTANCE_NUMBER = 0
39+
MAX_INSTANCE_NUMBER = 1
40+
FLAVOR = "s3.medium.1"
41+
IMAGE_NAME = "Standard_Ubuntu_18.04_latest"
42+
DISK_SIZE = 4
43+
DISK_VOL_TYPE = "SATA"
44+
DISK_TYPE = "SYS"
45+
46+
def _create_keypair(self):
47+
return self.conn.compute.create_keypair(
48+
name=self.KP_NAME
49+
)
50+
51+
def _delete_keypair(self, key_pair):
52+
return self.conn.compute.delete_keypair(
53+
keypair=key_pair
54+
)
55+
56+
def _create_sec_group(self):
57+
return self.conn.network.create_security_group(
58+
name=self.SG_NAME
59+
)
60+
61+
def _delete_sec_group(self, sec_group):
62+
return self.conn.network.delete_security_group(
63+
security_group=sec_group
64+
)
65+
66+
def _create_network(self):
67+
return self.conn.network.create_network(
68+
name=self.NETWORK_NAME
69+
)
70+
71+
def _delete_network(self, network):
72+
return self.conn.network.delete_network(
73+
network=network
74+
)
75+
76+
def _create_subnet(self, network_id):
77+
return self.conn.network.create_subnet(
78+
name=self.SUBNET_NAME,
79+
network_id=network_id,
80+
ip_version=self.IP_VERSION,
81+
cidr=self.CIDR
82+
)
83+
84+
def _delete_subnet(self, subnet):
85+
return self.conn.network.delete_subnet(
86+
subnet=subnet
87+
)
88+
89+
def _create_router(self, subnet_id):
90+
rtr = self.conn.network.create_router(
91+
name=self.ROUTER_NAME
92+
)
93+
return self.conn.network.add_interface_to_router(
94+
router=rtr,
95+
subnet_id=subnet_id
96+
)
97+
98+
def _delete_router(self, router, subnet_id):
99+
self.conn.network.remove_interface_from_router(
100+
router=router,
101+
subnet_id=subnet_id
102+
)
103+
return self.conn.network.delete_router(
104+
router=router
105+
)
106+
107+
def _get_image_id(self):
108+
image = self.conn.compute.find_image(
109+
name_or_id=self.IMAGE_NAME
110+
)
111+
if image:
112+
return image.id
113+
114+
def _create_as_config(self, image_id, sec_group_id):
115+
config_attrs = {
116+
"name": self.AS_CONFIG_NAME,
117+
"instance_config": {
118+
"flavorRef": self.FLAVOR,
119+
"imageRef": image_id,
120+
"disk": [{
121+
'size': self.DISK_SIZE,
122+
'volume_type': self.DISK_VOL_TYPE,
123+
'disk_type': self.DISK_TYPE
124+
}],
125+
"key_name": self.KP_NAME,
126+
"security_groups": [{
127+
"id": sec_group_id
128+
}]
129+
}
130+
}
131+
return self.conn.auto_scaling.create_config(**config_attrs)
132+
133+
def _delete_as_config(self, as_config):
134+
return self.conn.auto_scaling.delete_config(
135+
config=as_config
136+
)
137+
138+
def _create_as_group(self, as_config_id, router_id, network_id):
139+
group_attrs = {
140+
"scaling_group_name": self.AS_GROUP_NAME,
141+
"scaling_configuration_id": as_config_id,
142+
"desire_instance_number": self.DESIRE_INSTANCE_NUMBER,
143+
"min_instance_number": self.MIN_INSTANCE_NUMBER,
144+
"max_instance_number": self.MAX_INSTANCE_NUMBER,
145+
"vpc_id": router_id,
146+
"networks": [{
147+
"id": network_id
148+
}]
149+
}
150+
as_group = self.conn.auto_scaling.create_group(**group_attrs)
151+
self.conn.auto_scaling.resume_group(as_group)
152+
return as_group
153+
154+
def _delete_as_group(self, as_group):
155+
self.conn.auto_scaling.pause_group(as_group)
156+
return self.conn.auto_scaling.delete_group(
157+
group=as_group
158+
)
159+
160+
def _wait_for_instance(self, as_group):
161+
timeout = int(os.environ.get('OS_TEST_TIMEOUT'))
162+
for count in utils.iterate_timeout(
163+
timeout=timeout,
164+
message="Timeout waiting for instance"
165+
):
166+
instances = list(self.conn.auto_scaling.instances(
167+
group=as_group
168+
))
169+
if ((len(instances) == self.MAX_INSTANCE_NUMBER)
170+
and (instances[0].lifecycle_state == 'INSERVICE')):
171+
return instances[0]
172+
else:
173+
continue
174+
175+
def _delete_instance(self, instance, as_group):
176+
timeout = int(os.environ.get('OS_TEST_TIMEOUT'))
177+
self.conn.auto_scaling.remove_instance(
178+
instance=instance,
179+
delete_instance=True
180+
)
181+
for count in utils.iterate_timeout(
182+
timeout=timeout,
183+
message="Timeout waiting for deleting instance"
184+
):
185+
instances = list(self.conn.auto_scaling.instances(
186+
group=as_group
187+
))
188+
if len(instances) == 0:
189+
return None
190+
else:
191+
continue
192+
193+
def _initialize_as_group_with_instance(self):
194+
self.key_pair = self._create_keypair()
195+
self.sec_group = self._create_sec_group()
196+
self.network = self._create_network()
197+
self.subnet = self._create_subnet(self.network.id)
198+
self.router = self._create_router(self.subnet.id)
199+
self.as_config = self._create_as_config(
200+
self._get_image_id(), self.sec_group.id
201+
)
202+
self.as_group = self._create_as_group(
203+
self.as_config.id, self.router['id'], self.network.id
204+
)
205+
self.instance = self._wait_for_instance(
206+
self.as_group
207+
)
208+
209+
def _deinitialize_as_group_with_instance(self):
210+
if self.instance:
211+
self._delete_instance(self.instance, self.as_group)
212+
if self.as_group:
213+
self._delete_as_group(self.as_group)
214+
if self.as_config:
215+
self._delete_as_config(self.as_config)
216+
if self.router:
217+
self._delete_router(self.router, self.subnet.id)
218+
if self.subnet:
219+
self._delete_subnet(self.subnet)
220+
if self.network:
221+
self._delete_network(self.network)
222+
if self.sec_group:
223+
self._delete_sec_group(self.sec_group)
224+
if self.key_pair:
225+
self._delete_keypair(self.key_pair)
226+
227+
def setUp(self):
228+
test_timeout = 3 * int(os.environ.get('OS_TEST_TIMEOUT'))
229+
try:
230+
self.useFixture(
231+
fixtures.EnvironmentVariable(
232+
'OS_TEST_TIMEOUT', str(test_timeout)))
233+
except ValueError:
234+
pass
235+
super(TestInstance, self).setUp()
236+
self._initialize_as_group_with_instance()
237+
238+
def tearDown(self):
239+
super(TestInstance, self).tearDown()
240+
try:
241+
self._deinitialize_as_group_with_instance()
242+
except openstack.exceptions.SDKException as e:
243+
_logger.warning('Got exception during clearing resources %s'
244+
% e.message)
245+
246+
def test_find_instance_by_id(self):
247+
result = self.conn.auto_scaling.find_instance(
248+
name_or_id=self.instance.id,
249+
group=self.as_group
250+
)
251+
self.assertIsNotNone(result)
252+
self.assertEqual(self.instance.id, result.id)
253+
254+
def test_find_instance_by_name(self):
255+
result = self.conn.auto_scaling.find_instance(
256+
name_or_id=self.instance.name,
257+
group=self.as_group
258+
)
259+
self.assertIsNotNone(result)
260+
self.assertEqual(self.instance.name, result.name)

otcextensions/tests/unit/sdk/auto_scaling/v1/test_proxy.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,13 @@ def test_delete(self):
320320
'delete_instance': False
321321
}
322322
)
323+
324+
def test_find(self):
325+
self.verify_find(
326+
test_method=self.proxy.find_instance,
327+
resource_type=_instance.Instance,
328+
value=['name_or_id', 'group'],
329+
expected_kwargs={
330+
'group_id': 'group'
331+
}
332+
)

0 commit comments

Comments
 (0)