Skip to content

Commit

Permalink
Merge pull request #599 from grycap/devel
Browse files Browse the repository at this point in the history
Devel
  • Loading branch information
micafer authored May 4, 2018
2 parents 2496f14 + 4f634ba commit e1bb255
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 19 deletions.
13 changes: 8 additions & 5 deletions IM/InfrastructureList.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ def add_infrastructure(inf):
"""Add a new Infrastructure."""

with InfrastructureList._lock:
InfrastructureList.infrastructure_list[inf.id] = inf
if inf.id in InfrastructureList.infrastructure_list:
raise Exception("Trying to add an existing infrastructure ID.")
else:
InfrastructureList.infrastructure_list[inf.id] = inf

@staticmethod
def remove_inf(del_inf):
Expand Down Expand Up @@ -174,19 +177,19 @@ def _get_data_from_db(db_url, inf_id=None, auth=None):
if db.connect():
inf_list = {}
if inf_id:
res = db.select("select * from inf_list where id = '%s'" % inf_id)
res = db.select("select data from inf_list where id = '%s'" % inf_id)
else:
res = db.select("select * from inf_list where deleted = 0 order by id desc")
res = db.select("select data from inf_list where deleted = 0 order by id desc")
if len(res) > 0:
for elem in res:
# inf_id = elem[0]
# date = elem[1]
# deleted = elem[2]
try:
if auth:
inf = IM.InfrastructureInfo.InfrastructureInfo.deserialize_auth(elem[3])
inf = IM.InfrastructureInfo.InfrastructureInfo.deserialize_auth(elem[0])
else:
inf = IM.InfrastructureInfo.InfrastructureInfo.deserialize(elem[3])
inf = IM.InfrastructureInfo.InfrastructureInfo.deserialize(elem[0])
inf_list[inf.id] = inf
except:
InfrastructureList.logger.exception(
Expand Down
46 changes: 46 additions & 0 deletions IM/REST.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,23 @@ def RESTGetInfrastructureProperty(infid=None, prop=None):
bottle.abort(
403, "'outputs' infrastructure property is not valid in this infrastructure")
return format_output(res, default_type="application/json", field_name="outputs")
elif prop == "data":
accept = get_media_type('Accept')
if accept and "application/json" not in accept and "*/*" not in accept and "application/*" not in accept:
return return_error(415, "Unsupported Accept Media Types: %s" % accept)

delete = False
if "delete" in bottle.request.params.keys():
str_delete = bottle.request.params.get("delete").lower()
if str_delete in ['yes', 'true', '1']:
delete = True
elif str_delete in ['no', 'false', '0']:
delete = False
else:
return return_error(400, "Incorrect value in delete parameter")

data = InfrastructureManager.ExportInfrastructure(infid, delete, auth)
return format_output(data, default_type="application/json", field_name="data")
else:
return return_error(404, "Incorrect infrastructure property")

Expand Down Expand Up @@ -476,6 +493,35 @@ def RESTCreateInfrastructure():
return return_error(400, "Error Creating Inf.: " + str(ex))


@app.route('/infrastructures', method='PUT')
def RESTImportInfrastructure():
try:
auth = get_auth_header()
except:
return return_error(401, "No authentication data provided")

try:
content_type = get_media_type('Content-Type')
data = bottle.request.body.read().decode("utf-8")

if content_type:
if "application/json" not in content_type:
return return_error(415, "Unsupported Media Type %s" % content_type)

new_id = InfrastructureManager.ImportInfrastructure(data, auth)

bottle.response.headers['InfID'] = new_id
bottle.response.content_type = "text/uri-list"
res = get_full_url('/infrastructures/%s' % new_id)

return format_output(res, "text/uri-list", "uri")
except InvaliddUserException as ex:
return return_error(401, "Error Impporting Inf.: " + str(ex))
except Exception as ex:
logger.exception("Error Impporting Inf.")
return return_error(400, "Error Impporting Inf.: " + str(ex))


@app.route('/infrastructures/:infid/vms/:vmid', method='GET')
def RESTGetVMInfo(infid=None, vmid=None):
try:
Expand Down
34 changes: 30 additions & 4 deletions IM/VirtualMachine.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,36 @@ def setIps(self, public_ips, private_ips, remove_old=False):
self.info.networks.append(public_net)
num_net = self.getNumNetworkIfaces()

for public_ip in public_ips:
if public_ip not in private_ips:
vm_system.setValue('net_interface.%s.ip' % num_net, str(public_ip))
vm_system.setValue('net_interface.%s.connection' % num_net, public_net.id)
real_public_ips = [public_ip for public_ip in public_ips if public_ip not in private_ips]
if real_public_ips:
vm_system.setValue('net_interface.%s.connection' % num_net, public_net.id)
if len(real_public_ips) > 1:
self.logger.warn("Node with more that one public IP!")
self.logger.debug(real_public_ips)
if len(real_public_ips) == 2:
ip1 = IPAddress(real_public_ips[0])
ip2 = IPAddress(real_public_ips[1])
if ip1.version != ip2.version:
self.logger.info("It seems that there are one IPv4 and other IPv6. Get the IPv4 one.")
if ip1.version == 4:
vm_system.setValue('net_interface.%s.ip' % num_net, str(real_public_ips[0]))
vm_system.setValue('net_interface.%s.ipv6' % num_net, str(real_public_ips[1]))
else:
vm_system.setValue('net_interface.%s.ip' % num_net, str(real_public_ips[1]))
vm_system.setValue('net_interface.%s.ipv6' % num_net, str(real_public_ips[0]))
else:
self.logger.info("It seems that both are from the same version first one will be used")
vm_system.setValue('net_interface.%s.ip' % num_net, str(real_public_ips[0]))
else:
self.logger.info("It seems that there are more that 2 last ones will be used")
for ip in real_public_ips:
if IPAddress(ip).version == 4:
vm_system.setValue('net_interface.%s.ip' % num_net, str(ip))
else:
vm_system.setValue('net_interface.%s.ipv6' % num_net, str(ip))
else:
# The usual case
vm_system.setValue('net_interface.%s.ip' % num_net, str(real_public_ips[0]))

if private_ips:
private_net_map = {}
Expand Down
32 changes: 26 additions & 6 deletions doc/source/REST.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ Next tables summaries the resources and the HTTP methods available.
| **POST** | | **Create** a new infrastructure | | **Create** a new virtual machine | | **Alter** VM properties based on |
| | | based on the RADL posted | | based on the RADL posted. | | then RADL posted |
+-------------+------------------------------------+------------------------------------+-------------------------------------------+
| **PUT** | | | | **Modify** the virtual machine based on |
| | | | | the RADL posted. |
| **PUT** | | | **Import** an infrastructure | | **Modify** the virtual machine based on |
| | | | from another IM instance | | the RADL posted. |
+-------------+------------------------------------+------------------------------------+-------------------------------------------+
| **DELETE** | | | **Undeploy** all the virtual | | **Undeploy** the virtual machine. |
| | | | machines in the infrastructure. | |
Expand All @@ -39,8 +39,8 @@ Next tables summaries the resources and the HTTP methods available.
+=============+=====================================================+====================================================+
| **GET** | | **Get** the specified property ``property_name`` | | **Get** the specified property ``property_name`` |
| | | associated to the machine ``vmId`` in ``infId``. | | associated to the infrastructure ``infId``. |
| | | It has one special property: ``contmsg``. | | It has three properties: ``contmsg``, ``radl``, |
| | | | ``state`` and ``outputs``. |
| | | It has one special property: ``contmsg``. | | It has five properties: ``contmsg``, ``radl``, |
| | | | ``state``, ``outputs`` and ``data``. |
+-------------+-----------------------------------------------------+----------------------------------------------------+

+-------------+-----------------------------------------------+------------------------------------------------+
Expand Down Expand Up @@ -104,6 +104,23 @@ POST ``http://imserver.com/infrastructures``
"uri" : "http://server.com:8800/infrastructures/inf_id
}

PUT ``http://imserver.com/infrastructures``
:body: ``JSON data of the infrastructure``
:body Content-type: application/json
:Response Content-type: text/uri-list
:ok response: 200 OK
:fail response: 401, 400, 415

Take control of the infrastructure serialized in in the body and return
the ID associated in the server. (See GET /infrastructures/<infId>/data).

If success, it is returned the URI of the new infrastructure.
The result is JSON format has the following format::

{
"uri" : "http://server.com:8800/infrastructures/inf_id
}

GET ``http://imserver.com/infrastructures/<infId>``
:Response Content-type: text/uri-list or application/json
:ok response: 200 OK
Expand All @@ -122,7 +139,7 @@ GET ``http://imserver.com/infrastructures/<infId>``
GET ``http://imserver.com/infrastructures/<infId>/<property_name>``
:Response Content-type: text/plain or application/json
:ok response: 200 OK
:input fields: ``headeronly`` (optional)
:input fields: ``headeronly`` (optional), ``delete`` (optional)
:fail response: 401, 404, 400, 403

Return property ``property_name`` associated to the infrastructure with ID ``infId``. It has three properties:
Expand All @@ -131,6 +148,9 @@ GET ``http://imserver.com/infrastructures/<infId>/<property_name>``
'true' or '1' only the initial part of the infrastructure contextualization log will be
returned (without any VM contextualization log).
:``radl``: a string with the original specified RADL of the infrastructure.
:``data``: a string with the JSOMN serialized data of the infrastructure. In case of ``delete`` flag is set to 'yes',
'true' or '1' the data not only will be exported but also the infrastructure will be set deleted
(the virtual infrastructure will not be modified).
:``state``: a JSON object with two elements:

:``state``: a string with the aggregated state of the infrastructure.
Expand All @@ -139,7 +159,7 @@ GET ``http://imserver.com/infrastructures/<infId>/<property_name>``
The result is JSON format has the following format::
{
["radl"|"state"|"contmsg"|"outputs"]: <property_value>
["radl"|"state"|"contmsg"|"outputs"|"data"]: <property_value>
}

POST ``http://imserver.com/infrastructures/<infId>``
Expand Down
2 changes: 1 addition & 1 deletion test/integration/TestIM.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ def test_40_export_import(self):
Test ExportInfrastructure and ImportInfrastructure functions
"""
(success, res) = self.server.ExportInfrastructure(
self.inf_id, False, self.auth_data)
self.inf_id, True, self.auth_data)
self.assertTrue(
success, msg="ERROR calling ExportInfrastructure: " + str(res))

Expand Down
30 changes: 30 additions & 0 deletions test/unit/REST.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
RESTStopVM,
RESTGeVersion,
RESTCreateDiskSnapshot,
RESTImportInfrastructure,
return_error,
format_output)

Expand Down Expand Up @@ -702,6 +703,35 @@ def test_CreateDiskSnapshot(self, bottle_request, CreateDiskSnapshot):
res = RESTCreateDiskSnapshot("1", "1", 0)
self.assertEqual(res, "Error creating snapshot: Invalid VM ID")

@patch("IM.InfrastructureManager.InfrastructureManager.ExportInfrastructure")
@patch("bottle.request")
def test_ExportInfrastructure(self, bottle_request, ExportInfrastructure):
"""Test REST StopInfrastructure."""
bottle_request.return_value = MagicMock()
bottle_request.headers = {"AUTHORIZATION": ("type = InfrastructureManager; username = user; password = pass\n"
"id = one; type = OpenNebula; host = onedock.i3m.upv.es:2633; "
"username = user; password = pass")}

ExportInfrastructure.return_value = "strinf"

res = RESTGetInfrastructureProperty("1", "data")
self.assertEqual(res, '{"data": "strinf"}')

@patch("IM.InfrastructureManager.InfrastructureManager.ImportInfrastructure")
@patch("bottle.request")
def test_ImportInfrastructure(self, bottle_request, ImportInfrastructure):
"""Test REST StopInfrastructure."""
bottle_request.return_value = MagicMock()
bottle_request.headers = {"AUTHORIZATION": ("type = InfrastructureManager; username = user; password = pass\n"
"id = one; type = OpenNebula; host = onedock.i3m.upv.es:2633; "
"username = user; password = pass")}
bottle_request.environ = {'HTTP_HOST': 'imserver.com'}

ImportInfrastructure.return_value = "newid"

res = RESTImportInfrastructure()
self.assertEqual(res, "http://imserver.com/infrastructures/newid")

@patch("IM.REST.get_media_type")
def test_return_error(self, get_media_type):
get_media_type.return_value = ["application/json"]
Expand Down
21 changes: 18 additions & 3 deletions test/unit/connectors/OpenStack.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ def test_30_updateVMInfo(self, get_driver):
node = MagicMock()
node.id = "1"
node.state = "running"
node.extra = {'flavorId': 'small', 'addresses': {'os-lan': [{'addr': '10.0.0.1',
'OS-EXT-IPS:type': 'fixed'}]}}
node.extra = {'flavorId': 'small',
'addresses': {'os-lan': [{'addr': '10.0.0.1', 'OS-EXT-IPS:type': 'fixed'}]}}
node.public_ips = []
node.private_ips = ['10.0.0.1']
node.driver = driver
Expand Down Expand Up @@ -268,7 +268,7 @@ def test_30_updateVMInfo(self, get_driver):
self.assertEquals(vm.info.systems[0].getValue("net_interface.1.ip"), "10.0.0.1")

# In this case the Node has the float ip assigned
node.public_ips = ['8.8.8.8']
# node.public_ips = ['8.8.8.8']
floating_ip.node_id = node.id
pool.list_floating_ips.return_value = [floating_ip]
driver.ex_list_floating_ip_pools.return_value = [pool]
Expand All @@ -286,6 +286,21 @@ def test_30_updateVMInfo(self, get_driver):
self.assertEquals(vm.info.systems[0].getValue("net_interface.0.ip"), "8.8.8.8")

self.assertTrue(success, msg="ERROR: updating VM info.")

# the node has a IPv6 IP
node = MagicMock()
node.id = "2"
node.state = "running"
node.extra = {'flavorId': 'small'}
node.public_ips = ['8.8.8.8', '2001:630:12:581:f816:3eff:fe92:2146']
node.private_ips = ['10.0.0.1']
node.driver = driver
driver.ex_get_node_details.return_value = node

success, vm = ost_cloud.updateVMInfo(vm, auth)
self.assertTrue(success, msg="ERROR: updating VM info.")
self.assertEquals(vm.info.systems[0].getValue("net_interface.0.ip"), "8.8.8.8")
self.assertEquals(vm.info.systems[0].getValue("net_interface.0.ipv6"), "2001:630:12:581:f816:3eff:fe92:2146")
self.assertNotIn("ERROR", self.log.getvalue(), msg="ERROR found in log: %s" % self.log.getvalue())

@patch('libcloud.compute.drivers.openstack.OpenStackNodeDriver')
Expand Down

0 comments on commit e1bb255

Please sign in to comment.