Skip to content

Commit 0f09653

Browse files
authored
Merge pull request #16 from cloudify-incubator/for_review
Add cloudinit iso creation code
2 parents 864c68e + f36a4d5 commit 0f09653

20 files changed

+1315
-76
lines changed

.circleci/config.yml

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
- checkout
1919
- run:
2020
name: install python/pip/libvirt packages
21-
command: sudo apt-get install -yq python-libvirt libvirt-dev python-dev python-pip python-virtualenv
21+
command: sudo apt-get install -yq python-libvirt libvirt-dev python-dev python-pip
2222
- run:
2323
name: Download pip
2424
command: curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
@@ -28,15 +28,61 @@ jobs:
2828
- run:
2929
name: Install virtualenv
3030
command: pip install --user virtualenv
31+
- run:
32+
name: Init virtualenv
33+
command: virtualenv env
3134
- run:
3235
name: install tox
3336
command: pip install --user tox
3437
- run: /home/circleci/.local/bin/tox -e flake8
3538
- run: /home/circleci/.local/bin/tox -e py27
36-
- run: /home/circleci/.local/bin/tox -e validate
39+
40+
wagon:
41+
docker:
42+
- image: amd64/centos:centos7.5.1804
43+
steps:
44+
- checkout
45+
- run:
46+
name: Install dependencies
47+
command: yum -y install python-devel gcc openssl git libxslt-devel libxml2-devel openldap-devel libffi-devel openssl-devel libvirt-devel libvirt-python
48+
- run:
49+
name: Download pip
50+
command: curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py"
51+
- run:
52+
name: Install pip
53+
command: python get-pip.py
54+
- run:
55+
name: Upgrade pip
56+
command: pip install --upgrade pip==9.0.1
57+
- run:
58+
name: Install virtualenv
59+
command: pip install virtualenv
60+
- run:
61+
name: Init virtualenv
62+
command: virtualenv env
63+
- run:
64+
name: Install wagon
65+
command: pip install wagon==0.3.2
66+
- run:
67+
name: many_linux
68+
command: echo "manylinux1_compatible = False" > "env/bin/_manylinux.py"
69+
- run:
70+
name: make workspace
71+
command: mkdir -p workspace/build
72+
- run:
73+
name: Create wagon
74+
command: source env/bin/activate && wagon create -s . -v -o workspace/build -f -a '--no-cache-dir -c constraints.txt'
75+
- persist_to_workspace:
76+
root: workspace
77+
paths:
78+
- build/*
3779

3880
workflows:
3981
version: 2
4082
tests:
4183
jobs:
4284
- unittests
85+
- wagon:
86+
filters:
87+
branches:
88+
only: /([0-9\.]*\-build|master|dev)/

README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -178,14 +178,6 @@ Notes for use:
178178
cfy secret create libvirt_common_network -u -s "manager_network"
179179
```
180180
181-
## Wagon creation:
182-
183-
Recommended constraints file for CentOs 7.x and RedHat 7.x is:
184-
```
185-
libvirt-python==4.0.0
186-
cloudify-plugins-common==3.3
187-
```
188-
189181
You should to install [libvirt-devel](examples/bootstraps/centos.sh#L2) before create wagon.
190182
191183
## TODO:

cloudify_libvirt/iso9660_tasks.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
########
2+
# Copyright (c) 2016-2018 GigaSpaces Technologies Ltd. All rights reserved
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import libvirt
17+
import pycdlib
18+
import os
19+
import re
20+
from io import BytesIO
21+
22+
from cloudify import ctx
23+
from cloudify.decorators import operation
24+
from cloudify import exceptions as cfy_exc
25+
import cloudify_libvirt.common as common
26+
27+
28+
def _joliet_name(name):
29+
if name[0] == "/":
30+
name = name[1:]
31+
return "/{}".format(name[:64])
32+
33+
34+
def _name_cleanup(name):
35+
return re.sub('[^A-Z0-9_]{1}', r'_', name.upper())
36+
37+
38+
def _iso_name(name):
39+
if name[0] == "/":
40+
name = name[1:]
41+
42+
name_splited = name.split('.')
43+
if len(name_splited[-1]) <= 3 and len(name_splited) > 1:
44+
return "/{}.{};1".format(
45+
_name_cleanup("_".join(name_splited[:-1])[:8]),
46+
_name_cleanup(name_splited[-1]))
47+
else:
48+
return "/{}.;1".format(_name_cleanup(name[:8]))
49+
50+
51+
@operation
52+
def create(**kwargs):
53+
ctx.logger.info("Creating new iso image.")
54+
55+
libvirt_auth, template_params = common.get_libvirt_params(**kwargs)
56+
conn = libvirt.open(libvirt_auth)
57+
if conn is None:
58+
raise cfy_exc.NonRecoverableError(
59+
'Failed to open connection to the hypervisor'
60+
)
61+
62+
try:
63+
# lookup the default volume by name
64+
try:
65+
pool = conn.storagePoolLookupByName(template_params["pool"])
66+
volume = pool.storageVolLookupByName(template_params["volume"])
67+
except libvirt.libvirtError as e:
68+
raise cfy_exc.NonRecoverableError(
69+
'Failed to find the volume: {}'.format(repr(e))
70+
)
71+
72+
iso = pycdlib.PyCdlib()
73+
iso.new(vol_ident='cidata', joliet=3, rock_ridge='1.09')
74+
75+
fstree = template_params.get('files', {})
76+
for name in fstree:
77+
file_bufer = BytesIO()
78+
file_bufer.write(fstree[name].encode())
79+
iso.add_fp(file_bufer, len(fstree[name]),
80+
_iso_name(name), rr_name=name,
81+
joliet_path=_joliet_name(name))
82+
83+
outiso = BytesIO()
84+
iso.write_fp(outiso)
85+
outiso.seek(0, os.SEEK_END)
86+
iso_size = outiso.tell()
87+
iso.close()
88+
89+
ctx.logger.info("ISO size: {}".format(repr(iso_size)))
90+
91+
stream = conn.newStream(0)
92+
volume.upload(stream, 0, iso_size, 0)
93+
outiso.seek(0, os.SEEK_SET)
94+
95+
read_size = iso_size
96+
while read_size > 0:
97+
buffer = outiso.read(read_size)
98+
read_size -= len(buffer)
99+
stream.send(buffer)
100+
stream.finish()
101+
102+
finally:
103+
conn.close()

cloudify_libvirt/pool_tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ def stop(**kwargs):
202202
ctx.logger.info("Looks as not active.")
203203
break
204204

205-
ctx.logger.info("Tring to stop vm {}/10".format(i))
205+
ctx.logger.info("Tring to stop pool {}/10".format(i))
206206
if pool.destroy() < 0:
207207
raise cfy_exc.NonRecoverableError(
208208
'Can not destroy pool.'
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<volume>
2+
<name>{{ name }}</name>
3+
<uuid>{{ instance_uuid }}</uuid>
4+
<allocation unit='MiB'>{{ allocation }}</allocation>
5+
<capacity unit='MiB'>{{ capacity }}</capacity>
6+
<target>
7+
<permissions>
8+
<owner>107</owner>
9+
<group>107</group>
10+
<mode>0744</mode>
11+
<label>virt_image_t</label>
12+
</permissions>
13+
</target>
14+
</volume>

cloudify_libvirt/tests/test_common_base.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,29 @@ def _check_no_such_object_pool(self, libvirt_open, func, args, kwargs,
8080
connect.lookupByName.assert_not_called()
8181
connect.storagePoolLookupByName.assert_called_with(resource_id)
8282

83+
def _check_no_such_object_volume(self, libvirt_open, func, args, kwargs,
84+
resource_id):
85+
# check that we correctly raise exception without such object
86+
# no such
87+
connect = self._create_fake_connection()
88+
fake_pool = mock.Mock()
89+
fake_pool.storageVolLookupByName = mock.Mock(
90+
side_effect=libvirt.libvirtError("storageVolLookupByName"))
91+
connect.storagePoolLookupByName = mock.Mock(return_value=fake_pool)
92+
with mock.patch(
93+
libvirt_open,
94+
mock.Mock(return_value=connect)
95+
):
96+
with self.assertRaisesRegexp(
97+
NonRecoverableError,
98+
'Failed to find the volume'
99+
):
100+
func(*args, **kwargs)
101+
102+
connect.networkLookupByName.assert_not_called()
103+
connect.lookupByName.assert_not_called()
104+
connect.storagePoolLookupByName.assert_called_with('pool_name')
105+
83106
def _check_no_such_object_domain(self, libvirt_open, func, args, kwargs,
84107
resource_id):
85108
# check that we correctly raise exception without such object
@@ -194,3 +217,12 @@ def _test_no_snapshot_name(self, _ctx, libvirt_open, func):
194217
connect.storagePoolLookupByName = mock.Mock()
195218
with mock.patch(libvirt_open, mock.Mock(return_value=connect)):
196219
func(ctx=_ctx)
220+
221+
def _test_check_correct_connect_action(self, libvirt_open, func):
222+
# check correct handle exception with empty connection
223+
# for start/stop/delete
224+
_ctx = self._create_ctx()
225+
_ctx.instance.runtime_properties['resource_id'] = 'resource'
226+
self._check_correct_connect(
227+
libvirt_open,
228+
func, [], {'ctx': _ctx})

cloudify_libvirt/tests/test_domain.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ def test_configure(self):
156156
def test_update(self):
157157
self._test_no_resource_id(domain_tasks.update,
158158
"No servers for update")
159-
self._test_check_correct_connect_action(domain_tasks.update)
159+
self._test_check_correct_connect_action(
160+
"cloudify_libvirt.domain_tasks.libvirt.open",
161+
domain_tasks.update)
160162
self._test_check_correct_connect_no_object(domain_tasks.update)
161163
# check memory
162164
self._test_action_states(
@@ -180,7 +182,9 @@ def test_update(self):
180182
def test_reboot(self):
181183
self._test_no_resource_id(domain_tasks.reboot,
182184
"No servers for reboot")
183-
self._test_check_correct_connect_action(domain_tasks.reboot)
185+
self._test_check_correct_connect_action(
186+
"cloudify_libvirt.domain_tasks.libvirt.open",
187+
domain_tasks.reboot)
184188
self._test_check_correct_connect_no_object(domain_tasks.reboot)
185189
self._test_action_states(
186190
domain_tasks.reboot,
@@ -190,7 +194,9 @@ def test_reboot(self):
190194
def test_start(self):
191195
self._test_no_resource_id(domain_tasks.start,
192196
"No servers for start")
193-
self._test_check_correct_connect_action(domain_tasks.start)
197+
self._test_check_correct_connect_action(
198+
"cloudify_libvirt.domain_tasks.libvirt.open",
199+
domain_tasks.start)
194200
self._test_check_correct_connect_no_object(domain_tasks.start)
195201
self._test_action_states(
196202
domain_tasks.start,
@@ -202,7 +208,9 @@ def test_stop(self):
202208
self._test_reused_object(
203209
"cloudify_libvirt.domain_tasks.libvirt.open",
204210
domain_tasks.stop)
205-
self._test_check_correct_connect_action(domain_tasks.stop)
211+
self._test_check_correct_connect_action(
212+
"cloudify_libvirt.domain_tasks.libvirt.open",
213+
domain_tasks.stop)
206214
self._test_check_correct_connect_no_object(domain_tasks.stop)
207215
self._test_action_states(
208216
domain_tasks.stop,
@@ -212,7 +220,9 @@ def test_stop(self):
212220
def test_resume(self):
213221
self._test_no_resource_id(domain_tasks.resume,
214222
"No servers for resume")
215-
self._test_check_correct_connect_action(domain_tasks.resume)
223+
self._test_check_correct_connect_action(
224+
"cloudify_libvirt.domain_tasks.libvirt.open",
225+
domain_tasks.resume)
216226
self._test_check_correct_connect_no_object(domain_tasks.resume)
217227
self._test_action_states(
218228
domain_tasks.resume,
@@ -222,7 +232,9 @@ def test_resume(self):
222232
def test_suspend(self):
223233
self._test_no_resource_id(domain_tasks.suspend,
224234
"No servers for suspend")
225-
self._test_check_correct_connect_action(domain_tasks.suspend)
235+
self._test_check_correct_connect_action(
236+
"cloudify_libvirt.domain_tasks.libvirt.open",
237+
domain_tasks.suspend)
226238
self._test_check_correct_connect_no_object(domain_tasks.suspend)
227239
self._test_action_states(
228240
domain_tasks.suspend,
@@ -234,7 +246,9 @@ def test_delete(self):
234246
self._test_reused_object(
235247
"cloudify_libvirt.domain_tasks.libvirt.open",
236248
domain_tasks.delete)
237-
self._test_check_correct_connect_action(domain_tasks.delete)
249+
self._test_check_correct_connect_action(
250+
"cloudify_libvirt.domain_tasks.libvirt.open",
251+
domain_tasks.delete)
238252
self._test_check_correct_connect_no_object(domain_tasks.delete)
239253

240254
# delete snapshot with error
@@ -262,7 +276,7 @@ def test_delete(self):
262276
):
263277
with self.assertRaisesRegexp(
264278
RecoverableError,
265-
"Still have several snapshots: \['snapshot!'\]."
279+
"Still have several snapshots: \\['snapshot!'\\]."
266280
):
267281
domain_tasks.delete(ctx=_ctx,
268282
snapshot_name='snapshot_name',
@@ -326,7 +340,9 @@ def _snapshot_list():
326340
def test_perfomance(self):
327341
self._test_no_resource_id(domain_tasks.perfomance,
328342
"No servers for statistics.")
329-
self._test_check_correct_connect_action(domain_tasks.perfomance)
343+
self._test_check_correct_connect_action(
344+
"cloudify_libvirt.domain_tasks.libvirt.open",
345+
domain_tasks.perfomance)
330346
self._test_check_correct_connect_no_object(domain_tasks.perfomance)
331347

332348
# some fake statistics
@@ -532,14 +548,6 @@ def _test_check_correct_connect_no_object(self, func):
532548
"cloudify_libvirt.domain_tasks.libvirt.open", func, [],
533549
{'ctx': _ctx}, 'resource')
534550

535-
def _test_check_correct_connect_action(self, func):
536-
# check correct handle exception with empty connection
537-
_ctx = self._create_ctx()
538-
_ctx.instance.runtime_properties['resource_id'] = 'resource'
539-
self._check_correct_connect(
540-
"cloudify_libvirt.domain_tasks.libvirt.open",
541-
func, [], {'ctx': _ctx})
542-
543551
def _check_create_backups(self, _ctx, connect, domain, snapshot, raw_case):
544552
# raw_case - dump xml without real raw dump
545553
_ctx.instance.runtime_properties['params']['full_dump'] = raw_case
@@ -555,7 +563,7 @@ def _check_create_backups(self, _ctx, connect, domain, snapshot, raw_case):
555563
):
556564
with self.assertRaisesRegexp(
557565
NonRecoverableError,
558-
"Snapshot snapshot\! already exists."
566+
"Snapshot snapshot\\! already exists."
559567
):
560568
domain_tasks.snapshot_create(
561569
ctx=_ctx,
@@ -847,7 +855,7 @@ def test_snapshot_delete(self):
847855
):
848856
with self.assertRaisesRegexp(
849857
NonRecoverableError,
850-
"Sub snapshots \['snapshot-'\] found for "
858+
"Sub snapshots \\['snapshot-'\\] found for "
851859
"node_name-snapshot_name. You should remove subsnaphots before"
852860
" remove current."
853861
):

0 commit comments

Comments
 (0)