Shahar Havivi has uploaded a new change for review. Change subject: python: support for oVirt API ......................................................................
python: support for oVirt API Change the connection with oVirt engine to work with oVirt API and not via direct RESTful calls Change-Id: I8ab241473b714dacdf57909e02fe1cb579c252e6 Signed-off-by: Shahar Havivi <shav...@redhat.com> --- A python/OVirtDispatcher.py M python/README D python/RestClient.py D python/RestCommand.py M python/WebHandler.py 5 files changed, 134 insertions(+), 192 deletions(-) git pull ssh://gerrit.ovirt.org:29418/samples-portals refs/changes/24/10624/1 diff --git a/python/OVirtDispatcher.py b/python/OVirtDispatcher.py new file mode 100644 index 0000000..5f26f04 --- /dev/null +++ b/python/OVirtDispatcher.py @@ -0,0 +1,60 @@ +from ovirtsdk.api import API +from ovirtsdk.infrastructure.errors import RequestError, ConnectionError + +import ConfigParser +api = None + +class OVirtDispatcher(object): + + def __init__(self): + self.config = ConfigParser.ConfigParser() + self.config.read('ovirt.conf') + self.baseUrl = self.config.get('oVirt', 'BaseUrl') + + def login(self, username, password): + global api + try: + api = API(url=self.baseUrl, username=username, password=password, filter=True) + except RequestError as reqErr: + return False, "Login error" + except ConnectionError as conErr: + return False, "Bad URL" + return True, '' + + def getUserVms(self): + global api + return api.vms.list() + + def getVmById(self, id): + global api + return api.vms.get(id) + + def startVm(self, vmid): + global api + try: + api.vms.get(id=vmid).start() + except RequestError as reqErr: + return False, reqErr.reason, reqErr.detail + except ConnectionError as conErr: + return False, 'Connection Error' + return True, None, None + + def stopVm(self, vmid): + global api + try: + api.vms.get(id=vmid).stop() + except RequestError as reqErr: + return False, reqErr.reason, reqErr.detail + except ConnectionError as conErr: + return False, 'Connection Error' + return True, None, None + + def ticketVm(self, vmid): + global api + try: + ticket = api.vms.get(id=vmid).ticket() + return ticket.get_ticket().get_value(), ticket.get_ticket().get_expiry() + except RequestError as reqErr: + raise Exception(reqErr.reason, reqErr.detail) + except ConnectionError as conErr: + raise Exception('Connection Error', '') diff --git a/python/README b/python/README index 3c9fb13..fb7fa0d 100644 --- a/python/README +++ b/python/README @@ -1,3 +1,6 @@ +Install the sdk: +# yum install ovirt-engine-sdk + This is a Python sample web-portal that uses REST API to view and run VMs from oVirt Engine. This project run a local web server which can be configure via the ovirt.conf file. diff --git a/python/RestClient.py b/python/RestClient.py deleted file mode 100644 index f7061c3..0000000 --- a/python/RestClient.py +++ /dev/null @@ -1,49 +0,0 @@ -import base64 -import httplib -import urllib2 -import urlparse - -cookie = None - -''' -RestClient: handler RESTful post/get methods -''' -class RestClient(object): - - def doGetMethod(self, url, userName=None, password=None): - global cookie - req = urllib2.Request(url) - if cookie is None: - auth = "%s:%s" % (userName, password) - auth = base64.encodestring(auth) - req.add_header("Authorization", "Basic %s" % auth) - else: - req.add_header("Cookie", cookie) - - # run in user level API - req.add_header('filter', 'true') - # For using REST session via cookies, so we won't have to login on every request - req.add_header('Prefer', 'persistent-auth') - response = urllib2.urlopen(req) - if not response.info().getheader('Set-Cookie') is None: - cookie = response.info().getheader('Set-Cookie') - - return response.read() - - def doPostMethod(self, url): - global cookie - u = urlparse.urlparse(url) - - headers = {"Content-type": "application/xml"} - headers['filter'] = 'true' - headers['Prefer'] = 'persistent-auth' - headers['Cookie'] = cookie - - conn = httplib.HTTPConnection(u.hostname, u.port) - conn.request("POST", u.path, body="<action/>", headers=headers) - res = conn.getresponse() - return res.read() - - def resetCookie(self): - global cookie - cookie = None diff --git a/python/RestCommand.py b/python/RestCommand.py deleted file mode 100644 index d161289..0000000 --- a/python/RestCommand.py +++ /dev/null @@ -1,97 +0,0 @@ -import RestClient - -import ConfigParser -from xml.dom import minidom - -restClient = RestClient.RestClient() - -class RestCommand(object): - - def __init__(self): - global restClient - self.config = ConfigParser.ConfigParser() - self.config.read('ovirt.conf') - self.baseUrl = self.config.get('oVirt', 'BaseUrl') - - def _parseVm(self, xmlVm): - vm = {} - vm['startable'] = True - vm['stopable'] = True - vm['connectable'] = True - vm['vmid'] = xmlVm.getAttribute('id') - vm['name'] = xmlVm.getElementsByTagName('name').item(0).firstChild.nodeValue - vm['status'] = xmlVm.getElementsByTagName('status').item(0).getElementsByTagName('state').item(0).firstChild.nodeValue - vm['display'] = xmlVm.getElementsByTagName('display').item(0).getElementsByTagName('type').item(0).firstChild.nodeValue - if len(xmlVm.getElementsByTagName('port')) > 0: - vm['port'] = xmlVm.getElementsByTagName('port').item(0).firstChild.nodeValue - else: - vm['port'] = '-1' - if len(xmlVm.getElementsByTagName('address')) > 0: - vm['address'] = xmlVm.getElementsByTagName('address').item(0).firstChild.nodeValue - else: - vm['address'] = '' - return vm - - def login(self, userName, password): - self.userName = userName - self.password = password - - try: - restClient.resetCookie() - xml = restClient.doGetMethod(self.baseUrl + '/api', userName, password) - return True - except: - return False - - def getUserVms(self): - global restClient - xml = restClient.doGetMethod(self.baseUrl + '/api/vms') - dom = minidom.parseString(xml) - xmlVms = dom.getElementsByTagName('vm') - vms = [] - for xmlVm in xmlVms: - vms.append(self._parseVm(xmlVm)) - - return vms - - def getVmByVmId(self, vmid): - global restClient - xml = restClient.doGetMethod(self.baseUrl + '/api/vms/' + vmid) - dom = minidom.parseString(xml) - return self._parseVm(dom.getElementsByTagName('vm')[0]) - - ''' - Method return action results - - input: - vmid: guid of vm - action: action to run (start, stop or ticket) - there are more actions...(never tested) - - return value: - dictionary - { 'status': 'complete', - 'reason': '...', # only if failed - 'detail': '...', # only if failed - 'value': 'XXYYZZ', # the ticket (password) - 'expired': '7200', # in minutes - } - ''' - def runAction(self, vmid, action): - global restClient - ret = {} - url = '%s/api/vms/%s/%s' % (self.baseUrl, vmid, action) - - xml = restClient.doPostMethod(url) - dom = minidom.parseString(xml) - - ret['status'] = dom.getElementsByTagName('state').item(0).firstChild.nodeValue - if ret['status'] == 'failed': - ret['reason'] = dom.getElementsByTagName('reason').item(0).firstChild.nodeValue - ret['detail'] = dom.getElementsByTagName('detail').item(0).firstChild.nodeValue - - if action == 'ticket': - elem = dom.getElementsByTagName('ticket').item(0) - ret['value'] = elem.getElementsByTagName('value').item(0).firstChild.nodeValue - ret['expired'] = elem.getElementsByTagName('expiry').item(0).firstChild.nodeValue - - return ret diff --git a/python/WebHandler.py b/python/WebHandler.py index b54f19e..a61b982 100644 --- a/python/WebHandler.py +++ b/python/WebHandler.py @@ -1,9 +1,9 @@ -import RestCommand +import OVirtDispatcher import cgi import BaseHTTPServer -restCommand = None +dispatcher = None ''' WebHandler: simple Web Server @@ -18,6 +18,8 @@ if self.path == '/': form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], }) self._login_method(form) + elif self.path == '/uservms': + self._uservms_method() else: self._page_error() @@ -32,6 +34,8 @@ if p == '/': self._login_method() + elif p == '/uservms': + self._uservms_method() elif p == '/action': self._action_method(params) else: @@ -45,9 +49,17 @@ self.wfile.write("<body><p>Page not found %s</p>" % self.path) self.wfile.write("</body></html>") + def _failed_action(self, reason, detail): + return '''<html><body> + <p style='color:red'>Action failed!</p> + <br/>Reason: %s + <br/>Details: %s + <br/> + <button onclick=javascript:location.href='/uservms'>Back</button> + </body></html>''' % (reason, detail) def _action_method(self, params): - global restCommand + global dispatcher self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() @@ -56,37 +68,44 @@ vmid = form['vmid'][0] action = form['action'][0] + success = False - res = restCommand.runAction(vmid, action) + if action == 'start': + success, reason, detail = dispatcher.startVm(vmid) + if not success: + html = self._failed_action(reason, detail) - if res['status'] == 'failed': - html = '''<html><body> - <p style='color:red'>Action failed!</p> - <br/>Reason: %s - <br/>Details: %s - <br/> - <button onclick=javascript:history.back()>Back</button> - </body></html>''' % (res['reason'], res['detail']) + elif action == 'stop': + success, reason, detail = dispatcher.stopVm(vmid) + if not success: + html = self._failed_action(reason, detail) elif action == 'ticket': - vm = restCommand.getVmByVmId(vmid) + try: + value, expiry = dispatcher.ticketVm(vmid) - if self.headers['user-agent'].lower().find('windows') >= 0: - html = self._ticketIE(vm, res) - else: - html = self._ticketFirefox(vm, res) + vm = dispatcher.getVmById(vmid) + display = vm.get_display() - else: + if self.headers['user-agent'].lower().find('windows') >= 0: + html = self._ticketIE(display.get_address(), display.get_port(), value) + else: + html = self._ticketFirefox(display.get_address(), display.get_port(), value) + except Exception as e: + reason, detail = e.args + html = self._failed_action(reason, detail) + + if success: html = '''<html><body> VM '%s' successfully <br/> - <button onclick=javascript:history.back()>Back</button> + <button onclick=javascript:location.href='/uservms'>Back</button> </body></html> ''' % action self.wfile.write(html) - def _ticketIE(self, vm, res): + def _ticketIE(self, host, port, password): html = '''<html> <script> function onConnect() { @@ -97,17 +116,20 @@ } </script> <body> + VMs ticket set: + <br/> <OBJECT style='visibility: hidden' codebase='SpiceX.cab#version=1,0,0,1' ID='spice' CLASSID='CLSID:ACD6D89C-938D-49B4-8E81-DDBD13F4B48A'> </OBJECT> <form> <input type=button onclick='javascript:onConnect();' value='Connect'/> </form> + <button onclick=javascript:location.href='/uservms'>Back</button> </body> -</html>''' % (vm['address'], vm['port'], res['value']) +</html>''' % (host, port, password) return html - def _ticketFirefox(self, vm, res): + def _ticketFirefox(self, host, port, password): html = '''<html> <script> function onConnect() { @@ -119,24 +141,27 @@ } </script> <body> + VMs ticket set: + <br/> <embed id='spice' type="application/x-spice" width=0 height=0><br> <form> <input type=button value='Connent' onclick='onConnect()'/> </form> + <button onclick=javascript:location.href='/uservms'>Back</button> </body> -</html>''' % (vm['address'], vm['port'], res['value']) +</html>''' % (host, port, password) return html def _uservms_method(self): - #form = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], }) + global dispatcher self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() - vms = restCommand.getUserVms() + vms = dispatcher.getUserVms() # render the html html = '''<html> @@ -154,19 +179,9 @@ ''' for vm in vms: - startable = '' - if not vm['startable']: - startable = "disabled='disabled'" - stopable = '' - if not vm['stopable']: - stopable = "disabled='disabled'" - connectable = '' - if not vm['connectable'] or vm['display'] != 'spice': - connectable = "disabled='disabled'" - - startbtn = "<button %s onclick=javascript:location.href='action?vmid=%s&action=start' type='button'>Start</button>" % (startable, vm['vmid']) - stopbtn = "<button %s onclick=javascript:location.href='action?vmid=%s&action=stop' type='button'>Stop</button>" % (stopable, vm['vmid']) - connectbtn = "<button %s onclick=javascript:location.href='action?vmid=%s&action=ticket' type='button'>Console</button>" % (connectable, vm['vmid']) + startbtn = "<button onclick=javascript:location.href='action?vmid=%s&action=start' type='button'>Start</button>" % (vm.get_id()) + stopbtn = "<button onclick=javascript:location.href='action?vmid=%s&action=stop' type='button'>Stop</button>" % (vm.get_id()) + connectbtn = "<button onclick=javascript:location.href='action?vmid=%s&action=ticket' type='button'>Console</button>" % (vm.get_id()) html = html + ''' <tr> <td>%s</td> @@ -175,22 +190,32 @@ <td>%s</td> <td>%s</td> <td>%s</td> - </tr>''' % (vm['name'], vm['status'], vm['display'], startbtn, stopbtn, connectbtn) + </tr> ''' % (vm.get_name(), vm.get_status().get_state(), vm.get_display().get_type(), startbtn, stopbtn, connectbtn) - html = html + ''' </table></center></body></html>''' + html = html + ''' + <tr> + <td><button onclick=javascript:location.href='/'>Logout</button></td> + </tr> + </table></center></body></html>''' self.wfile.write(html) + + def _redirectTo(self, url, timeout=0): + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + self.wfile.write("""<html><head><meta HTTP-EQUIV="REFRESH" content="%i; url=%s"/></head></html>""" % (timeout, url)) def _login_method(self, form={}): - global restCommand + global dispatcher message = '' if form.has_key("username") and form.has_key('password'): - restCommand = RestCommand.RestCommand() - if restCommand.login(form.getvalue("username"), form.getvalue("password")): - self._uservms_method() + dispatcher = OVirtDispatcher.OVirtDispatcher() + loggedin, message = dispatcher.login(form.getvalue("username"), form.getvalue("password")) + + if loggedin: + self._redirectTo('/uservms') return - else: - message = 'Login Error' self.send_response(200) self.send_header("Content-type", "text/html") -- To view, visit http://gerrit.ovirt.org/10624 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I8ab241473b714dacdf57909e02fe1cb579c252e6 Gerrit-PatchSet: 1 Gerrit-Project: samples-portals Gerrit-Branch: master Gerrit-Owner: Shahar Havivi <shav...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches