From: Aline Manera <alinefm(a)br.ibm.com>
To avoid duplicating code in model and mockmodel, the code related to
storagepool resource was added to model_/storagepools.py and
the specific code for each backend (libvirt or mock) was added to
model_/libvirtbackend.py and model_/mockbackend.py
Signed-off-by: Aline Manera <alinefm(a)br.ibm.com>
---
src/kimchi/model_/libvirtbackend.py | 171 ++++++++++++++++++++++++++++++++++-
src/kimchi/model_/mockbackend.py | 51 +++++++++++
src/kimchi/model_/storagepools.py | 86 ++++++++++++++++++
3 files changed, 307 insertions(+), 1 deletion(-)
create mode 100644 src/kimchi/model_/storagepools.py
diff --git a/src/kimchi/model_/libvirtbackend.py b/src/kimchi/model_/libvirtbackend.py
index 4ab4db6..28bfc01 100644
--- a/src/kimchi/model_/libvirtbackend.py
+++ b/src/kimchi/model_/libvirtbackend.py
@@ -36,18 +36,33 @@ from cherrypy.process.plugins import BackgroundTask
from collections import defaultdict
from kimchi import config
+from kimchi import xmlutils
from kimchi.asynctask import AsyncTask
from kimchi.featuretests import FeatureTests
from kimchi.exception import OperationFailed
+from kimchi.model_.libvirtconnection import LibvirtConnection
+from kimchi.model_.libvirtstoragepool import StoragePoolDef
from kimchi.objectstore import ObjectStore
+from kimchi.scan import Scanner
from kimchi.utils import kimchi_log
HOST_STATS_INTERVAL = 1
+STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
+ 'path': '/pool/source/dir/@path'}}
class LibvirtBackend(object):
- def __init__(self, objstore_loc=None):
+ pool_state_map = {0: 'inactive',
+ 1: 'initializing',
+ 2: 'active',
+ 3: 'degraded',
+ 4: 'inaccessible'}
+
+ def __init__(self, libvirt_uri=None, objstore_loc=None):
+ self.libvirt_uri = libvirt_uri or 'qemu:///system'
+ self.conn = LibvirtConnection(self.libvirt_uri)
self.objstore = ObjectStore(objstore_loc)
self.next_taskid = 1
+ self.scanner = Scanner(self._clean_scan)
self.host_stats = defaultdict(int)
self.host_stats_thread = BackgroundTask(HOST_STATS_INTERVAL,
self._update_host_stats)
@@ -228,3 +243,157 @@ class LibvirtBackend(object):
res['os_codename'] = unicode(codename,"utf-8")
return res
+
+ def get_storagepools(self):
+ try:
+ conn = self.conn.get()
+ names = conn.listStoragePools()
+ names += conn.listDefinedStoragePools()
+ return names
+ except libvirt.libvirtError as e:
+ raise OperationFailed(e.get_error_message())
+
+ def _clean_scan(self, pool_name):
+ try:
+ self.deactivate_storagepool(pool_name)
+ with self.objstore as session:
+ session.delete('scanning', pool_name)
+ except Exception, e:
+ kimchi_log.debug("Error while cleaning deep scan results" %
+ e.message)
+
+ def do_deep_scan(self, params):
+ scan_params = dict(ignore_list=[])
+ scan_params['scan_path'] = params['path']
+ params['type'] = 'dir'
+
+ for pool in self.get_storagepools():
+ try:
+ res = self.get_storagepool_by_name(pool)
+ if res['state'] == 'active':
+ scan_params['ignore_list'].append(res['path'])
+ except Exception, e:
+ kimchi_log.debug("Error while preparing for deep scan: %s" %
+ e.message)
+
+ params['path'] = self.scanner.scan_dir_prepare(params['name'])
+ scan_params['pool_path'] = params['path']
+ task_id = self.add_task('', self.scanner.start_scan, scan_params)
+ # Record scanning-task/storagepool mapping for future querying
+ with self.objstore as session:
+ session.store('scanning', params['name'], task_id)
+ return task_id
+
+ def create_storagepool(self, params):
+ conn = self.conn.get()
+ try:
+ poolDef = StoragePoolDef.create(params)
+ poolDef.prepare(conn)
+ xml = poolDef.xml
+ except KeyError, key:
+ raise MissingParameter("You need to specify '%s' in order to
"
+ "create storage pool" % key)
+
+ try:
+ if task_id:
+ # Create transient pool for deep scan
+ conn.storagePoolCreateXML(xml, 0)
+ return name
+
+ pool = conn.storagePoolDefineXML(xml, 0)
+ if params['type'] in ['logical', 'dir',
'netfs']:
+ pool.build(libvirt.VIR_STORAGE_POOL_BUILD_NEW)
+ # autostart dir and logical storage pool created from kimchi
+ pool.setAutostart(1)
+ else:
+ # disable autostart for others
+ pool.setAutostart(0)
+ except libvirt.libvirtError as e:
+ msg = "Problem creating Storage Pool: %s"
+ kimchi_log.error(msg, e)
+ raise OperationFailed(e.get_error_message())
+
+ def get_storagepool_by_name(self, name):
+ conn = self.conn.get()
+ pool = conn.storagePoolLookupByName(name)
+ info = pool.info()
+ nr_volumes = self._get_storagepool_vols_num(pool)
+ autostart = True if pool.autostart() else False
+ xml = pool.XMLDesc(0)
+ path = xmlutils.xpath_get_text(xml, "/pool/target/path")[0]
+ pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0]
+ source = self._get_storage_source(pool_type, xml)
+ res = {'state': self.pool_state_map[info[0]],
+ 'path': path,
+ 'source': source,
+ 'type': pool_type,
+ 'autostart': autostart,
+ 'capacity': info[1],
+ 'allocated': info[2],
+ 'available': info[3],
+ 'nr_volumes': nr_volumes}
+
+ if not pool.isPersistent():
+ # Deal with deep scan generated pool
+ try:
+ with self.objstore as session:
+ task_id = session.get('scanning', name)
+ res['task_id'] = str(task_id)
+ res['type'] = 'kimchi-iso'
+ except NotFoundError:
+ # User created normal pool
+ pass
+ return res
+
+ def _get_storagepool_vols_num(self, pool):
+ try:
+ if pool.isActive():
+ pool.refresh(0)
+ return pool.numOfVolumes()
+ else:
+ return 0
+ except libvirt.libvirtError as e:
+ raise OperationFailed(e.get_error_message())
+
+ def _get_storage_source(self, pool_type, pool_xml):
+ source = {}
+ if pool_type not in STORAGE_SOURCES:
+ return source
+
+ for key, val in STORAGE_SOURCES[pool_type].items():
+ res = xmlutils.xpath_get_text(pool_xml, val)
+ source[key] = res[0] if len(res) == 1 else res
+
+ return source
+
+ def activate_storagepool(self, name):
+ try:
+ conn = self.conn.get()
+ pool = conn.storagePoolLookupByName(name)
+ pool.create(0)
+ except libvirt.libvirtError as e:
+ raise OperationFailed(e.get_error_message())
+
+ def deactivate_storagepool(self, name):
+ try:
+ conn = self.conn.get()
+ pool = conn.storagePoolLookupByName(name)
+ pool.destroy()
+ except libvirt.libvirtError as e:
+ raise OperationFailed(e.get_error_message())
+
+ def delete_storagepool(self, name):
+ try:
+ conn = self.conn.get()
+ pool = conn.storagePoolLookupByName(name)
+ pool.undefine()
+ except libvirt.libvirtError as e:
+ raise OperationFailed(e.get_error_message())
+
+ def autostart_storagepool(self, name, value):
+ conn = self.conn.get()
+ pool = conn.storagePoolLookupByName(name)
+ if autostart:
+ pool.setAutostart(1)
+ else:
+ pool.setAutostart(0)
diff --git a/src/kimchi/model_/mockbackend.py b/src/kimchi/model_/mockbackend.py
index 5fb332e..01f4c22 100644
--- a/src/kimchi/model_/mockbackend.py
+++ b/src/kimchi/model_/mockbackend.py
@@ -21,6 +21,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+import copy
import os
import random
@@ -33,6 +34,7 @@ class MockBackend(object):
self.objstore = ObjectStore(objstore_loc)
self.next_taskid = 1
self.host_stats = self._get_host_stats()
+ self._storagepools = {}
def _get_host_stats(self):
memory_stats = {'total': 3934908416L,
@@ -85,3 +87,52 @@ class MockBackend(object):
res['os_codename'] = 'Santiago'
return res
+
+ def get_storagepools(self):
+ return self._storagepools.keys()
+
+ def do_deep_scan(self):
+ return self.add_task('', time.sleep, 25)
+
+ def create_storagepool(self, params):
+ name = params['name']
+ pool = MockStoragePool(name)
+ pool.info.update(params)
+ if params['type'] == 'dir':
+ pool.info['autostart'] = True
+ else:
+ pool.info['autostart'] = False
+
+ self._storagepools[name] = pool
+
+ def get_storagepool_by_name(self, name):
+ pool = self._storagepools[name]
+ pool.refresh()
+ return pool.info
+
+ def activate_storagepool(self, name):
+ self._storagepools[name].info['state'] = 'active'
+
+ def deactivate_storagepool(self, name):
+ self._storagepools[name].info['state'] = 'inactive'
+
+ def delete_storagepool(self, name):
+ del self._storagepools[name]
+
+ def autostart_storagepool(self, name, value):
+ self._storagepools[name].info['autostart'] = value
+
+class MockStoragePool(object):
+ def __init__(self, name):
+ self.name = name
+ self._volumes = {}
+ self.info = {'state': 'inactive', 'capacity': 1024
<< 20,
+ 'allocated': 512 << 20, 'available': 512
<< 20,
+ 'path': '/var/lib/libvirt/images', 'source':
{},
+ 'type': 'dir', 'nr_volumes': 0,
'autostart': 0}
+
+ def refresh(self):
+ state = self.info['state']
+ self.info['nr_volumes'] = 0
+ if state == 'active':
+ self.info['nr_volumes'] = len(self._volumes)
diff --git a/src/kimchi/model_/storagepools.py b/src/kimchi/model_/storagepools.py
new file mode 100644
index 0000000..abdebd8
--- /dev/null
+++ b/src/kimchi/model_/storagepools.py
@@ -0,0 +1,86 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+# Adam Litke <agl(a)linux.vnet.ibm.com>
+# Aline Manera <alinefm(a)linux.vnet.ibm.com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ISO_POOL_NAME = u'kimchi_isos'
+
+from kimchi.exception import InvalidParameter
+
+class StoragePools(object):
+ def __init__(self, backend):
+ self.backend = backend
+
+ def get_list(self):
+ return sorted(self.backend.get_storagepools())
+
+ def create(self, params):
+ name = params['name']
+ if name in self.get_list() or name in (ISO_POOL_NAME,):
+ raise InvalidParameter("Storage pool '%s' already exists" %
name)
+
+ task_id = None
+ if params['type'] == 'kimchi-iso':
+ task_id = self.backend.do_deep_scan(params)
+
+ self.backend.create_storagepool(params)
+ return name
+
+class StoragePool(StoragePools):
+ def lookup(self, name):
+ if name not in self.get_list():
+ raise NotFoundError("Storage pool '%s' not found.")
+
+ return self.backend.get_storagepool_by_name(name)
+
+ def activate(self, name):
+ if name not in self.get_list():
+ raise NotFoundError("Storage pool '%s' not found.")
+
+ self.backend.activate_storagepool()
+
+ def deactivate(self, name):
+ if name not in self.get_list():
+ raise NotFoundError("Storage pool '%s' not found.")
+
+ self.backend.deactivate_storagepool()
+
+ def delete(self, name):
+ if name not in self.get_list():
+ raise NotFoundError("Storage pool '%s' not found.")
+
+ if self.get_storagepool_by_name(name)['state'] == 'active':
+ raise InvalidOperation("Unable to delete active storage pool
'%s'" %
+ name)
+
+ self.backend.delete_storagepool()
+
+ def update(self, name, params):
+ if name not in self.get_list():
+ raise NotFoundError("Storage pool '%s' not found.")
+
+ autostart = params['autostart']
+ if autostart not in [True, False]:
+ raise InvalidOperation("Autostart flag must be true or false")
+
+ self.backend.autostart_storagepool(name, autostart)
+
+ return name
--
1.7.10.4