On 14/01/2015 21:24, Christy Perez wrote:
If a transient (or non-persistent) VM is created outside of
Kimchi, it will (expectedly) be deleted when shutdown. However,
Kimchi calls a GET after shutdown and other POST requests. When
the GET fails because the VM has been deleted, a user will get
an error. However, this is not an error condition.
This patch checks to see if a VM is transient, and if so, does not
call the subsequent GET on the VM.
The same scenario exists for networks and storage pools.
Could you please also update them?
Also as it is common to multiple resources we could move the
verification to framework
At control/base.py in in generate_action_handler_base():
is_persistent = None
if hasattr(model, model_fn(self, 'is_persistent')):
is_persistent = getattr(self.model, model_fn(self, 'is_persistent'))
(...)
except NotFoundError, e:
# only raise the exception if the resource is persistent
if is_persistent is not None and is_persistent():
raise cherrypy.HTTPError(404, e.message)
---
src/kimchi/control/base.py | 13 +++++++++----
src/kimchi/control/vms.py | 17 +++++++++++++----
src/kimchi/i18n.py | 1 +
src/kimchi/model/vms.py | 9 ++++++++-
4 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py
index 97e789f..8048370 100644
--- a/src/kimchi/control/base.py
+++ b/src/kimchi/control/base.py
@@ -63,7 +63,8 @@ def _redirect(self, ident, code=303):
uri_params += [urllib2.quote(ident.encode('utf-8'),
safe="")]
raise cherrypy.HTTPRedirect(self.uri_fmt % tuple(uri_params), code)
- def generate_action_handler(self, action_name, action_args=None):
+ def generate_action_handler(self, action_name, action_args=None,
+ ignore_not_found=False):
def _render_element(self, ident):
self._redirect(ident)
uri_params = []
@@ -75,7 +76,7 @@ def _render_element(self, ident):
raise internal_redirect(self.uri_fmt % tuple(uri_params))
return self._generate_action_handler_base(action_name, _render_element,
- action_args)
+ ignore_not_found, action_args)
def generate_action_handler_task(self, action_name, action_args=None):
def _render_task(self, task):
@@ -86,7 +87,7 @@ def _render_task(self, task):
action_args)
def _generate_action_handler_base(self, action_name, render_fn,
- action_args=None):
+ ignore_not_found, action_args=None):
def wrapper(*args, **kwargs):
validate_method(('POST'), self.role_key, self.admin_methods)
try:
@@ -111,7 +112,11 @@ def wrapper(*args, **kwargs):
except UnauthorizedError, e:
raise cherrypy.HTTPError(403, e.message)
except NotFoundError, e:
- raise cherrypy.HTTPError(404, e.message)
+ if ignore_not_found is True:
+ # Should something be returned instead?
+ pass
+ else:
+ raise cherrypy.HTTPError(404, e.message)
except OperationFailed, e:
raise cherrypy.HTTPError(500, e.message)
except KimchiException, e:
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index 1a84b6c..82682af 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -21,7 +21,6 @@
from kimchi.control.utils import internal_redirect, UrlSubNode
from kimchi.control.vm import sub_nodes
-
@UrlSubNode('vms', True)
class VMs(Collection):
def __init__(self, model):
@@ -37,12 +36,22 @@ def __init__(self, model, ident):
self.role_key = 'guests'
self.screenshot = VMScreenShot(model, ident)
self.uri_fmt = '/vms/%s'
+ _ignore_not_found = False
+ try:
+ # the test driver causes an exception in is_persistent()
+ _ignore_not_found = \
+ not model.vm_is_persistent(self.ident.decode('utf-8'))
+ except:
+ pass
for ident, node in sub_nodes.items():
setattr(self, ident, node(model, self.ident))
self.start = self.generate_action_handler('start')
- self.poweroff = self.generate_action_handler('poweroff')
- self.shutdown = self.generate_action_handler('shutdown')
- self.reset = self.generate_action_handler('reset')
+ self.poweroff = self.generate_action_handler('poweroff',
+ ignore_not_found = _ignore_not_found)
+ self.shutdown = self.generate_action_handler('shutdown',
+ ignore_not_found = _ignore_not_found)
+ self.reset = self.generate_action_handler('reset',
+ ignore_not_found = _ignore_not_found)
self.connect = self.generate_action_handler('connect')
self.clone = self.generate_action_handler_task('clone')
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 4eccc3e..4ab08d1 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -106,6 +106,7 @@
"KCHVM0033E": _("Virtual machine '%(name)s' must be stopped
before cloning it."),
"KCHVM0034E": _("Insufficient disk space to clone virtual machine
'%(name)s'"),
"KCHVM0035E": _("Unable to clone VM '%(name)s'. Details:
%(err)s"),
+ "KCHVM0036E": _("Invalid operation for non-persistent virtual machine
%(name)s"),
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned
host device %(dev_name)s."),
"KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to
directly assign to VM."),
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index bae27c1..d3b4f1f 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -760,9 +760,13 @@ def lookup(self, name):
'users': users,
'groups': groups,
'access': 'full',
- 'persistent': True if dom.isPersistent() else False
+ 'persistent': self.is_persistent(name)
}
+ def is_persistent(self, name):
+ dom = self.get_vm(name, self.conn)
+ return True if dom.isPersistent() else False
+
def _vm_get_disk_paths(self, dom):
xml = dom.XMLDesc(0)
xpath = "/domain/devices/disk[@device='disk']/source/@file"
@@ -784,6 +788,9 @@ def get_vm(name, conn):
def delete(self, name):
conn = self.conn.get()
dom = self.get_vm(name, self.conn)
+ if not dom.isPersistent():
+ raise InvalidOperation("KCHVM0036E", {'name': name})
+
self._vmscreenshot_delete(dom.UUIDString())
paths = self._vm_get_disk_paths(dom)
info = self.lookup(name)