Author: tomaz
Date: Mon Oct 17 00:35:15 2011
New Revision: 1184967
URL: http://svn.apache.org/viewvc?rev=1184967&view=rev
Log:
Add support for OpenNebula 3.0 API. This patch has been contributed by Hutson
Betts <hut606 at gmail dot com> and it's part of LIBCLOUD-120.
Modified:
libcloud/trunk/libcloud/compute/drivers/opennebula.py
libcloud/trunk/test/compute/test_opennebula.py
Modified: libcloud/trunk/libcloud/compute/drivers/opennebula.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/compute/drivers/opennebula.py?rev=1184967&r1=1184966&r2=1184967&view=diff
==============================================================================
--- libcloud/trunk/libcloud/compute/drivers/opennebula.py (original)
+++ libcloud/trunk/libcloud/compute/drivers/opennebula.py Mon Oct 17 00:35:15
2011
@@ -15,24 +15,28 @@
# 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.
-"""
+try:
+ import simplejson as json
+except ImportError:
+ import json
+
+from xml.etree import ElementTree as ET
from base64 import b64encode
import hashlib
-from xml.etree import ElementTree as ET
+import sys
+from libcloud.compute.base import NodeState, NodeDriver, Node, NodeLocation
from libcloud.common.base import ConnectionUserAndKey, Response
+from libcloud.compute.base import NodeImage, NodeSize
from libcloud.common.types import InvalidCredsError
from libcloud.compute.providers import Provider
-from libcloud.compute.types import NodeState
-from libcloud.compute.base import NodeDriver, Node, NodeLocation
-from libcloud.compute.base import NodeImage, NodeSize
+from libcloud.common.base import Response
API_HOST = ''
API_PORT = (4567, 443)
API_SECURE = True
+DEFAULT_API_VERSION = '3.0'
class OpenNebulaResponse(Response):
@@ -64,7 +68,8 @@ class OpenNebulaConnection(ConnectionUse
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)))
+ headers['Authorization'] = ("Basic %s" % b64encode("%s:%s" %
+ (self.user_id, pass_sha1)))
return headers
@@ -78,12 +83,33 @@ class OpenNebulaNodeDriver(NodeDriver):
name = 'OpenNebula'
NODE_STATE_MAP = {
- 'PENDING': NodeState.PENDING,
- 'ACTIVE': NodeState.RUNNING,
- 'DONE': NodeState.TERMINATED,
- 'STOPPED': NodeState.TERMINATED
+ 'pending': NodeState.PENDING,
+ 'hold': NodeState.PENDING,
+ 'prolog': NodeState.PENDING,
+ 'running': NodeState.RUNNING,
+ 'migrate': NodeState.PENDING,
+ 'epilog': NodeState.TERMINATED,
+ 'stopped': NodeState.TERMINATED,
+ 'suspended': NodeState.PENDING,
+ 'failed': NodeState.TERMINATED,
+ 'unknown': NodeState.UNKNOWN,
+ 'done': NodeState.TERMINATED,
}
+ def __new__(cls, key, secret=None, api_version=DEFAULT_API_VERSION,
+ **kwargs):
+ if cls is OpenNebulaNodeDriver:
+ if api_version == '1.4':
+ cls = OpenNebula_1_4_NodeDriver
+ elif api_version == '3.0':
+ cls = OpenNebula_3_0_NodeDriver
+ else:
+ raise NotImplementedError(
+ "No OpenNebulaNodeDriver found for API version %s" %
+ (api_version)
+ )
+ return super(OpenNebulaNodeDriver, cls).__new__(cls)
+
def list_sizes(self, location=None):
return [
NodeSize(id=1,
@@ -122,12 +148,16 @@ class OpenNebulaNodeDriver(NodeDriver):
compute_id = str(node.id)
url = '/compute/%s' % compute_id
- resp1 =
self.connection.request(url,method='PUT',data=self._xml_action(compute_id,'STOPPED'))
+ 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'))
+ resp2 = self.connection.request(url, method='PUT',
+ data=self._xml_action(compute_id,
+ 'RESUME'))
if resp2.status == 400:
return False
@@ -136,7 +166,7 @@ class OpenNebulaNodeDriver(NodeDriver):
def destroy_node(self, node):
url = '/compute/%s' % (str(node.id))
- resp = self.connection.request(url,method='DELETE')
+ resp = self.connection.request(url, method='DELETE')
return resp.status == 204
@@ -150,19 +180,9 @@ class OpenNebulaNodeDriver(NodeDriver):
name = ET.SubElement(compute, 'NAME')
name.text = kwargs['name']
- # """
- # Other extractable (but unused) information
- # """
- # 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
+ node = self.connection.request('/compute', method='POST',
+ data=xml).object
return self._to_node(node)
@@ -170,7 +190,8 @@ class OpenNebulaNodeDriver(NodeDriver):
images = []
for element in object.findall("DISK"):
image_id = element.attrib["href"].partition("/storage/")[2]
- image = self.connection.request(("/storage/%s" %
(image_id))).object
+ image = self.connection.request(("/storage/%s" % (
+ image_id))).object
images.append(self._to_image(image))
return images
@@ -178,31 +199,38 @@ class OpenNebulaNodeDriver(NodeDriver):
def _to_image(self, image):
return NodeImage(id=image.findtext("ID"),
name=image.findtext("NAME"),
- driver=self.connection.driver)
+ driver=self.connection.driver,
+ extra={"size": image.findtext("SIZE"),
+ "url": image.findtext("URL")})
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
+ compute = self.connection.request(("/compute/%s" % (
+ compute_id))).object
computes.append(self._to_node(compute))
return computes
+ def _extract_networks(self, compute):
+ networks = []
+
+ for element in compute.findall("NIC"):
+ ip = element.element.attrib.get('ip', None)
+
+ if ip is not None:
+ networks.append(ip)
+
+ return networks
+
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"):
- ip = element.element.attrib.get('ip', None)
-
- if ip is None:
- ip = element.findtext("IP")
-
- networks.append(ip)
+ networks = self._extract_networks(compute)
return Node(id=compute.findtext("ID"),
name=compute.findtext("NAME"),
@@ -222,3 +250,59 @@ class OpenNebulaNodeDriver(NodeDriver):
xml = ET.tostring(compute)
return xml
+
+
+class OpenNebula_1_4_NodeDriver(OpenNebulaNodeDriver):
+ pass
+
+
+class OpenNebula_3_0_NodeDriver(OpenNebulaNodeDriver):
+ def create_node(self, **kwargs):
+ """Create a new OpenNebula node
+
+ See L{NodeDriver.create_node} for more keyword args.
+ """
+ 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
+
+ disk = ET.SubElement(compute, 'DISK')
+ storage = ET.SubElement(disk, 'STORAGE', {'href': '/storage/%s' %
+ (str(kwargs['image'].id))})
+
+ 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("STORAGE"):
+ 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,
+ extra={"description": image.findtext("DESCRIPTION"),
+ "TYPE": image.findtext("TYPE"),
+ "size": image.findtext("SIZE"),
+ "fstype": image.findtext("FSTYPE", None)})
+
+ def _extract_networks(self, compute):
+ networks = []
+ for element in compute.findall("NIC"):
+ for ip in element.findall("IP"):
+ networks.append(ip)
+
+ return networks
Modified: libcloud/trunk/test/compute/test_opennebula.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/test/compute/test_opennebula.py?rev=1184967&r1=1184966&r2=1184967&view=diff
==============================================================================
--- libcloud/trunk/test/compute/test_opennebula.py (original)
+++ libcloud/trunk/test/compute/test_opennebula.py Mon Oct 17 00:35:15 2011
@@ -28,16 +28,20 @@ from test.file_fixtures import ComputeFi
from test.secrets import OPENNEBULA_PARAMS
-class OpenNebulaTests(unittest.TestCase, TestCaseMixin):
+
+class OpenNebula_1_4_Tests(unittest.TestCase, TestCaseMixin):
def setUp(self):
- OpenNebulaNodeDriver.connectionCls.conn_classes = (None,
OpenNebulaMockHttp)
- self.driver = OpenNebulaNodeDriver(*OPENNEBULA_PARAMS)
+ OpenNebulaNodeDriver.connectionCls.conn_classes = (None,
+ OpenNebulaMockHttp)
+ self.driver = OpenNebulaNodeDriver(*OPENNEBULA_PARAMS + ('1.4',))
def test_create_node(self):
- image = NodeImage(id=1, name='UbuntuServer9.04-Contextualized',
driver=self.driver)
+ 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)
+ node = self.driver.create_node(name='MyCompute', image=image,
+ size=size)
self.assertEqual(node.id, '5')
self.assertEqual(node.name, 'MyCompute')
@@ -61,9 +65,9 @@ class OpenNebulaTests(unittest.TestCase,
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])
+ 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()
@@ -72,6 +76,7 @@ class OpenNebulaTests(unittest.TestCase,
self.assertEqual(image.id, '1')
self.assertEqual(image.name, 'UbuntuServer9.04-Contextualized')
+
class OpenNebulaMockHttp(MockHttp):
fixtures = ComputeFileFixtures('opennebula')
@@ -83,7 +88,8 @@ class OpenNebulaMockHttp(MockHttp):
if method == 'POST':
body = self.fixtures.load('compute.xml')
- return (httplib.CREATED, body, {},
httplib.responses[httplib.CREATED])
+ return (httplib.CREATED, body, {},
+ httplib.responses[httplib.CREATED])
def _storage(self, method, url, body, headers):
if method == 'GET':
@@ -97,11 +103,13 @@ class OpenNebulaMockHttp(MockHttp):
if method == 'PUT':
body = ""
- return (httplib.ACCEPTED, body, {},
httplib.responses[httplib.ACCEPTED])
+ return (httplib.ACCEPTED, body, {},
+ httplib.responses[httplib.ACCEPTED])
if method == 'DELETE':
body = ""
- return (httplib.NO_CONTENT, body, {},
httplib.responses[httplib.NO_CONTENT])
+ return (httplib.NO_CONTENT, body, {},
+ httplib.responses[httplib.NO_CONTENT])
def _compute_15(self, method, url, body, headers):
if method == 'GET':