Skip to content

Commit 048e9d2

Browse files
committed
Add vm size update action
1 parent 49d4311 commit 048e9d2

12 files changed

Lines changed: 217 additions & 96 deletions

File tree

CHANGELOG.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ releases:
2323
* Update start action for fill in `network` in vm runtime properties
2424
* Cluster Example: Automatically add libvirt host to trusted
2525
* Support for reuse external vm
26+
* move use_external_resource to top of instance properties
27+
* add `update` action for sync vm size to values from runtime properties.
28+
* rename `memory_size` to `memory_maxsize`

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ Description for VM
4141
**Supported properties:**
4242
* `libvirt_auth`: connection url, by default: `qemu:///system`
4343
* `backup_dir`: directory for save backups, by default: `./`
44+
* `use_external_resource`: (optional) Use external object. The default is
45+
`false`.
46+
* `resource_id`: (optional) Used to identify the object when
47+
`use_external_resource` is true.
4448
* `params`: params used for create object, useful for embeded template.
45-
* `use_external_resource`: (optional) Use external object. The default is
46-
`false`.
47-
* `resource_id`: (optional) Used to identify the object when
48-
`use_external_resource` is true.
4949
* `vcpu`: CPU count
50-
* `memory_minsize`: (optional) recomended VM memory size in KiB for
51-
downgrade.
5250
* `memory_size`: VM memory size in KiB
51+
* `memory_maxsize`: (optional) recomended VM memory size in KiB for
52+
downgrade. The default is value from `memory_size` * 2.
5353
* `nvram`: (optional) path to nvram (useful for arm)
5454
* `disks`: list connected disks
5555
* `networks`: list connected networks
@@ -75,11 +75,11 @@ Description for Network
7575
**Supported properties:**
7676
* `libvirt_auth`: connection url, by default: `qemu:///system`
7777
* `backup_dir`: directory for save backups, by default: `./`
78+
* `use_external_resource`: (optional) Use external object. The default is
79+
`false`.
80+
* `resource_id`: (optional) Used to identify the object when
81+
`use_external_resource` is true.
7882
* `params`: params used for create object.
79-
* `use_external_resource`: (optional) Use external object. The default is
80-
`false`.
81-
* `resource_id`: (optional) Used to identify the object when
82-
`use_external_resource` is true.
8383
* `dev`: Device name
8484
* `forwards`: settings for network `forwards`.
8585
* `ips`: settings for network `ips`.

cloudify_libvirt/common.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ def get_libvirt_params(**kwargs):
3131
template_params.update(ctx.instance.runtime_properties.get('params', {}))
3232
template_params.update(kwargs.get('params', {}))
3333
ctx.instance.runtime_properties['params'] = template_params
34+
35+
# update 'resource_id', 'use_external_resource' from kwargs
36+
for field in ['resource_id', 'use_external_resource']:
37+
if field in kwargs:
38+
ctx.instance.runtime_properties[field] = kwargs[field]
39+
3440
return libvirt_auth, template_params
3541

3642

cloudify_libvirt/domain_tasks.py

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,12 @@ def _update_template_params(template_params):
4040

4141
if not template_params.get("resource_id"):
4242
template_params["resource_id"] = ctx.instance.id
43-
if (not template_params.get("memory_minsize") and
43+
if (not template_params.get("memory_maxsize") and
4444
template_params.get('memory_size')):
45-
# if have no minimal memory size, set current as minimum
45+
# if have no maximum memory size, set current as minimum
4646
# and twised memory as maximum
4747
memory_size = int(template_params['memory_size'])
48-
template_params["memory_minsize"] = memory_size
49-
template_params['memory_size'] = memory_size * 2
48+
template_params['memory_maxsize'] = memory_size * 2
5049
if not template_params.get("instance_uuid"):
5150
template_params["instance_uuid"] = str(uuid.uuid4())
5251
if not template_params.get("domain_type"):
@@ -68,10 +67,11 @@ def configure(**kwargs):
6867
template_params = _update_template_params(template_params)
6968

7069
try:
71-
if template_params.get("use_external_resource"):
70+
if ctx.instance.runtime_properties.get("use_external_resource"):
7271
# lookup the default domain by name
7372
try:
74-
dom = conn.lookupByName(template_params["resource_id"])
73+
dom = conn.lookupByName(
74+
ctx.instance.runtime_properties["resource_id"])
7575
except Exception as e:
7676
dom = None
7777
ctx.logger.info("Non critical error: {}".format(str(e)))
@@ -131,6 +131,8 @@ def _update_network_list(dom, lease_only=True):
131131

132132
# get known by libvirt interfaces
133133
virt_networks = dom.interfaceAddresses(request_type)
134+
ctx.logger.info("Libvirt knows about such networks: {}"
135+
.format(repr(virt_networks)))
134136

135137
# networks from instance
136138
if 'params' not in ctx.instance.runtime_properties:
@@ -179,7 +181,7 @@ def reboot(**kwargs):
179181
resource_id = ctx.instance.runtime_properties.get('resource_id')
180182

181183
if not resource_id:
182-
ctx.logger.info("No servers for start")
184+
ctx.logger.info("No servers for reboot")
183185
return
184186

185187
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
@@ -209,6 +211,72 @@ def reboot(**kwargs):
209211
conn.close()
210212

211213

214+
@operation
215+
def update(**kwargs):
216+
ctx.logger.info("set vcpu/memory values")
217+
218+
resource_id = ctx.instance.runtime_properties.get('resource_id')
219+
220+
if not resource_id:
221+
ctx.logger.info("No servers for update")
222+
return
223+
224+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
225+
conn = libvirt.open(libvirt_auth)
226+
if conn is None:
227+
raise cfy_exc.NonRecoverableError(
228+
'Failed to open connection to the hypervisor'
229+
)
230+
231+
try:
232+
try:
233+
dom = conn.lookupByName(resource_id)
234+
except Exception as e:
235+
dom = None
236+
ctx.logger.info("Non critical error: {}".format(str(e)))
237+
238+
if dom is None:
239+
raise cfy_exc.NonRecoverableError(
240+
'Failed to find the domain'
241+
)
242+
243+
# change memory values
244+
if template_params.get('memory_size'):
245+
ctx.logger.info("Set memory to {}"
246+
.format(repr(template_params['memory_size'])))
247+
if dom.setMemory(template_params['memory_size']) < 0:
248+
raise cfy_exc.NonRecoverableError(
249+
"Can not change memory amount."
250+
)
251+
252+
state, _ = dom.state()
253+
if state == libvirt.VIR_DOMAIN_RUNNING:
254+
ctx.logger.info("CPU/Maximum memory size count should be changed "
255+
"on stopped vm.")
256+
return
257+
258+
# change vcpu values
259+
if template_params.get('vcpu'):
260+
ctx.logger.info("Set cpu count to {}"
261+
.format(repr(template_params['vcpu'])))
262+
if dom.setVcpus(template_params['vcpu']) < 0:
263+
raise cfy_exc.NonRecoverableError(
264+
"Can not change cpu count."
265+
)
266+
267+
# change max memory values
268+
if template_params.get('memory_maxsize'):
269+
ctx.logger.info("Set max memory to {}"
270+
.format(repr(template_params['memory_maxsize'])))
271+
if dom.setMaxMemory(template_params['memory_maxsize']) < 0:
272+
raise cfy_exc.NonRecoverableError(
273+
"Can not change max memory amount."
274+
)
275+
276+
finally:
277+
conn.close()
278+
279+
212280
@operation
213281
def start(**kwargs):
214282
ctx.logger.info("start")

cloudify_libvirt/network_tasks.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ def create(**kwargs):
5454
template_params = _update_template_params(template_params)
5555

5656
try:
57-
if template_params.get("use_external_resource"):
57+
if ctx.instance.runtime_properties.get("use_external_resource"):
5858
# lookup the default network by name
59-
network = conn.networkLookupByName(template_params["resource_id"])
59+
network = conn.networkLookupByName(
60+
ctx.instance.runtime_properties["resource_id"])
6061
if network is None:
6162
raise cfy_exc.NonRecoverableError(
6263
'Failed to find the network'

cloudify_libvirt/templates/domain.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<partition>/machine</partition>
44
</resource>
55
<uuid>{{ instance_uuid }}</uuid>
6-
<currentMemory unit="KiB">{{ memory_minsize }}</currentMemory>
6+
<currentMemory unit="KiB">{{ memory_size }}</currentMemory>
77
<on_poweroff>destroy</on_poweroff>
88
<devices>
99
<console type="pty" tty="/dev/pts/1">
@@ -56,7 +56,7 @@
5656
<acpi/>
5757
<apic/>
5858
</features>
59-
<memory unit="KiB">{{ memory_size }}</memory>
59+
<memory unit="KiB">{{ memory_maxsize }}</memory>
6060
<os>
6161
<type machine="pc" arch="x86_64">hvm</type>
6262
<boot dev="hd"/>

cloudify_libvirt/tests/test_domain.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def _create_ctx(self):
3535
'libvirt_auth': {'a': 'c'}
3636
},
3737
runtime_properties=DirtyTrackingDict({
38-
'params': {'c': "d", 'e': 'g', 'memory_size': 1024},
38+
'params': {'c': "d", 'e': 'g'},
3939
'libvirt_auth': {'a': 'd'}
4040
})
4141
)
@@ -47,10 +47,11 @@ def test_reuse_domain_create_not_exist(self):
4747
_ctx = self._create_ctx()
4848
self._check_no_such_object_domain(
4949
"cloudify_libvirt.domain_tasks.libvirt.open",
50-
domain_tasks.configure, [], {'ctx': _ctx, 'params': {
50+
domain_tasks.configure, [], {
51+
'ctx': _ctx,
5152
"resource_id": 'resource',
52-
"use_external_resource": True,
53-
}}, 'resource')
53+
"use_external_resource": True
54+
}, 'resource')
5455

5556
def test_reuse_domain_create_exist(self):
5657
# check that we can use domain
@@ -65,9 +66,8 @@ def test_reuse_domain_create_exist(self):
6566
"cloudify_libvirt.domain_tasks.libvirt.open",
6667
mock.Mock(return_value=connect)
6768
):
68-
domain_tasks.configure(ctx=_ctx, params={
69-
"resource_id": 'resource',
70-
"use_external_resource": True})
69+
domain_tasks.configure(ctx=_ctx, resource_id='resource',
70+
use_external_resource=True)
7171
connect.lookupByName.assert_called_with('resource')
7272
self.assertEqual(
7373
_ctx.instance.runtime_properties['resource_id'], 'resource'
@@ -86,7 +86,7 @@ def test_create(self):
8686
self.assertEqual(_ctx.instance.runtime_properties, {
8787
'libvirt_auth': {'w': 'x'},
8888
'params': {
89-
'a': 'b', 'c': 'd', 'e': 'g', 'z': 'y', 'memory_size': 1024
89+
'a': 'b', 'c': 'd', 'e': 'g', 'z': 'y'
9090
}
9191
})
9292

@@ -114,17 +114,61 @@ def test_configure(self):
114114

115115
connect = self._create_fake_connection()
116116
connect.defineXML = mock.Mock(return_value=domain)
117+
118+
# without params
117119
_ctx.instance.runtime_properties['params'] = {}
118120
_ctx.node.properties['params'] = {}
119121
with mock.patch(
120122
"cloudify_libvirt.domain_tasks.libvirt.open",
121123
mock.Mock(return_value=connect)
122124
):
123-
domain_tasks.configure(ctx=_ctx, domain_file="domain_file")
125+
domain_tasks.configure(ctx=_ctx,
126+
domain_file="domain_file")
124127
connect.defineXML.assert_called_with('<somexml/>')
125128
self.assertEqual(
126129
_ctx.instance.runtime_properties['resource_id'], "domain_name"
127130
)
131+
# with params from inputs
132+
_ctx.instance.runtime_properties['params'] = {}
133+
_ctx.node.properties['params'] = {}
134+
with mock.patch(
135+
"cloudify_libvirt.domain_tasks.libvirt.open",
136+
mock.Mock(return_value=connect)
137+
):
138+
domain_tasks.configure(ctx=_ctx,
139+
domain_file="domain_file",
140+
params={"memory_size": 1024})
141+
connect.defineXML.assert_called_with('<somexml/>')
142+
self.assertEqual(
143+
_ctx.instance.runtime_properties['params']['memory_maxsize'],
144+
2048)
145+
self.assertEqual(
146+
_ctx.instance.runtime_properties['params']['memory_size'],
147+
1024)
148+
149+
150+
def test_update(self):
151+
self._test_no_resource_id(domain_tasks.update)
152+
self._test_check_correct_connect_action(domain_tasks.update)
153+
self._test_check_correct_connect_no_object(domain_tasks.update)
154+
# check memory
155+
self._test_action_states(
156+
domain_tasks.update,
157+
[libvirt.VIR_DOMAIN_RUNNING],
158+
'Can not change memory amount.',
159+
params_update={'memory_size': 1024})
160+
# check max memory
161+
self._test_action_states(
162+
domain_tasks.update,
163+
[libvirt.VIR_DOMAIN_RUNNING_UNKNOWN],
164+
'Can not change max memory amount.',
165+
params_update={'memory_maxsize': 1024})
166+
# check cpu
167+
self._test_action_states(
168+
domain_tasks.update,
169+
[libvirt.VIR_DOMAIN_RUNNING_UNKNOWN],
170+
'Can not change cpu count.',
171+
params_update={'vcpu': 1024})
128172

129173
def test_reboot(self):
130174
self._test_no_resource_id(domain_tasks.reboot)
@@ -378,10 +422,12 @@ def test_update_network_list(self):
378422
)
379423

380424
def _test_action_states(self, func, states, error_text,
381-
error_check_ip=None):
425+
error_check_ip=None, params_update=None):
382426
_ctx = self._create_ctx()
383427
_ctx.instance.runtime_properties['resource_id'] = 'check'
384428
_ctx.instance.runtime_properties['params']['wait_for_ip'] = True
429+
if params_update:
430+
_ctx.instance.runtime_properties['params'].update(params_update)
385431
fake_states = [state for state in states]
386432

387433
def _fake_state():
@@ -401,6 +447,9 @@ def _fake_state():
401447
domain.resume = mock.Mock(return_value=-1)
402448
domain.suspend = mock.Mock(return_value=-1)
403449
domain.reboot = mock.Mock(return_value=-1)
450+
domain.setMemory = mock.Mock(return_value=-1)
451+
domain.setMaxMemory = mock.Mock(return_value=-1)
452+
domain.setVcpus = mock.Mock(return_value=-1)
404453

405454
with mock.patch(
406455
"cloudify_libvirt.domain_tasks.libvirt.open",
@@ -419,6 +468,9 @@ def _fake_state():
419468
domain.resume = mock.Mock(return_value=0)
420469
domain.suspend = mock.Mock(return_value=0)
421470
domain.reboot = mock.Mock(return_value=0)
471+
domain.setMemory = mock.Mock(return_value=0)
472+
domain.setMaxMemory = mock.Mock(return_value=0)
473+
domain.setVcpus = mock.Mock(return_value=0)
422474
fake_states = [state for state in states]
423475

424476
def _fake_state():

cloudify_libvirt/tests/test_network.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,11 @@ def test_reuse_network_create_not_exist(self):
171171
_ctx = self._create_ctx()
172172
self._check_no_such_object_network(
173173
"cloudify_libvirt.network_tasks.libvirt.open",
174-
network_tasks.create, [], {'ctx': _ctx, 'params': {
174+
network_tasks.create, [], {
175+
'ctx': _ctx,
175176
"resource_id": 'resource',
176177
"use_external_resource": True,
177-
}}, 'resource')
178+
}, 'resource')
178179

179180
def test_reuse_network_create_exist(self):
180181
# check that we can use network
@@ -189,9 +190,9 @@ def test_reuse_network_create_exist(self):
189190
"cloudify_libvirt.domain_tasks.libvirt.open",
190191
mock.Mock(return_value=connect)
191192
):
192-
network_tasks.create(ctx=_ctx, params={
193-
"resource_id": 'resource',
194-
"use_external_resource": True})
193+
network_tasks.create(ctx=_ctx,
194+
resource_id='resource',
195+
use_external_resource=True)
195196
connect.networkLookupByName.assert_called_with('resource')
196197
self.assertEqual(
197198
_ctx.instance.runtime_properties['resource_id'], 'resource'

examples/templates/domain-arm.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<partition>/machine</partition>
44
</resource>
55
<uuid>{{ instance_uuid }}</uuid>
6-
<currentMemory unit="KiB">{{ memory_minsize }}</currentMemory>
6+
<currentMemory unit="KiB">{{ memory_size }}</currentMemory>
77
<on_poweroff>destroy</on_poweroff>
88
<devices>
99
<emulator>/usr/bin/qemu-system-aarch64</emulator>
@@ -66,7 +66,7 @@
6666
<features>
6767
<gic version='2'/>
6868
</features>
69-
<memory unit="KiB">{{ memory_size }}</memory>
69+
<memory unit="KiB">{{ memory_maxsize }}</memory>
7070
<os>
7171
<type arch='aarch64' machine='virt'>hvm</type>
7272
<loader readonly='yes' type='pflash'>/usr/share/AAVMF/AAVMF_CODE.fd</loader>

0 commit comments

Comments
 (0)