From: Aline Manera <alinefm(a)br.ibm.com>
Separate StoragePoolDef (and its sub-classes) from model as it handles the xml
generation for libvirt storage pools.
Signed-off-by: Aline Manera <alinefm(a)br.ibm.com>
---
src/kimchi/model.py | 232 +---------------------------
src/kimchi/model_/libvirtstoragepool.py | 257 +++++++++++++++++++++++++++++++
2 files changed, 260 insertions(+), 229 deletions(-)
create mode 100644 src/kimchi/model_/libvirtstoragepool.py
diff --git a/src/kimchi/model.py b/src/kimchi/model.py
index 5d74458..2e89598 100644
--- a/src/kimchi/model.py
+++ b/src/kimchi/model.py
@@ -39,7 +39,6 @@ import re
import shutil
import subprocess
import sys
-import tempfile
import threading
import time
import uuid
@@ -69,17 +68,16 @@ from kimchi.asynctask import AsyncTask
from kimchi.distroloader import DistroLoader
from kimchi.config import config as kconfig
from kimchi.exception import InvalidOperation, InvalidParameter, IsoFormatError
-from kimchi.exception import MissingParameter, NotFoundError, OperationFailed,
TimeoutExpired
+from kimchi.exception import MissingParameter, NotFoundError, OperationFailed
from kimchi.featuretests import FeatureTests
-from kimchi.iscsi import TargetClient
from kimchi.isoinfo import IsoImage
from kimchi.model_.libvirtconnection import LibvirtConnection
+from kimchi.model_.libvirtstoragepool import StoragePoolDef
from kimchi.objectstore import ObjectStore
-from kimchi.rollbackcontext import RollbackContext
from kimchi.scan import Scanner
from kimchi.screenshot import VMScreenshot
from kimchi.utils import get_enabled_plugins, is_digit, kimchi_log
-from kimchi.utils import run_command, parse_cmd_output, patch_find_nfs_target
+from kimchi.utils import patch_find_nfs_target
from kimchi.vmtemplate import VMTemplate
@@ -1688,230 +1686,6 @@ def _get_storage_server_spec(**kwargs):
return xml
-class StoragePoolDef(object):
- @classmethod
- def create(cls, poolArgs):
- for klass in cls.__subclasses__():
- if poolArgs['type'] == klass.poolType:
- return klass(poolArgs)
- raise OperationFailed('Unsupported pool type: %s' %
poolArgs['type'])
-
- def __init__(self, poolArgs):
- self.poolArgs = poolArgs
-
- def prepare(self, conn):
- ''' Validate pool arguments and perform preparations. Operation
which
- would cause side effect should be put here. Subclasses can optionally
- override this method, or it always succeeds by default. '''
- pass
-
- @property
- def xml(self):
- ''' Subclasses have to override this method to actually generate the
- storage pool XML definition. Should cause no side effect and be
- idempotent'''
- # TODO: When add new pool type, should also add the related test in
- # tests/test_storagepool.py
- raise OperationFailed('self.xml is not implemented: %s' % self)
-
-
-class DirPoolDef(StoragePoolDef):
- poolType = 'dir'
-
- @property
- def xml(self):
- # Required parameters
- # name:
- # type:
- # path:
- xml = """
- <pool type='dir'>
- <name>{name}</name>
- <target>
- <path>{path}</path>
- </target>
- </pool>
- """.format(**self.poolArgs)
- return xml
-
-
-class NetfsPoolDef(StoragePoolDef):
- poolType = 'netfs'
-
- def __init__(self, poolArgs):
- super(NetfsPoolDef, self).__init__(poolArgs)
- self.path = '/var/lib/kimchi/nfs_mount/' + self.poolArgs['name']
-
- def prepare(self, conn):
- mnt_point = tempfile.mkdtemp(dir='/tmp')
- export_path = "%s:%s" % (
- self.poolArgs['source']['host'],
self.poolArgs['source']['path'])
- mount_cmd = ["mount", "-o",
'soft,timeo=100,retrans=3,retry=0',
- export_path, mnt_point]
- umount_cmd = ["umount", "-f", export_path]
- mounted = False
-
- with RollbackContext() as rollback:
- rollback.prependDefer(os.rmdir, mnt_point)
- try:
- run_command(mount_cmd, 30)
- rollback.prependDefer(run_command, umount_cmd)
- except TimeoutExpired:
- raise InvalidParameter("Export path %s may block during nfs
mount" % export_path)
-
- with open("/proc/mounts" , "rb") as f:
- rawMounts = f.read()
- output_items = ['dev_path', 'mnt_point', 'type']
- mounts = parse_cmd_output(rawMounts, output_items)
- for item in mounts:
- if 'dev_path' in item and item['dev_path'] ==
export_path:
- mounted = True
-
- if not mounted:
- raise InvalidParameter(
- "Export path %s mount failed during nfs mount" %
export_path)
-
- @property
- def xml(self):
- # Required parameters
- # name:
- # type:
- # source[host]:
- # source[path]:
- poolArgs = copy.deepcopy(self.poolArgs)
- poolArgs['path'] = self.path
- xml = """
- <pool type='netfs'>
- <name>{name}</name>
- <source>
- <host name='{source[host]}'/>
- <dir path='{source[path]}'/>
- </source>
- <target>
- <path>{path}</path>
- </target>
- </pool>
- """.format(**poolArgs)
- return xml
-
-
-class LogicalPoolDef(StoragePoolDef):
- poolType = 'logical'
-
- def __init__(self, poolArgs):
- super(LogicalPoolDef, self).__init__(poolArgs)
- self.path = '/var/lib/kimchi/logical_mount/' +
self.poolArgs['name']
-
- @property
- def xml(self):
- # Required parameters
- # name:
- # type:
- # source[devices]:
- poolArgs = copy.deepcopy(self.poolArgs)
- devices = []
- for device_path in poolArgs['source']['devices']:
- devices.append('<device path="%s" />' % device_path)
-
- poolArgs['source']['devices'] = ''.join(devices)
- poolArgs['path'] = self.path
-
- xml = """
- <pool type='logical'>
- <name>{name}</name>
- <source>
- {source[devices]}
- </source>
- <target>
- <path>{path}</path>
- </target>
- </pool>
- """.format(**poolArgs)
- return xml
-
-
-class IscsiPoolDef(StoragePoolDef):
- poolType = 'iscsi'
-
- def prepare(self, conn):
- source = self.poolArgs['source']
- if not TargetClient(**source).validate():
- raise OperationFailed("Can not login to iSCSI host %s target %s" %
- (source['host'], source['target']))
- self._prepare_auth(conn)
-
- def _prepare_auth(self, conn):
- try:
- auth = self.poolArgs['source']['auth']
- except KeyError:
- return
-
- try:
- virSecret = conn.secretLookupByUsage(
- libvirt.VIR_SECRET_USAGE_TYPE_ISCSI, self.poolArgs['name'])
- except libvirt.libvirtError:
- xml = '''
- <secret ephemeral='no' private='yes'>
- <description>Secret for iSCSI storage pool
{name}</description>
- <auth type='chap' username='{username}'/>
- <usage type='iscsi'>
- <target>{name}</target>
- </usage>
- </secret>'''.format(name=self.poolArgs['name'],
- username=auth['username'])
- virSecret = conn.secretDefineXML(xml)
-
- virSecret.setValue(auth['password'])
-
- def _format_port(self, poolArgs):
- try:
- port = poolArgs['source']['port']
- except KeyError:
- return ""
- return "port='%s'" % port
-
- def _format_auth(self, poolArgs):
- try:
- auth = poolArgs['source']['auth']
- except KeyError:
- return ""
-
- return '''
- <auth type='chap' username='{username}'>
- <secret type='iscsi' usage='{name}'/>
- </auth>'''.format(name=poolArgs['name'],
username=auth['username'])
-
- @property
- def xml(self):
- # Required parameters
- # name:
- # type:
- # source[host]:
- # source[target]:
- #
- # Optional parameters
- # source[port]:
- poolArgs = copy.deepcopy(self.poolArgs)
- poolArgs['source'].update({'port': self._format_port(poolArgs),
- 'auth': self._format_auth(poolArgs)})
- poolArgs['path'] = '/dev/disk/by-id'
-
- xml = """
- <pool type='iscsi'>
- <name>{name}</name>
- <source>
- <host name='{source[host]}' {source[port]}/>
- <device path='{source[target]}'/>
- {source[auth]}
- </source>
- <target>
- <path>{path}</path>
- </target>
- </pool>
- """.format(**poolArgs)
- return xml
-
-
def _get_volume_xml(**kwargs):
# Required parameters
# name:
diff --git a/src/kimchi/model_/libvirtstoragepool.py
b/src/kimchi/model_/libvirtstoragepool.py
new file mode 100644
index 0000000..f4dbf2e
--- /dev/null
+++ b/src/kimchi/model_/libvirtstoragepool.py
@@ -0,0 +1,257 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2013
+#
+# Authors:
+# 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
+
+import copy
+import os
+import tempfile
+
+import libvirt
+
+from kimchi.exception import InvalidParameter, OperationFailed, TimeoutExpired
+from kimchi.iscsi import TargetClient
+from kimchi.rollbackcontext import RollbackContext
+from kimchi.utils import parse_cmd_output, run_command
+
+
+class StoragePoolDef(object):
+ @classmethod
+ def create(cls, poolArgs):
+ for klass in cls.__subclasses__():
+ if poolArgs['type'] == klass.poolType:
+ return klass(poolArgs)
+ raise OperationFailed('Unsupported pool type: %s' %
poolArgs['type'])
+
+ def __init__(self, poolArgs):
+ self.poolArgs = poolArgs
+
+ def prepare(self, conn):
+ ''' Validate pool arguments and perform preparations. Operation
which
+ would cause side effect should be put here. Subclasses can optionally
+ override this method, or it always succeeds by default. '''
+ pass
+
+ @property
+ def xml(self):
+ ''' Subclasses have to override this method to actually generate the
+ storage pool XML definition. Should cause no side effect and be
+ idempotent'''
+ # TODO: When add new pool type, should also add the related test in
+ # tests/test_storagepool.py
+ raise OperationFailed('self.xml is not implemented: %s' % self)
+
+
+class DirPoolDef(StoragePoolDef):
+ poolType = 'dir'
+
+ @property
+ def xml(self):
+ # Required parameters
+ # name:
+ # type:
+ # path:
+ xml = """
+ <pool type='dir'>
+ <name>{name}</name>
+ <target>
+ <path>{path}</path>
+ </target>
+ </pool>
+ """.format(**self.poolArgs)
+ return xml
+
+
+class NetfsPoolDef(StoragePoolDef):
+ poolType = 'netfs'
+
+ def __init__(self, poolArgs):
+ super(NetfsPoolDef, self).__init__(poolArgs)
+ self.path = '/var/lib/kimchi/nfs_mount/' + self.poolArgs['name']
+
+ def prepare(self, conn):
+ mnt_point = tempfile.mkdtemp(dir='/tmp')
+ export_path = "%s:%s" % (
+ self.poolArgs['source']['host'],
self.poolArgs['source']['path'])
+ mount_cmd = ["mount", "-o",
'soft,timeo=100,retrans=3,retry=0',
+ export_path, mnt_point]
+ umount_cmd = ["umount", "-f", export_path]
+ mounted = False
+
+ with RollbackContext() as rollback:
+ rollback.prependDefer(os.rmdir, mnt_point)
+ try:
+ run_command(mount_cmd, 30)
+ rollback.prependDefer(run_command, umount_cmd)
+ except TimeoutExpired:
+ err = "Export path %s may block during nfs mount"
+ raise InvalidParameter(err % export_path)
+
+ with open("/proc/mounts", "rb") as f:
+ rawMounts = f.read()
+ output_items = ['dev_path', 'mnt_point', 'type']
+ mounts = parse_cmd_output(rawMounts, output_items)
+ for item in mounts:
+ if 'dev_path' in item and item['dev_path'] ==
export_path:
+ mounted = True
+
+ if not mounted:
+ err = "Export path %s mount failed during nfs mount"
+ raise InvalidParameter(err % export_path)
+
+ @property
+ def xml(self):
+ # Required parameters
+ # name:
+ # type:
+ # source[host]:
+ # source[path]:
+ poolArgs = copy.deepcopy(self.poolArgs)
+ poolArgs['path'] = self.path
+ xml = """
+ <pool type='netfs'>
+ <name>{name}</name>
+ <source>
+ <host name='{source[host]}'/>
+ <dir path='{source[path]}'/>
+ </source>
+ <target>
+ <path>{path}</path>
+ </target>
+ </pool>
+ """.format(**poolArgs)
+ return xml
+
+
+class LogicalPoolDef(StoragePoolDef):
+ poolType = 'logical'
+
+ def __init__(self, poolArgs):
+ super(LogicalPoolDef, self).__init__(poolArgs)
+ self.path = '/var/lib/kimchi/logical_mount/' +
self.poolArgs['name']
+
+ @property
+ def xml(self):
+ # Required parameters
+ # name:
+ # type:
+ # source[devices]:
+ poolArgs = copy.deepcopy(self.poolArgs)
+ devices = []
+ for device_path in poolArgs['source']['devices']:
+ devices.append('<device path="%s" />' % device_path)
+
+ poolArgs['source']['devices'] = ''.join(devices)
+ poolArgs['path'] = self.path
+
+ xml = """
+ <pool type='logical'>
+ <name>{name}</name>
+ <source>
+ {source[devices]}
+ </source>
+ <target>
+ <path>{path}</path>
+ </target>
+ </pool>
+ """.format(**poolArgs)
+ return xml
+
+
+class IscsiPoolDef(StoragePoolDef):
+ poolType = 'iscsi'
+
+ def prepare(self, conn):
+ source = self.poolArgs['source']
+ if not TargetClient(**source).validate():
+ raise OperationFailed("Can not login to iSCSI host %s target %s" %
+ (source['host'], source['target']))
+ self._prepare_auth(conn)
+
+ def _prepare_auth(self, conn):
+ try:
+ auth = self.poolArgs['source']['auth']
+ except KeyError:
+ return
+
+ try:
+ virSecret = conn.secretLookupByUsage(
+ libvirt.VIR_SECRET_USAGE_TYPE_ISCSI, self.poolArgs['name'])
+ except libvirt.libvirtError:
+ xml = '''
+ <secret ephemeral='no' private='yes'>
+ <description>Secret for iSCSI storage pool
{name}</description>
+ <auth type='chap' username='{username}'/>
+ <usage type='iscsi'>
+ <target>{name}</target>
+ </usage>
+ </secret>'''.format(name=self.poolArgs['name'],
+ username=auth['username'])
+ virSecret = conn.secretDefineXML(xml)
+
+ virSecret.setValue(auth['password'])
+
+ def _format_port(self, poolArgs):
+ try:
+ port = poolArgs['source']['port']
+ except KeyError:
+ return ""
+ return "port='%s'" % port
+
+ def _format_auth(self, poolArgs):
+ try:
+ auth = poolArgs['source']['auth']
+ except KeyError:
+ return ""
+
+ return '''
+ <auth type='chap' username='{username}'>
+ <secret type='iscsi' usage='{name}'/>
+ </auth>'''.format(name=poolArgs['name'],
username=auth['username'])
+
+ @property
+ def xml(self):
+ # Required parameters
+ # name:
+ # type:
+ # source[host]:
+ # source[target]:
+ #
+ # Optional parameters
+ # source[port]:
+ poolArgs = copy.deepcopy(self.poolArgs)
+ poolArgs['source'].update({'port': self._format_port(poolArgs),
+ 'auth': self._format_auth(poolArgs)})
+ poolArgs['path'] = '/dev/disk/by-id'
+
+ xml = """
+ <pool type='iscsi'>
+ <name>{name}</name>
+ <source>
+ <host name='{source[host]}' {source[port]}/>
+ <device path='{source[target]}'/>
+ {source[auth]}
+ </source>
+ <target>
+ <path>{path}</path>
+ </target>
+ </pool>
+ """.format(**poolArgs)
+ return xml
--
1.7.10.4