You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by or...@apache.org on 2010/05/04 23:33:07 UTC
svn commit: r941052 - in /incubator/libcloud/trunk: libcloud/
libcloud/drivers/ test/ test/fixtures/opennebula/
Author: oremj
Date: Tue May 4 21:33:06 2010
New Revision: 941052
URL: http://svn.apache.org/viewvc?rev=941052&view=rev
Log:
LIBCLOUD-17: Add OpenNebula driver.
Author: Daniel Molina Aranda <da...@pdi.ucm.es>
Signed-off-by: Jeremy Orem <je...@gmail.com>
Added:
incubator/libcloud/trunk/libcloud/drivers/opennebula.py
incubator/libcloud/trunk/test/fixtures/opennebula/
incubator/libcloud/trunk/test/fixtures/opennebula/compute.xml
incubator/libcloud/trunk/test/fixtures/opennebula/computes.xml
incubator/libcloud/trunk/test/fixtures/opennebula/disk.xml
incubator/libcloud/trunk/test/fixtures/opennebula/storage.xml
incubator/libcloud/trunk/test/test_opennebula.py
Modified:
incubator/libcloud/trunk/libcloud/providers.py
incubator/libcloud/trunk/libcloud/types.py
incubator/libcloud/trunk/test/secrets.py-dist
Added: incubator/libcloud/trunk/libcloud/drivers/opennebula.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/drivers/opennebula.py?rev=941052&view=auto
==============================================================================
--- incubator/libcloud/trunk/libcloud/drivers/opennebula.py (added)
+++ incubator/libcloud/trunk/libcloud/drivers/opennebula.py Tue May 4 21:33:06 2010
@@ -0,0 +1,206 @@
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# libcloud.org licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""
+OpenNebula driver
+"""
+
+from base64 import b64encode
+import hashlib
+from xml.etree import ElementTree as ET
+
+from libcloud.providers import Provider
+from libcloud.types import NodeState, InvalidCredsException
+from libcloud.base import Response, ConnectionUserAndKey
+from libcloud.base import NodeDriver, Node, NodeLocation
+from libcloud.base import NodeImage, NodeSize
+
+
+API_HOST = ''
+API_PORT = (4567, 443)
+API_SECURE = True
+
+
+class OpenNebulaResponse(Response):
+
+ def success(self):
+ i = int(self.status)
+ return i >= 200 and i <= 299
+
+ def parse_body(self):
+ if not self.body:
+ return None
+ return ET.XML(self.body)
+
+ def parse_error(self):
+ if int(self.status) == 401:
+ raise InvalidCredsException(self.body)
+ return self.body
+
+
+class OpenNebulaConnection(ConnectionUserAndKey):
+
+ host = API_HOST
+ port = API_PORT
+ secure = API_SECURE
+ responseCls = OpenNebulaResponse
+
+ def add_default_headers(self, headers):
+ pass_sha1 = hashlib.sha1(self.key).hexdigest()
+ headers['Authorization'] = ("Basic %s" % b64encode("%s:%s" % (self.user_id, pass_sha1)))
+ return headers
+
+
+class OpenNebulaNodeDriver(NodeDriver):
+
+ connectionCls = OpenNebulaConnection
+ type = Provider.OPENNEBULA
+ name = 'OpenNebula'
+
+ NODE_STATE_MAP = {
+ 'PENDING': NodeState.PENDING,
+ 'ACTIVE': NodeState.RUNNING,
+ 'DONE': NodeState.TERMINATED,
+ 'STOPPED': NodeState.TERMINATED
+ }
+
+ def list_sizes(self, location=None):
+ return [
+ NodeSize(id=1,
+ name="small",
+ ram=None,
+ disk=None,
+ bandwidth=None,
+ price=None,
+ driver=self),
+ NodeSize(id=2,
+ name="medium",
+ ram=None,
+ disk=None,
+ bandwidth=None,
+ price=None,
+ driver=self),
+ NodeSize(id=3,
+ name="large",
+ ram=None,
+ disk=None,
+ bandwidth=None,
+ price=None,
+ driver=self),
+ ]
+
+ def list_nodes(self):
+ return self._to_nodes(self.connection.request('/compute').object)
+
+ def list_images(self, location=None):
+ return self._to_images(self.connection.request('/storage').object)
+
+ def list_locations(self):
+ return [NodeLocation(0, 'OpenNebula', 'ONE', self)]
+
+ def reboot_node(self, node):
+ compute_id = str(node.id)
+
+ url = '/compute/%s' % compute_id
+ resp1 = self.connection.request(url,method='PUT',data=self._xml_action(compute_id,'STOPPED'))
+
+ if resp1.status == 400:
+ return False
+
+ resp2 = self.connection.request(url,method='PUT',data=self._xml_action(compute_id,'RESUME'))
+
+ if resp2.status == 400:
+ return False
+
+ return True
+
+ def destroy_node(self, node):
+ url = '/compute/%s' % (str(node.id))
+ resp = self.connection.request(url,method='DELETE')
+
+ return resp.status == 204
+
+ def create_node(self, **kwargs):
+ compute = ET.Element('COMPUTE')
+
+ name = ET.SubElement(compute, 'NAME')
+ name.text = kwargs['name']
+
+ instance_type = ET.SubElement(compute, 'INSTANCE_TYPE')
+ instance_type.text = kwargs['size'].name
+
+ storage = ET.SubElement(compute, 'STORAGE')
+ disk = ET.SubElement(storage, 'DISK', {'image': str(kwargs['image'].id),
+ 'dev': 'sda1'})
+
+ xml = ET.tostring(compute)
+
+ node = self.connection.request('/compute',method='POST',data=xml).object
+
+ return self._to_node(node)
+
+ def _to_images(self, object):
+ images = []
+ for element in object.findall("DISK"):
+ image_id = element.attrib["href"].partition("/storage/")[2]
+ image = self.connection.request(("/storage/%s" % (image_id))).object
+ images.append(self._to_image(image))
+
+ return images
+
+ def _to_image(self, image):
+ return NodeImage(id=image.findtext("ID"),
+ name=image.findtext("NAME"),
+ driver=self.connection.driver)
+
+ def _to_nodes(self, object):
+ computes = []
+ for element in object.findall("COMPUTE"):
+ compute_id = element.attrib["href"].partition("/compute/")[2]
+ compute = self.connection.request(("/compute/%s" % (compute_id))).object
+ computes.append(self._to_node(compute))
+
+ return computes
+
+ def _to_node(self, compute):
+ try:
+ state = self.NODE_STATE_MAP[compute.findtext("STATE")]
+ except KeyError:
+ state = NodeState.UNKNOWN
+
+ networks = []
+ for element in compute.findall("NIC"):
+ networks.append(element.attrib["ip"])
+
+ return Node(id=compute.findtext("ID"),
+ name=compute.findtext("NAME"),
+ state=state,
+ public_ip=networks,
+ private_ip=[],
+ driver=self.connection.driver)
+
+ def _xml_action(self, compute_id, action):
+ compute = ET.Element('COMPUTE')
+
+ compute_id = ET.SubElement(compute, 'ID')
+ compute_id.text = str(compute_id)
+
+ state = ET.SubElement(compute, 'STATE')
+ state.text = action
+
+ xml = ET.tostring(compute)
+ return xml
Modified: incubator/libcloud/trunk/libcloud/providers.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/providers.py?rev=941052&r1=941051&r2=941052&view=diff
==============================================================================
--- incubator/libcloud/trunk/libcloud/providers.py (original)
+++ incubator/libcloud/trunk/libcloud/providers.py Tue May 4 21:33:06 2010
@@ -49,6 +49,8 @@ DRIVERS = {
('libcloud.drivers.ec2', 'EucNodeDriver'),
Provider.IBM:
('libcloud.drivers.ibm', 'IBMNodeDriver'),
+ Provider.OPENNEBULA:
+ ('libcloud.drivers.opennebula', 'OpenNebulaNodeDriver'),
}
def get_driver(provider):
Modified: incubator/libcloud/trunk/libcloud/types.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/libcloud/types.py?rev=941052&r1=941051&r2=941052&view=diff
==============================================================================
--- incubator/libcloud/trunk/libcloud/types.py (original)
+++ incubator/libcloud/trunk/libcloud/types.py Tue May 4 21:33:06 2010
@@ -32,6 +32,7 @@ class Provider(object):
@cvar VCLOUD: vmware vCloud
@cvar RIMUHOSTING: RimuHosting.com
@cvar ECP: Enomaly
+ @cvar OPENNEBULA: OpenNebula.org
"""
DUMMY = 0
EC2 = 1 # deprecated name
@@ -51,6 +52,7 @@ class Provider(object):
EUCALYPTUS = 13
ECP = 14
IBM = 15
+ OPENNEBULA = 16
class NodeState(object):
"""
Added: incubator/libcloud/trunk/test/fixtures/opennebula/compute.xml
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/fixtures/opennebula/compute.xml?rev=941052&view=auto
==============================================================================
--- incubator/libcloud/trunk/test/fixtures/opennebula/compute.xml (added)
+++ incubator/libcloud/trunk/test/fixtures/opennebula/compute.xml Tue May 4 21:33:06 2010
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<COMPUTE>
+ <ID>5</ID>
+ <NAME>MyCompute</NAME>
+ <STATE>ACTIVE</STATE>
+ <STORAGE>
+ <DISK type="disk" href="devel.cloud.opennebula.org/storage/1" dev="sda1"/>
+ <DISK type="fs" size="512" format="ext3" dev="sda3"/>
+ <DISK type="swap" size="1024" dev="sda2"/>
+ </STORAGE>
+ <NETWORK>
+ <NIC href="devel.cloud.opennebula.org/network/3" ip="192.168.1.4"/>
+ </NETWORK>
+ <INSTANCE_TYPE>small</INSTANCE_TYPE>
+</COMPUTE>
\ No newline at end of file
Added: incubator/libcloud/trunk/test/fixtures/opennebula/computes.xml
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/fixtures/opennebula/computes.xml?rev=941052&view=auto
==============================================================================
--- incubator/libcloud/trunk/test/fixtures/opennebula/computes.xml (added)
+++ incubator/libcloud/trunk/test/fixtures/opennebula/computes.xml Tue May 4 21:33:06 2010
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<COMPUTES>
+ <COMPUTE href="devel.cloud.opennebula.org/compute/5"/>
+ <COMPUTE href="devel.cloud.opennebula.org/compute/15"/>
+</COMPUTES>
\ No newline at end of file
Added: incubator/libcloud/trunk/test/fixtures/opennebula/disk.xml
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/fixtures/opennebula/disk.xml?rev=941052&view=auto
==============================================================================
--- incubator/libcloud/trunk/test/fixtures/opennebula/disk.xml (added)
+++ incubator/libcloud/trunk/test/fixtures/opennebula/disk.xml Tue May 4 21:33:06 2010
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<DISK>
+ <ID>1</ID>
+ <NAME>UbuntuServer9.04-Contextualized</NAME>
+ <SIZE>5120</SIZE>
+ <URL>file:///Users/oneuser/ubuntu-server-9.04/ubuntu-server-9.04.img</URL>
+</DISK>
\ No newline at end of file
Added: incubator/libcloud/trunk/test/fixtures/opennebula/storage.xml
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/fixtures/opennebula/storage.xml?rev=941052&view=auto
==============================================================================
--- incubator/libcloud/trunk/test/fixtures/opennebula/storage.xml (added)
+++ incubator/libcloud/trunk/test/fixtures/opennebula/storage.xml Tue May 4 21:33:06 2010
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<STORAGE>
+ <DISK href="devel.cloud.opennebula.org/storage/1"/>
+ <DISK href="devel.cloud.opennebula.org/storage/8"/>
+</STORAGE>
\ No newline at end of file
Modified: incubator/libcloud/trunk/test/secrets.py-dist
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/secrets.py-dist?rev=941052&r1=941051&r2=941052&view=diff
==============================================================================
--- incubator/libcloud/trunk/test/secrets.py-dist (original)
+++ incubator/libcloud/trunk/test/secrets.py-dist Tue May 4 21:33:06 2010
@@ -50,3 +50,6 @@ ECP_PASSWORD=''
IBM_USER=''
IBM_SECRET=''
+
+OPENNEBULA_USER=''
+OPENNEBULA_KEY=''
Added: incubator/libcloud/trunk/test/test_opennebula.py
URL: http://svn.apache.org/viewvc/incubator/libcloud/trunk/test/test_opennebula.py?rev=941052&view=auto
==============================================================================
--- incubator/libcloud/trunk/test/test_opennebula.py (added)
+++ incubator/libcloud/trunk/test/test_opennebula.py Tue May 4 21:33:06 2010
@@ -0,0 +1,122 @@
+# Copyright 2002-2009, Distributed Systems Architecture Group, Universidad
+# Complutense de Madrid (dsa-research.org)
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# libcloud.org licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import sys
+import unittest
+
+from libcloud.drivers.opennebula import OpenNebulaNodeDriver
+from libcloud.base import Node, NodeImage, NodeSize
+
+from test import MockHttp, TestCaseMixin
+from test.file_fixtures import FileFixtures
+
+import httplib
+
+from secrets import OPENNEBULA_USER, OPENNEBULA_KEY
+
+class OpenNebulaTests(unittest.TestCase, TestCaseMixin):
+
+ def setUp(self):
+ OpenNebulaNodeDriver.connectionCls.conn_classes = (None, OpenNebulaMockHttp)
+ self.driver = OpenNebulaNodeDriver(OPENNEBULA_USER, OPENNEBULA_KEY)
+
+ def test_create_node(self):
+ image = NodeImage(id=1, name='UbuntuServer9.04-Contextualized', driver=self.driver)
+ size = NodeSize(1, 'small', None, None, None, None, driver=self.driver)
+ node = self.driver.create_node(name='MyCompute', image=image, size=size)
+ self.assertEqual(node.id, '5')
+ self.assertEqual(node.name, 'MyCompute')
+
+ def test_list_nodes(self):
+ nodes = self.driver.list_nodes()
+ self.assertEqual(len(nodes), 2)
+ node = nodes[0]
+ self.assertEqual(node.id, '5')
+ self.assertEqual(node.name, 'MyCompute')
+
+ def test_reboot_node(self):
+ node = Node(5, None, None, None, None, self.driver)
+ ret = self.driver.reboot_node(node)
+ self.assertTrue(ret)
+
+ def test_destroy_node(self):
+ node = Node(5, None, None, None, None, self.driver)
+ ret = self.driver.destroy_node(node)
+ self.assertTrue(ret)
+
+ def test_list_sizes(self):
+ sizes = self.driver.list_sizes()
+ self.assertEqual(len(sizes), 3)
+ self.assertTrue('small' in [ s.name for s in sizes])
+ self.assertTrue('medium' in [ s.name for s in sizes])
+ self.assertTrue('large' in [ s.name for s in sizes])
+
+ def test_list_images(self):
+ images = self.driver.list_images()
+ self.assertEqual(len(images), 2)
+ image = images[0]
+ self.assertEqual(image.id, '1')
+ self.assertEqual(image.name, 'UbuntuServer9.04-Contextualized')
+
+class OpenNebulaMockHttp(MockHttp):
+
+ fixtures = FileFixtures('opennebula')
+
+ def _compute(self, method, url, body, headers):
+ if method == 'GET':
+ body = self.fixtures.load('computes.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ if method == 'POST':
+ body = self.fixtures.load('compute.xml')
+ return (httplib.CREATED, body, {}, httplib.responses[httplib.CREATED])
+
+ def _storage(self, method, url, body, headers):
+ if method == 'GET':
+ body = self.fixtures.load('storage.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _compute_5(self, method, url, body, headers):
+ if method == 'GET':
+ body = self.fixtures.load('compute.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ if method == 'PUT':
+ body = ""
+ return (httplib.ACCEPTED, body, {}, httplib.responses[httplib.ACCEPTED])
+
+ if method == 'DELETE':
+ body = ""
+ return (httplib.NO_CONTENT, body, {}, httplib.responses[httplib.NO_CONTENT])
+
+ def _compute_15(self, method, url, body, headers):
+ if method == 'GET':
+ body = self.fixtures.load('compute.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _storage_1(self, method, url, body, headers):
+ if method == 'GET':
+ body = self.fixtures.load('disk.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+ def _storage_8(self, method, url, body, headers):
+ if method == 'GET':
+ body = self.fixtures.load('disk.xml')
+ return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+if __name__ == '__main__':
+ sys.exit(unittest.main())
\ No newline at end of file