[Kimchi-devel] [PATCH] Take off libvirt to get vms info used before any shutdown and reboot actions
Aline Manera
alinefm at linux.vnet.ibm.com
Tue Nov 3 17:50:39 UTC 2015
On 03/11/2015 04:45, chandra at linux.vnet.ibm.com wrote:
> From: chandrureddy <chandra at linux.vnet.ibm.com>
>
> Earlier been used libvirt to fetch the vms information in the back end code
> UI was using REST API 'plugins/kimchi/vms' to get the vms info.
> Ginger Base not have this functionality and will not work on plain linux.
> Ginger Base should handle well on both plain linux with out KVM and with KVM
>
> New code provides REST API 'plugins/gingerbase/vms'
> 1. to get the vms information on KVM mahcine (virsh way)
> 2. return empty json in case of plain linux machine
> ---
> src/wok/plugins/gingerbase/control/vmsinfo.py | 34 +++++++++++++
> src/wok/plugins/gingerbase/i18n.py | 1 +
> src/wok/plugins/gingerbase/model/host.py | 23 +++------
> src/wok/plugins/gingerbase/model/vmsinfo.py | 32 ++++++++++++
> .../plugins/gingerbase/ui/js/src/gingerbase.api.js | 12 +++++
> .../gingerbase/ui/js/src/gingerbase.host.js | 23 ++++-----
> src/wok/plugins/gingerbase/ui/pages/i18n.json.tmpl | 2 +
> src/wok/plugins/gingerbase/utils.py | 59 +++++++++++++++++++++-
> 8 files changed, 157 insertions(+), 29 deletions(-)
> create mode 100644 src/wok/plugins/gingerbase/control/vmsinfo.py
> create mode 100644 src/wok/plugins/gingerbase/model/vmsinfo.py
>
> diff --git a/src/wok/plugins/gingerbase/control/vmsinfo.py b/src/wok/plugins/gingerbase/control/vmsinfo.py
> new file mode 100644
> index 0000000..e9bddea
> --- /dev/null
> +++ b/src/wok/plugins/gingerbase/control/vmsinfo.py
> @@ -0,0 +1,34 @@
> +#
> +# Project Ginger Base
> +#
> +# Copyright IBM, Corp. 2015
> +#
> +# 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
> +
> +from wok.control.utils import UrlSubNode
> +from wok.control.base import SimpleCollection
> +
> +
> + at UrlSubNode('vms', True)
> +class VMs(SimpleCollection):
> + def __init__(self, model):
> + super(VMs, self).__init__(model)
> + self.role_key = 'host'
> + self.admin_methods = ['GET']
> + self.uri_fmt = '/vms/%s'
> +
> + @property
> + def data(self):
> + return self.info
As I said in the previous patch, we can let the backend to the sanity
verification and return the right error code instead of creating a new API.
> diff --git a/src/wok/plugins/gingerbase/i18n.py b/src/wok/plugins/gingerbase/i18n.py
> index 8596f17..d477b20 100644
> --- a/src/wok/plugins/gingerbase/i18n.py
> +++ b/src/wok/plugins/gingerbase/i18n.py
> @@ -40,6 +40,7 @@ messages = {
> "GGBHOST0001E": _("Unable to shutdown host machine as there are running virtual machines"),
> "GGBHOST0002E": _("Unable to reboot host machine as there are running virtual machines"),
> "GGBHOST0005E": _("When specifying CPU topology, each element must be an integer greater than zero."),
> + "GGBHOST0006E": _("Failed to execute virsh command"),
>
> "GGBPKGUPD0001E": _("No packages marked for update"),
> "GGBPKGUPD0002E": _("Package %(name)s is not marked to be updated."),
> diff --git a/src/wok/plugins/gingerbase/model/host.py b/src/wok/plugins/gingerbase/model/host.py
> index 670fec5..307f26a 100644
> --- a/src/wok/plugins/gingerbase/model/host.py
> +++ b/src/wok/plugins/gingerbase/model/host.py
> @@ -37,6 +37,7 @@ from wok.model.tasks import TaskModel
> from wok.plugins.gingerbase.model.debugreports import DebugReportsModel
> from wok.plugins.gingerbase.repositories import Repositories
> from wok.plugins.gingerbase.swupdate import SoftwareUpdate
> +from wok.plugins.gingerbase.utils import get_vms_by_state
>
> HOST_STATS_INTERVAL = 1
>
> @@ -142,30 +143,22 @@ class HostModel(object):
>
> def shutdown(self, args=None):
> # Check for running vms before shutdown
> - # FIXME : Find alternative way to figure out if any vms running
> - # running_vms = self._get_vms_list_by_state('running')
> - # if len(running_vms) > 0:
> - # raise OperationFailed("GGBHOST0001E")
> + running_vms = get_vms_by_state('running')
> + if len(running_vms) > 0:
> + raise OperationFailed("GGBHOST0001E")
>
> wok_log.info('Host is going to shutdown.')
> os.system('shutdown -h now')
>
> def reboot(self, args=None):
> - # Find running VMs
> - # FIXME : Find alternative way to figure out if any vms running
> - # running_vms = self._get_vms_list_by_state('running')
> - # if len(running_vms) > 0:
> - # raise OperationFailed("GGBHOST0002E")
> + # Check for running vms before reboot
> + running_vms = get_vms_by_state('running')
> + if len(running_vms) > 0:
> + raise OperationFailed("GGBHOST0002E")
>
> wok_log.info('Host is going to reboot.')
> os.system('reboot')
> - # def _get_vms_list_by_state(self, state):
> - # conn = self.conn.get()
> - # return [dom.name().decode('utf-8')
> - # for dom in conn.listAllDomains(0)
> - # if (DOM_STATE_MAP[dom.info()[0]]) == state]
> -
I will refer to this code below. =)
> class SoftwareUpdateProgressModel(object):
> def __init__(self, **kargs):
> diff --git a/src/wok/plugins/gingerbase/model/vmsinfo.py b/src/wok/plugins/gingerbase/model/vmsinfo.py
> new file mode 100644
> index 0000000..a9895df
> --- /dev/null
> +++ b/src/wok/plugins/gingerbase/model/vmsinfo.py
> @@ -0,0 +1,32 @@
> +#
> +# Project Ginger Base
> +#
> +# Copyright IBM, Corp. 2015
> +#
> +# 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
> +
> +from wok.model.tasks import TaskModel
> +from wok.plugins.gingerbase.utils import get_vms
> +
> +
> +class VMsModel(object):
> + def __init__(self, **kargs):
> + self.objstore = kargs['objstore']
> + self.task = TaskModel(**kargs)
> + self.vms_info = get_vms()
> +
> + def get_list(self, *name):
> + self.vms_info = get_vms()
> + return self.vms_info
> diff --git a/src/wok/plugins/gingerbase/ui/js/src/gingerbase.api.js b/src/wok/plugins/gingerbase/ui/js/src/gingerbase.api.js
> index db6543f..9cd3b4a 100644
> --- a/src/wok/plugins/gingerbase/ui/js/src/gingerbase.api.js
> +++ b/src/wok/plugins/gingerbase/ui/js/src/gingerbase.api.js
> @@ -358,5 +358,17 @@ var kimchi = {
> wok.message.error(data.responseJSON.reason);
> }
> });
> + },
> +
> + listVMs : function(suc, err) {
> + wok.requestJSON({
> + url : 'plugins/gingerbase/vms',
> + type : 'GET',
> + contentType : 'application/json',
> + dataType : 'json',
> + resend: true,
> + success : suc,
> + error : err
> + });
> }
> };
> diff --git a/src/wok/plugins/gingerbase/ui/js/src/gingerbase.host.js b/src/wok/plugins/gingerbase/ui/js/src/gingerbase.host.js
> index 0d52b92..ea64dad 100644
> --- a/src/wok/plugins/gingerbase/ui/js/src/gingerbase.host.js
> +++ b/src/wok/plugins/gingerbase/ui/js/src/gingerbase.host.js
> @@ -500,18 +500,17 @@ kimchi.host_main = function() {
> $(shutdownButtonID).prop('disabled', true);
> $(restartButtonID).prop('disabled', true);
> // Check if there is any VM is running.
> - // FIXME : Find alternative way to figure out if any vms running
> - // kimchi.listVMs(function(vms) {
> - // for(var i = 0; i < vms.length; i++) {
> - // if(vms[i]['state'] === 'running') {
> - // wok.message.error.code('GGBHOST6001E');
> - // $(shutdownButtonID).prop('disabled', false);
> - // $(restartButtonID).prop('disabled', false);
> - // return;
> - // }
> - // }
> - //
> - // });
> + kimchi.listVMs(function(vms) {
> + for(var i = 0; i < vms.length; i++) {
> + if(vms[i]['state'] === 'running') {
> + wok.message.error.code('GGBHOST6001E');
> + $(shutdownButtonID).prop('disabled', false);
> + $(restartButtonID).prop('disabled', false);
> + return;
> + }
> + }
> +
> + });
Let the backend returns the right error message while performing the
request POST /plugins/gingerbase/host/shutdown or POST
/plugins/gingerbase/host/reboot
So we don't need to create a new API
> }, function() {
> });
> };
> diff --git a/src/wok/plugins/gingerbase/ui/pages/i18n.json.tmpl b/src/wok/plugins/gingerbase/ui/pages/i18n.json.tmpl
> index f6228ab..346e76f 100644
> --- a/src/wok/plugins/gingerbase/ui/pages/i18n.json.tmpl
> +++ b/src/wok/plugins/gingerbase/ui/pages/i18n.json.tmpl
> @@ -113,6 +113,8 @@
> "GGBDR6012M": "$_("Pending...")",
> "GGBDR6013M": "$_("Report name is the same as the original one.")",
>
> + "GGBHOST6001E": "$_("Unable to shut down system as there are some virtual machines running!")",
> +
> "GGBVM6001M": "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")",
> "GGBVM6002M": "$_("Power off Confirmation")",
> "GGBVM6003M": "$_("This action may produce undesirable results, "
> diff --git a/src/wok/plugins/gingerbase/utils.py b/src/wok/plugins/gingerbase/utils.py
> index 9f41967..b606438 100644
> --- a/src/wok/plugins/gingerbase/utils.py
> +++ b/src/wok/plugins/gingerbase/utils.py
> @@ -22,12 +22,13 @@
>
> import contextlib
> import os
> +import re
> import urllib2
> from httplib import HTTPConnection, HTTPException
> from urlparse import urlparse
>
> -from wok.exception import InvalidParameter
> -
> +from wok.exception import InvalidParameter, OperationFailed
> +from wok.utils import run_command, wok_log
>
> MAX_REDIRECTION_ALLOWED = 5
>
> @@ -80,3 +81,57 @@ def validate_repo_url(url):
> raise InvalidParameter("WOKUTILS0001E", {'url': url})
> else:
> raise InvalidParameter("KCHREPOS0002E")
> +
> +
> +def get_vms():
> + # Check for vms running on the machine
> + # virsh -r -c 'qemu:///system' list --all
> + # Id Name State
> + # ----------------------------------------------------
> + # 3 Fedora21 running
> + # - a8Sr0LzRgWjEqy3iiKjQvA shut off
> + # - kimchi-cdrom shut off
> + # - kimchi-ifaces shut off
> + vms = []
> + try:
> + __import__('libvirt')
> + vms_running = ["virsh", "-r", "-c", "qemu:///system", "list", "--all"]
> + (std_out, std_err, rc) = run_command(vms_running)
> +
I really don't like the idea to parse command output when there is a
specific python binding to care about it.
We can change the former function _get_vms_list_by_state() to work
with/without libvirt support, in a similar way you did here:
Below is the former function:
- # def _get_vms_list_by_state(self, state):
- # conn = self.conn.get()
- # return [dom.name().decode('utf-8')
- # for dom in conn.listAllDomains(0)
- # if (DOM_STATE_MAP[dom.info()[0]]) == state]
-
We could change it like:
def _get_vms_list_by_state(self, state):
try:
libvirt_mod = __import __(libvirt)
except Exception, e:
wok_log.info("Unable to import libvirt module. Details:",
e.message)
# Ignore any error and assume there is no vm running in the host
return []
try:
conn = libvirt_mod.open()
return [dom.name().decode('utf-8') for dom in
conn.listAllDomains(0) - # if (DOM_STATE_MAP[dom.info()[0]]) == state]
except Exception, e:
wok_log.info("Unable to get virtual machines information.
Details:", e.message)
# In that case it is safer to raise the exception, so as we
could not verify the system state may there be running vms
raise OperationFailed(..)
Hope it helps! =)
> + if rc == 0:
> + header_pattern = r'('+re.escape('Id') + r')\s+' \
> + r'('+re.escape('Name') + r')\s+' \
> + r'('+re.escape('State') + r')$'
> +
> + vm_pattern = r'([-]|[0-9]+)\s+' \
> + r'([0-9a-zA-Z-_]*)\s+' \
> + r'([a-zA-Z-_]+[\s]?[a-zA-Z-_]+)'
> + command_out = std_out.strip().split("\n")
> + header = re.search(header_pattern, command_out[0], re.M | re.I)
> +
> + value_pattern = re.compile(vm_pattern, re.M | re.I)
> + for line in command_out[2:]:
> + value = re.search(value_pattern, line)
> + vm_data = {}
> + if value:
> + if (header.group() != value.group()) and \
> + (len(header.groups()) == len(value.groups())):
> + for cnt in range(1, len(header.groups())+1):
> + vm_data[header.group(cnt).lower()] = \
> + value.group(cnt)
> + vms.append(vm_data)
> + else:
> + raise OperationFailed('GGBHOST0006E')
> + except ImportError, err:
> + wok_log.info('Host is running linux with out KVM. %s' % err.message)
> + return []
> + return vms
> +
> +
> +def get_vms_by_state(state='running'):
> + vms_running = []
> + vms = get_vms()
> + for vm in vms:
> + if vm['state'] == state:
> + vms_running.append(vm)
> + return vms_running
More information about the Kimchi-devel
mailing list