aboutsummaryrefslogtreecommitdiffstats
path: root/patches/opnfv-fuel/0015-virtual_fuel-initial-support-for-libvirt-volumes.patch
blob: 87266ef851de9effd739f04f2059bee79af6b34c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
From: Josep Puigdemont <josep.puigdemont@enea.com>
Date: Wed, 4 May 2016 14:27:23 +0200
Subject: [PATCH] virtual_fuel: initial support for libvirt volumes

This patch introduces the ability to create volumes on the libvirt host
where the Fuel VM is being deployed. For now a default pool is used,
but the idea is to allow this to be configured.

Since all virsh commands honor LIBVIRT_DEFAULT_URI, we use this
environment variable to detect wheter we should create a volume or not.
The rationale being that this environment variable will only be set if
the user wants to do the VM deployment on a remote libvirt host.

All this could also be done using scp and a user directory on the host
machine, but using pools allows us to take advantage of libvirt's
policies and file permissions.

CHANGE: before this patch, the file system image was named like the VM:
vm_name.raw. This patch introduces a change and adds a timestamp suffix
to the image: vm_name-timestamp.raw. This is so to avoid collisions with
an image with the same name on the remote pool. It may also be useful to
keep around old images for later testing, while the VM definition can
likely be the same.

FIXME: This patch will use a pool called "jenkins" in the libvirt
server, and it will fail if it is not present. This is a requirement
that should be amended in the future, and properly documented.

Signed-off-by: Josep Puigdemont <josep.puigdemont@enea.com>
---
 deploy/deploy.py                       |  5 +++
 deploy/dha_adapters/libvirt_adapter.py | 28 +++++++++++++++++
 deploy/environments/virtual_fuel.py    | 57 +++++++++++++++++++++++++++++-----
 deploy/install_fuel_master.py          |  8 +++--
 4 files changed, 88 insertions(+), 10 deletions(-)

diff --git a/deploy/deploy.py b/deploy/deploy.py
index f86f2be..265e888 100755
--- a/deploy/deploy.py
+++ b/deploy/deploy.py
@@ -243,6 +243,11 @@ class AutoDeploy(object):
 
 
 def check_bridge(pxe_bridge, dha_path):
+    # Assume that bridges on remote nodes exists, we could ssh but
+    # the remote user might not have a login shell.
+    if os.environ.get('LIBVIRT_DEFAULT_URI'):
+        return
+
     with io.open(dha_path) as yaml_file:
         dha_struct = yaml.load(yaml_file)
     if dha_struct['adapter'] != 'libvirt':
diff --git a/deploy/dha_adapters/libvirt_adapter.py b/deploy/dha_adapters/libvirt_adapter.py
index 85913ac..8f3042c 100644
--- a/deploy/dha_adapters/libvirt_adapter.py
+++ b/deploy/dha_adapters/libvirt_adapter.py
@@ -11,6 +11,7 @@
 from lxml import etree
 from hardware_adapter import HardwareAdapter
 import tempfile
+import os
 
 from common import (
     log,
@@ -23,6 +24,13 @@ DEV = {'pxe': 'network',
        'disk': 'hd',
        'iso': 'cdrom'}
 
+vol_xml_template = '''<volume type='file'>
+  <name>%s</name>
+  <capacity unit='%s'>%s</capacity>
+  <target>
+    <format type='%s'/>
+  </target>
+</volume>'''
 
 class LibvirtAdapter(HardwareAdapter):
 
@@ -140,3 +148,23 @@ class LibvirtAdapter(HardwareAdapter):
 
     def get_virt_net_conf_dir(self):
         return self.dha_struct['virtNetConfDir']
+
+    def upload_iso(self, iso_file):
+        size = os.path.getsize(iso_file)
+        vol_name = os.path.basename(iso_file)
+        vol_xml = vol_xml_template % (vol_name, 'bytes', str(size), 'raw')
+        fd, fname = tempfile.mkstemp(text=True, suffix='deploy')
+        os.write(fd, vol_xml)
+        os.close(fd)
+
+        log(vol_xml)
+        pool = 'jenkins' # FIXME
+        exec_cmd('virsh vol-create --pool %s %s' % (pool, fname))
+        vol_path = exec_cmd('virsh vol-path --pool %s %s' % (pool, vol_name))
+
+        exec_cmd('virsh vol-upload %s %s' % (vol_path, iso_file),
+                 attempts=5, delay=10, verbose=True)
+
+        delete(fname)
+
+        return vol_path
diff --git a/deploy/environments/virtual_fuel.py b/deploy/environments/virtual_fuel.py
index 82c4e47..56d6f98 100644
--- a/deploy/environments/virtual_fuel.py
+++ b/deploy/environments/virtual_fuel.py
@@ -11,14 +11,33 @@
 from lxml import etree
 from execution_environment import ExecutionEnvironment
 import tempfile
+import os
+import re
 
 from common import (
     exec_cmd,
     check_file_exists,
     check_if_root,
     delete,
+    log,
 )
 
+vol_xml_template = '''<volume type='file'>
+  <name>%s</name>
+  <capacity unit='%s'>%s</capacity>
+  <target>
+    <format type='%s'/>
+  </target>
+</volume>'''
+
+def get_size_and_unit(s):
+    p = re.compile('^(\d+)\s*(\D+)')
+    m = p.match(s)
+    if m == None:
+        return None, None
+    size = m.groups()[0]
+    unit = m.groups()[1]
+    return size, unit
 
 class VirtualFuel(ExecutionEnvironment):
 
@@ -51,19 +70,41 @@ class VirtualFuel(ExecutionEnvironment):
         with open(temp_vm_file, 'w') as f:
             vm_xml.write(f, pretty_print=True, xml_declaration=True)
 
+    def create_volume(self, pool, name, su, img_type='qcow2'):
+        log('Creating image using Libvirt volumes in pool %s, name: %s' %
+            (pool, name))
+        size, unit = get_size_and_unit(su)
+        if size == None:
+            err('Could not determine size and unit of %s' % s)
+
+        vol_xml = vol_xml_template % (name, unit, str(size), img_type)
+        fname = os.path.join(self.temp_dir, '%s_vol.xml' % name)
+        with file(fname, 'w') as f:
+            f.write(vol_xml)
+
+        exec_cmd('virsh vol-create --pool %s %s' % (pool, fname))
+        vol_path = exec_cmd('virsh vol-path --pool %s %s' % (pool, name))
+
+        delete(fname)
+
+        return vol_path
+
     def create_image(self, disk_path, disk_size):
-        exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
+        if os.environ.get('LIBVIRT_DEFAULT_URI') == None:
+            exec_cmd('qemu-img create -f qcow2 %s %s' % (disk_path, disk_size))
+        else:
+            pool = 'jenkins' # FIXME
+            name = os.path.basename(disk_path)
+            disk_path = self.create_volume(pool, name, disk_size)
 
-    def create_vm(self):
-        vm_template = '%s/%s' % (self.root_dir,
-                                 self.dha.get_node_property(
-                                     self.fuel_node_id, 'libvirtTemplate'))
-        check_file_exists(vm_template)
+        return disk_path
 
-        disk_path = '%s/%s.raw' % (self.storage_dir, self.vm_name)
+    def create_vm(self):
+        stamp = time.strftime("%Y%m%d%H%M%S")
+        disk_path = '%s/%s-%s.raw' % (self.storage_dir, self.vm_name, stamp)
         disk_sizes = self.dha.get_disks()
         disk_size = disk_sizes['fuel']
-        self.create_image(disk_path, disk_size)
+        disk_path = self.create_image(disk_path, disk_size)
 
         temp_vm_file = '%s/%s' % (self.temp_dir, self.vm_name)
         exec_cmd('cp %s %s' % (vm_template, temp_vm_file))
diff --git a/deploy/install_fuel_master.py b/deploy/install_fuel_master.py
index 4f6a052..1c1bf05 100644
--- a/deploy/install_fuel_master.py
+++ b/deploy/install_fuel_master.py
@@ -54,8 +54,12 @@ class InstallFuelMaster(object):
 
         self.dha.node_power_off(self.fuel_node_id)
 
-        log('Zero the MBR')
-        self.dha.node_zero_mbr(self.fuel_node_id)
+        if os.environ.get('LIBVIRT_DEFAULT_URI'):
+            log('Upload ISO to pool')
+            self.iso_file = self.dha.upload_iso(self.iso_file)
+        else:
+            log('Zero the MBR')
+            self.dha.node_zero_mbr(self.fuel_node_id)
 
         self.dha.node_set_boot_order(self.fuel_node_id, ['disk', 'iso'])