Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ dist/*apache-libcloud*
dist/*apache_libcloud*
_build/
apache_libcloud.egg-info/
.project
.pydevproject
.settings
178 changes: 178 additions & 0 deletions libcloud/compute/drivers/cloudstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@
'can_use_for_deploy': {
'key_name': 'canusefordeploy',
'transform_func': str
},
'gateway': {
'key_name': 'gateway',
'transform_func': str
},
'netmask': {
'key_name': 'netmask',
'transform_func': str
},
'vpc_id': {
'key_name': 'vpcid',
'transform_func': str
},
'project_id': {
'key_name': 'projectid',
'transform_func': str
}
},
'node': {
Expand Down Expand Up @@ -453,6 +469,31 @@ def __repr__(self):
self.networkofferingid, self.zoneid, self.driver.name))


class CloudStackNetworkOffering(object):
"""
Class representing a CloudStack Network Offering.
"""

def __init__(self, name, display_text, guest_ip_type, id,
service_offering_id,
forvpc, driver, extra=None):
self.displaytext = display_text
self.name = name
self.guestiptype = guest_ip_type
self.id = id
self.serviceofferingid = service_offering_id
self.forvpc = forvpc
self.driver = driver
self.extra = extra or {}

def __repr__(self):
return (('<CloudStackNetworkOffering: id=%s, name=%s, displaytext=%s, '
'guestiptype=%s, serviceofferingid=%s, forvpc=%s, driver%s>')
% (self.id, self.name, self.displaytext,
self.guestiptype, self.serviceofferingid, self.forvpc,
self.driver.name))


class CloudStackProject(object):
"""
Class representing a CloudStack Project.
Expand Down Expand Up @@ -880,6 +921,143 @@ def ex_list_networks(self):

return networks

def ex_list_network_offerings(self):
"""
List the available network offerings

:rtype ``list`` of :class:`CloudStackNetworkOffering`
"""

res = self._sync_request(command='listNetworkOfferings',
method='GET')
netoffers = res.get('networkoffering', [])

networkofferings = []

for netoffer in netoffers:

networkofferings.append(CloudStackNetworkOffering(
netoffer['name'],
netoffer['displaytext'],
netoffer['guestiptype'],
netoffer['id'],
netoffer['serviceofferingid'],
netoffer['forvpc'],
self))

return networkofferings

def ex_create_network(self, display_text, name, network_offering,
location, gateway=None, netmask=None,
network_domain=None, vpc_id=None, project_id=None):

"""

Creates a Network, only available in advanced zones.

:param display_text: the display text of the network
:type display_text: ``str``

:param name: the name of the network
:type name: ``str``

:param network_offering: the network offering id
:type network_offering: :class:'CloudStackNetworkOffering`

:param location: Zone
:type location: :class:`NodeLocation`

:param gateway: Optional, the Gateway of this network
:type gateway: ``str``

:param netmask: Optional, the netmask of this network
:type netmask: ``str``

:param network_domain: Optional, the DNS domain of the network
:type network_domain: ``str``

:param vpc_id: Optional, the VPC id the network belongs to
:type vpc_id: ``str``

:param project_id: Optional, the project id the networks belongs to
:type project_id: ``str``

:rtype: :class:`CloudStackNetwork`

"""

extra_map = RESOURCE_EXTRA_ATTRIBUTES_MAP['network']

args = {
'displaytext': display_text,
'name': name,
'networkofferingid': network_offering.id,
'zoneid': location.id,
}

if gateway is not None:
args['gateway'] = gateway

if netmask is not None:
args['netmask'] = netmask

if network_domain is not None:
args['networkdomain'] = network_domain

if vpc_id is not None:
args['vpcid'] = vpc_id

if project_id is not None:
args['projectid'] = project_id

""" Cloudstack allows for duplicate network names,
this should be handled in the code leveraging libcloud
As there could be use cases for duplicate names.
e.g. management from ROOT level"""

# for net in self.ex_list_networks():
# if name == net.name:
# raise LibcloudError('This network name already exists')

result = self._sync_request(command='createNetwork',
params=args,
method='GET')

result = result['network']
extra = self._get_extra_dict(result, extra_map)

network = CloudStackNetwork(display_text,
name,
network_offering.id,
result['id'],
location.id,
self,
extra=extra)

return network

def ex_delete_network(self, network, force=None):
"""

Deletes a Network, only available in advanced zones.

:param network: The network
:type network: :class: 'CloudStackNetwork'

:param force: Force deletion of the network?
:type force: ``bool``

:rtype: ``bool``

"""

args = {'id': network.id, 'forced': force}

self._async_request(command='deleteNetwork',
params=args,
method='GET')
return True

def ex_list_projects(self):
"""
List the available projects
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "createnetworkresponse" : { "network" : {"id":"a804d341-996e-4d9a-b2b0-226c648dc6e3","name":"test","displaytext":"test","broadcastdomaintype":"Lswitch","traffictype":"Guest","gateway":"10.1.1.1","netmask":"255.255.255.0","cidr":"10.1.1.0/24","zoneid":"2","zonename":"BETA-SBP-DC-1","networkofferingid":"c348cabe-0208-49e0-91ad-32b88c55fd8c","networkofferingname":"SourceNatNiciraNvpNetwork","networkofferingdisplaytext":"Offering for a Nicira Nvp isolated network with SourceNat","networkofferingconservemode":true,"networkofferingavailability":"Optional","issystem":false,"state":"Allocated","related":"a804d341-996e-4d9a-b2b0-226c648dc6e3","dns1":"8.8.8.8","dns2":"8.8.8.4","type":"Isolated","acltype":"Account","account":"rkuipers_admin","projectid":"d5f1209d-3a28-4dfb-8cc1-884e5d5e1d56","domainid":"4b6e626c-9d50-4480-bf77-daae632c7ffd","domain":"rkuipers","service":[{"name":"Firewall","capability":[{"name":"SupportedProtocols","value":"tcp,udp,icmp","canchooseservicecapability":false},{"name":"SupportedTrafficDirection","value":"ingress, egress","canchooseservicecapability":false},{"name":"MultipleIps","value":"true","canchooseservicecapability":false},{"name":"SupportedEgressProtocols","value":"tcp,udp,icmp, all","canchooseservicecapability":false},{"name":"TrafficStatistics","value":"per public ip","canchooseservicecapability":false}]},{"name":"StaticNat"},{"name":"Lb","capability":[{"name":"LbSchemes","value":"Public","canchooseservicecapability":false},{"name":"SupportedStickinessMethods","value":"[{\"methodname\":\"LbCookie\",\"paramlist\":[{\"paramname\":\"cookie-name\",\"required\":false,\"isflag\":false,\"description\":\" \"},{\"paramname\":\"mode\",\"required\":false,\"isflag\":false,\"description\":\" \"},{\"paramname\":\"nocache\",\"required\":false,\"isflag\":true,\"description\":\" \"},{\"paramname\":\"indirect\",\"required\":false,\"isflag\":true,\"description\":\" \"},{\"paramname\":\"postonly\",\"required\":false,\"isflag\":true,\"description\":\" \"},{\"paramname\":\"domain\",\"required\":false,\"isflag\":false,\"description\":\" \"}],\"description\":\"This is loadbalancer cookie based stickiness method.\"},{\"methodname\":\"AppCookie\",\"paramlist\":[{\"paramname\":\"cookie-name\",\"required\":false,\"isflag\":false,\"description\":\" \"},{\"paramname\":\"length\",\"required\":false,\"isflag\":false,\"description\":\" \"},{\"paramname\":\"holdtime\",\"required\":false,\"isflag\":false,\"description\":\" \"},{\"paramname\":\"request-learn\",\"required\":false,\"isflag\":true,\"description\":\" \"},{\"paramname\":\"prefix\",\"required\":false,\"isflag\":true,\"description\":\" \"},{\"paramname\":\"mode\",\"required\":false,\"isflag\":false,\"description\":\" \"}],\"description\":\"This is App session based sticky method. Define session stickiness on an existing application cookie. It can be used only for a specific http traffic\"},{\"methodname\":\"SourceBased\",\"paramlist\":[{\"paramname\":\"tablesize\",\"required\":false,\"isflag\":false,\"description\":\" \"},{\"paramname\":\"expire\",\"required\":false,\"isflag\":false,\"description\":\" \"}],\"description\":\"This is source based Stickiness method, it can be used for any type of protocol.\"}]","canchooseservicecapability":false},{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"SupportedLbAlgorithms","value":"roundrobin,leastconn,source","canchooseservicecapability":false},{"name":"SupportedProtocols","value":"tcp, udp","canchooseservicecapability":false}]},{"name":"SourceNat","capability":[{"name":"RedundantRouter","value":"true","canchooseservicecapability":false},{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false}]},{"name":"Dns","capability":[{"name":"AllowDnsSuffixModification","value":"true","canchooseservicecapability":false}]},{"name":"Connectivity"},{"name":"Vpn","capability":[{"name":"SupportedVpnTypes","value":"pptp,l2tp,ipsec","canchooseservicecapability":false},{"name":"VpnTypes","value":"removeaccessvpn","canchooseservicecapability":false}]},{"name":"Dhcp","capability":[{"name":"DhcpAccrossMultipleSubnets","value":"true","canchooseservicecapability":false}]},{"name":"UserData"},{"name":"PortForwarding"}],"networkdomain":"rkuipers.local","physicalnetworkid":"e48527a6-6882-4c5f-bce9-c02ecd5ef8c1","restartrequired":false,"specifyipranges":false,"vpcid":"22e8388c-21bf-4b84-8f20-e92a7f550898","canusefordeploy":true,"ispersistent":false,"tags":[],"displaynetwork":true} } }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "deletenetworkresponse" : {"jobid":"deleteNetwork"} }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "listnetworkofferingsresponse" : { "count":2 ,"networkoffering" : [ {"id":"c348cabe-0208-49e0-91ad-32b88c55fd8c","name":"SourceNatNiciraNvpNetwork","displaytext":"Offering for a Nicira Nvp isolated network with SourceNat","tags":"BETA-SBP-DC-1-pSTT","traffictype":"Guest","isdefault":true,"specifyvlan":false,"conservemode":true,"specifyipranges":false,"availability":"Optional","networkrate":-1,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"01f93707-3a35-44a6-84e9-ea767287a6b2","service":[{"name":"Firewall","provider":[{"name":"VirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"false","canchooseservicecapability":false}]},{"name":"Lb","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"SourceNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Dns","provider":[{"name":"VirtualRouter"}]},{"name":"Connectivity","provider":[{"name":"NiciraNvp"}]},{"name":"Vpn","provider":[{"name":"VirtualRouter"}]},{"name":"Dhcp","provider":[{"name":"VirtualRouter"}]},{"name":"UserData","provider":[{"name":"VirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VirtualRouter"}]}],"forvpc":false,"ispersistent":false,"egressdefaultpolicy":false}, {"id":"7c09e208-2af5-43d6-9f0b-53868ef788ea","name":"OAT offering for OAT purposes","displaytext":"OAT offering for OAT purposes","tags":"BETA-SBP-DC-1-pSTT","traffictype":"Guest","isdefault":false,"specifyvlan":false,"conservemode":true,"specifyipranges":false,"availability":"Optional","networkrate":-1,"state":"Enabled","guestiptype":"Isolated","serviceofferingid":"01f93707-3a35-44a6-84e9-ea767287a6b2","service":[{"name":"Firewall","provider":[{"name":"VirtualRouter"}]},{"name":"StaticNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"ElasticIp","value":"false","canchooseservicecapability":false},{"name":"AssociatePublicIP","value":"false","canchooseservicecapability":false}]},{"name":"Lb","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedLBIsolation","value":"dedicated","canchooseservicecapability":false},{"name":"ElasticLb","value":"false","canchooseservicecapability":false},{"name":"InlineMode","value":"false","canchooseservicecapability":false}]},{"name":"SourceNat","provider":[{"name":"VirtualRouter"}],"capability":[{"name":"SupportedSourceNatTypes","value":"peraccount","canchooseservicecapability":false},{"name":"RedundantRouter","value":"false","canchooseservicecapability":false}]},{"name":"Dns","provider":[{"name":"VirtualRouter"}]},{"name":"Connectivity","provider":[{"name":"NiciraNvp"}]},{"name":"Vpn","provider":[{"name":"VirtualRouter"}]},{"name":"Dhcp","provider":[{"name":"VirtualRouter"}]},{"name":"UserData","provider":[{"name":"VirtualRouter"}]},{"name":"PortForwarding","provider":[{"name":"VirtualRouter"}]}],"forvpc":false,"ispersistent":false,"egressdefaultpolicy":true,"maxconnections":4096} ] } }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "queryasyncjobresultresponse" : {"accountid":"02c9bf08-6f36-44b1-a57f-df0708f90de4","userid":"6ef2b921-4ecf-4651-8188-f9868db73e73","cmd":"org.apache.cloudstack.api.command.user.network.DeleteNetworkCmd","jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"success":true},"created":"2014-06-11T10:09:00+0200","jobid":"65789636-d2c8-484c-9d13-47ad3de384ed"} }
57 changes: 57 additions & 0 deletions libcloud/test/compute/test_cloudstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,63 @@ def test_ex_list_networks(self):
fixture_networks[i]['networkofferingid'])
self.assertEqual(network.zoneid, fixture_networks[i]['zoneid'])

def test_ex_list_network_offerings(self):
_, fixture = CloudStackMockHttp()._load_fixture(
'listNetworkOfferings_default.json')
fixture_networkoffers = \
fixture['listnetworkofferingsresponse']['networkoffering']

networkoffers = self.driver.ex_list_network_offerings()

for i, networkoffer in enumerate(networkoffers):
self.assertEqual(networkoffer.id, fixture_networkoffers[i]['id'])
self.assertEqual(networkoffer.name,
fixture_networkoffers[i]['name'])
self.assertEqual(networkoffer.displaytext,
fixture_networkoffers[i]['displaytext'])
self.assertEqual(networkoffer.forvpc,
fixture_networkoffers[i]['forvpc'])
self.assertEqual(networkoffer.guestiptype,
fixture_networkoffers[i]['guestiptype'])
self.assertEqual(networkoffer.serviceofferingid,
fixture_networkoffers[i]['serviceofferingid'])

def test_ex_create_network(self):
_, fixture = CloudStackMockHttp()._load_fixture(
'createNetwork_default.json')

fixture_network = fixture['createnetworkresponse']['network']

netoffer = self.driver.ex_list_network_offerings()[0]
location = self.driver.list_locations()[0]
network = self.driver.ex_create_network(display_text='test',
name='test',
network_offering=netoffer,
location=location,
gateway='10.1.1.1',
netmask='255.255.255.0',
network_domain='cloud.local',
vpc_id="2",
project_id="2")

self.assertEqual(network.name, fixture_network['name'])
self.assertEqual(network.displaytext, fixture_network['displaytext'])
self.assertEqual(network.id, fixture_network['id'])
self.assertEqual(network.extra['gateway'], fixture_network['gateway'])
self.assertEqual(network.extra['netmask'], fixture_network['netmask'])
self.assertEqual(network.networkofferingid,
fixture_network['networkofferingid'])
self.assertEqual(network.extra['vpc_id'], fixture_network['vpcid'])
self.assertEqual(network.extra['project_id'],
fixture_network['projectid'])

def test_ex_delete_network(self):

network = self.driver.ex_list_networks()[0]

result = self.driver.ex_delete_network(network=network)
self.assertTrue(result)

def test_ex_list_projects(self):
_, fixture = CloudStackMockHttp()._load_fixture(
'listProjects_default.json')
Expand Down