summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--newton/newton/requests/tests/test_flavor.py4
-rw-r--r--newton/newton/requests/urls.py2
-rw-r--r--ocata/ocata/requests/urls.py2
-rw-r--r--ocata/ocata/requests/urlsV1.py2
-rw-r--r--ocata/ocata/urls.py6
-rw-r--r--pike/pike/requests/urls.py2
-rw-r--r--pike/pike/requests/urlsV1.py2
-rw-r--r--pike/pike/urls.py6
-rw-r--r--share/common/msapi/extsys.py9
-rw-r--r--share/newton_base/openoapi/flavor.py2
-rw-r--r--share/newton_base/openoapi/network.py8
-rw-r--r--share/newton_base/openoapi/server.py119
-rw-r--r--share/newton_base/openoapi/subnet.py12
-rw-r--r--share/newton_base/openoapi/tenants.py4
-rw-r--r--share/newton_base/openoapi/vport.py11
-rw-r--r--share/newton_base/registration/registration.py2
-rw-r--r--share/newton_base/util.py30
-rw-r--r--windriver/titanium_cloud/requests/urls.py2
18 files changed, 152 insertions, 73 deletions
diff --git a/newton/newton/requests/tests/test_flavor.py b/newton/newton/requests/tests/test_flavor.py
index 4a9df9c5..ef7ec86a 100644
--- a/newton/newton/requests/tests/test_flavor.py
+++ b/newton/newton/requests/tests/test_flavor.py
@@ -174,7 +174,7 @@ class TestFlavorsNewton(unittest.TestCase, AbstractTestResource):
"content": self.MOCK_GET_RESOURCES_RESPONSE},
"post": {
"content": self.MOCK_POST_RESOURCE_RESPONSE,
- "status_code": status.HTTP_202_ACCEPTED,
+ "status_code": status.HTTP_200_OK,
}
}
),
@@ -190,7 +190,7 @@ class TestFlavorsNewton(unittest.TestCase, AbstractTestResource):
HTTP_X_AUTH_TOKEN=mock_info.MOCK_TOKEN_ID)
context = response.json()
- self.assertEquals(status.HTTP_202_ACCEPTED,
+ self.assertEquals(status.HTTP_200_OK,
response.status_code)
self.assertIsNotNone(context['id'])
self.assertEqual(1, context['returnCode'])
diff --git a/newton/newton/requests/urls.py b/newton/newton/requests/urls.py
index ff1a7fb2..c6c9f5ed 100644
--- a/newton/newton/requests/urls.py
+++ b/newton/newton/requests/urls.py
@@ -34,6 +34,8 @@ urlpatterns = [
image.Images.as_view()),
url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
volume.Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.ServerAction.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
server.Servers.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
diff --git a/ocata/ocata/requests/urls.py b/ocata/ocata/requests/urls.py
index 435ff239..73cc1543 100644
--- a/ocata/ocata/requests/urls.py
+++ b/ocata/ocata/requests/urls.py
@@ -34,6 +34,8 @@ urlpatterns = [
image.Images.as_view()),
url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
volume.Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.ServerAction.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
server.Servers.as_view()),
url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',
diff --git a/ocata/ocata/requests/urlsV1.py b/ocata/ocata/requests/urlsV1.py
index 4a8134aa..80b0a7e9 100644
--- a/ocata/ocata/requests/urlsV1.py
+++ b/ocata/ocata/requests/urlsV1.py
@@ -34,6 +34,8 @@ urlpatterns = [
image.APIv1Images.as_view()),
url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
volume.APIv1Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.APIv1ServerAction.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
server.APIv1Servers.as_view()),
url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',
diff --git a/ocata/ocata/urls.py b/ocata/ocata/urls.py
index d1601d6c..a055af4a 100644
--- a/ocata/ocata/urls.py
+++ b/ocata/ocata/urls.py
@@ -46,11 +46,11 @@ urlpatterns = [
vesagent_ctrl.VesAgentCtrl.as_view()),
# API upgrading
- url(r'^api/multicloud-ocata/v1/(?P<vimid>[0-9a-zA-Z_-]+)/registry$',
+ url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/registry$',
registration.RegistryV1.as_view()),
- url(r'^api/multicloud-ocata/v1/(?P<vimid>[0-9a-zA-Z_-]+)$',
+ url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)$',
registration.RegistryV1.as_view()),
- url(r'^api/multicloud-ocata/v1/(?P<vimid>[0-9a-zA-Z_-]+)/exten',
+ url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/exten',
include('ocata.extensions.urlsV1')),
url(r'^api/multicloud-ocata/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/',
include('ocata.proxy.urlsV1')),
diff --git a/pike/pike/requests/urls.py b/pike/pike/requests/urls.py
index 2420c08d..64ec70db 100644
--- a/pike/pike/requests/urls.py
+++ b/pike/pike/requests/urls.py
@@ -34,6 +34,8 @@ urlpatterns = [
image.Images.as_view()),
url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
volume.Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.ServerAction.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
server.Servers.as_view()),
url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',
diff --git a/pike/pike/requests/urlsV1.py b/pike/pike/requests/urlsV1.py
index 4a8134aa..80b0a7e9 100644
--- a/pike/pike/requests/urlsV1.py
+++ b/pike/pike/requests/urlsV1.py
@@ -34,6 +34,8 @@ urlpatterns = [
image.APIv1Images.as_view()),
url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
volume.APIv1Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.APIv1ServerAction.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
server.APIv1Servers.as_view()),
url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',
diff --git a/pike/pike/urls.py b/pike/pike/urls.py
index 264b4a77..8b8eb3be 100644
--- a/pike/pike/urls.py
+++ b/pike/pike/urls.py
@@ -39,11 +39,11 @@ urlpatterns = [
url(r'^api/multicloud-pike/v0/(?P<vimid>[0-9a-zA-Z_-]+)/infra_workload/(?P<requri>[0-9a-zA-Z_-]*)/?$',
infra_workload.InfraWorkload.as_view()),
# API upgrading
- url(r'^api/multicloud-pike/v1/(?P<vimid>[0-9a-zA-Z_-]+)/registry$',
+ url(r'^api/multicloud-pike/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/registry$',
registration.RegistryV1.as_view()),
- url(r'^api/multicloud-pike/v1/(?P<vimid>[0-9a-zA-Z_-]+)$',
+ url(r'^api/multicloud-pike/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)$',
registration.RegistryV1.as_view()),
- url(r'^api/multicloud-pike/v1/(?P<vimid>[0-9a-zA-Z_-]+)/exten',
+ url(r'^api/multicloud-pike/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/exten',
include('pike.extensions.urlsV1')),
url(r'^api/multicloud-pike/v1/(?P<cloud_owner>[0-9a-zA-Z_-]+)/(?P<cloud_region_id>[0-9a-zA-Z_-]+)/',
include('pike.proxy.urlsV1')),
diff --git a/share/common/msapi/extsys.py b/share/common/msapi/extsys.py
index e1534523..9b65072e 100644
--- a/share/common/msapi/extsys.py
+++ b/share/common/msapi/extsys.py
@@ -12,6 +12,7 @@
import json
import logging
import re
+from django.core.cache import cache
from common.exceptions import VimDriverNewtonException
from common.utils import restcall
@@ -22,6 +23,12 @@ logger = logging.getLogger(__name__)
def get_vim_by_id(vim_id):
+ # try to load from cache
+ cachedviminfostr = cache.get("VIMINFOCACHE_"+vim_id)
+ if cachedviminfostr:
+ viminfo = json.loads(cachedviminfostr)
+ return viminfo
+
cloud_owner,cloud_region_id = decode_vim_id(vim_id)
if cloud_owner and cloud_region_id:
@@ -74,6 +81,8 @@ def get_vim_by_id(vim_id):
viminfo['openstack_region_id'] = tmp_viminfo.get("cloud-epa-caps") \
if tmp_viminfo.get("cloud-epa-caps") else cloud_region_id
+ # cache the viminfo for 24 hour
+ cache.set("VIMINFOCACHE_"+vim_id, json.dumps(viminfo), 3600*24)
return viminfo
return None
diff --git a/share/newton_base/openoapi/flavor.py b/share/newton_base/openoapi/flavor.py
index b0a6bf6c..8dcd10f6 100644
--- a/share/newton_base/openoapi/flavor.py
+++ b/share/newton_base/openoapi/flavor.py
@@ -232,7 +232,7 @@ class Flavors(APIView):
extra_specs_vfc = request.data.pop("extraSpecs", None)
#create flavor first
resp = self._create_flavor(sess, request)
- if resp.status_code == 202:
+ if resp.status_code == 200:
resp_body = resp.json()["flavor"]
else:
return resp
diff --git a/share/newton_base/openoapi/network.py b/share/newton_base/openoapi/network.py
index 2206143a..0f4a2df1 100644
--- a/share/newton_base/openoapi/network.py
+++ b/share/newton_base/openoapi/network.py
@@ -45,7 +45,11 @@ class Networks(APIView):
logger.debug("With data = %s" % request.data)
pass
try:
- query = VimDriverUtils.get_query_part(request)
+ querystr = VimDriverUtils.get_query_part(request)
+ query = "project_id=%s" % (tenantid)
+ if querystr:
+ query += "&" + querystr
+
content, status_code = self._get_networks(query, vimid, tenantid, networkid)
logger.info("response with status = %s" % status_code)
return Response(data=content, status=status_code)
@@ -115,7 +119,7 @@ class Networks(APIView):
pass
try:
#check if created already: check name
- query = "name=%s" % request.data["name"]
+ query = "project_id=%s&name=%s" % (tenantid, request.data["name"])
content, status_code = self._get_networks(query, vimid, tenantid)
existed = False
if status_code == 200:
diff --git a/share/newton_base/openoapi/server.py b/share/newton_base/openoapi/server.py
index 5796f4f3..0c2382ae 100644
--- a/share/newton_base/openoapi/server.py
+++ b/share/newton_base/openoapi/server.py
@@ -182,17 +182,17 @@ class Servers(APIView):
logger.info("request returns with status %s" % resp.status_code)
logger.debug("Servers--dettachVolume resp status::>%s" % resp.status_code)
- def _convert_metadata(self, metadata_vfc, metadata_openstack, reverse=True):
- if not reverse:
- # from vfc format to openstack format
- for spec in metadata_vfc:
- metadata_openstack[spec['keyName']] = spec['value']
- else:
- for k, v in metadata_openstack.items():
- spec = {}
- spec['keyName'] = k
- spec['value'] = v
- metadata_vfc.append(spec)
+ #def _convert_metadata(self, metadata_vfc, metadata_openstack, reverse=True):
+ # if not reverse:
+ # # from vfc format to openstack format
+ # for spec in metadata_vfc:
+ # metadata_openstack[spec['keyName']] = spec['value']
+ # else:
+ # for k, v in metadata_openstack.items():
+ # spec = {}
+ # spec['keyName'] = k
+ # spec['value'] = v
+ # metadata_vfc.append(spec)
def _convert_resp(self, server):
#convert volumeArray
@@ -299,11 +299,11 @@ class Servers(APIView):
if not serverid:
# convert the key naming in servers
for server in content["servers"]:
- metadata_openstack = server.pop("metadata", None)
- if metadata_openstack:
- metadata_vfc = []
- self._convert_metadata(metadata_vfc, metadata_openstack, True)
- server["metadata"] = metadata_vfc
+ #metadata_openstack = server.pop("metadata", None)
+ #if metadata_openstack:
+ # metadata_vfc = []
+ # self._convert_metadata(metadata_vfc, metadata_openstack, True)
+ # server["metadata"] = metadata_vfc
VimDriverUtils.replace_key_by_mapping(server,
self.keys_mapping)
self._convert_resp(server)
@@ -312,11 +312,11 @@ class Servers(APIView):
else:
# convert the key naming in the server specified by id
server = content.pop("server", None)
- metadata_openstack = server.pop("metadata", None)
- if metadata_openstack:
- metadata_vfc = []
- self._convert_metadata(metadata_vfc, metadata_openstack, True)
- server["metadata"] = metadata_vfc
+ #metadata_openstack = server.pop("metadata", None)
+ #if metadata_openstack:
+ # metadata_vfc = []
+ # self._convert_metadata(metadata_vfc, metadata_openstack, True)
+ # server["metadata"] = metadata_vfc
VimDriverUtils.replace_key_by_mapping(server,
self.keys_mapping)
self._convert_resp(server)
@@ -335,6 +335,7 @@ class Servers(APIView):
servername = request.data["name"]
query = "name=%s" % servername
content, status_code = self._get_servers(query, vimid, tenantid)
+ logger.info("content %s" % content)
existed = False
if status_code == status.HTTP_200_OK:
for server in content["servers"]:
@@ -378,47 +379,55 @@ class Servers(APIView):
if len(networks) > 0:
server["networks"] = networks
- metadata_vfc = server.pop("metadata", None)
- if metadata_vfc:
- metadata_openstack = {}
- self._convert_metadata(metadata_vfc, metadata_openstack, False)
- server["metadata"] = metadata_openstack
+ #metadata_vfc = server.pop("metadata", None)
+ #if metadata_vfc:
+ # metadata_openstack = {}
+ # self._convert_metadata(metadata_vfc, metadata_openstack, True)
+ # server["metadata"] = metadata_openstack
contextarray = server.pop("contextArray", None)
volumearray = server.pop("volumeArray", None)
- userdata = server.pop("userdata", None)
- if contextarray:
- user_data = []
- strUserData = ''
- source_content = ""
- dest_path = ""
- user_data.append("#cloud-config\n")
- for context in contextarray:
- user_data.append("write_files:\n")
- user_data.append("- encoding: b64\n")
- user_data.append(" content: " + context["source_data_base64"] + "\n")
- user_data.append(" owner: root:root\n")
- user_data.append(" path: " + context["dest_path"] + "\n")
- user_data.append(" permissions: '0644'\n")
- user_data.append("\n")
- if userdata:
- user_data.append("runcmd:\n")
- user_data.append("- " + userdata + "\n")
-
- strUserData.join(user_data)
- server["user_data"] = strUserData
+
+ # inject files
+ logger.info("Start inject files contextarray %s" % contextarray)
+ if contextarray is not None:
+ if isinstance(contextarray, list):
+ personalities = []
+ for context in contextarray:
+ personality = {}
+ personality["path"] = context["dest_path"]
+ personality["contents"] = context["source_data_base64"]
+ personalities.append(personality)
+
+ if len(personalities) > 0:
+ server["personality"] = personalities
+ logger.info("List personalities %s" % personalities)
+ elif isinstance(contextarray, dict):
+ personalities = []
+ personality = {}
+ context = contextarray
+ personality["path"] = context["dest_path"]
+ personality["contents"] = context["source_data_base64"]
+ personalities.append(personality)
+
+ if len(personalities) > 0:
+ server["personality"] = personalities
+ logger.info("Personalities %s" % personalities)
+ else:
+ logger.error("contextarray %s format is not right.", contextarray)
+ return Response(data={'error': str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR)
VimDriverUtils.replace_key_by_mapping(server,
self.keys_mapping, True)
req_body = json.JSONEncoder().encode({"server": server})
-
+ logger.info("-server %s, json_server %s" % (server, req_body))
vim = VimDriverUtils.get_vim_info(vimid)
sess = VimDriverUtils.get_session(vim, tenantid)
self.service['region_name'] = vim['openstack_region_id'] \
if vim.get('openstack_region_id') \
else vim['cloud_region_id']
-
logger.info("making request with URI:%s" % req_resouce)
resp = sess.post(req_resouce, data=req_body,
endpoint_filter=self.service,
@@ -434,11 +443,11 @@ class Servers(APIView):
volumeIds = [extraVolume["volumeId"] for extraVolume in volumearray]
self._attachVolume(vimid, tenantid, resp_body["id"], *volumeIds)
- metadata_openstack = resp_body.pop("metadata", None)
- if metadata_openstack:
- metadata_vfc = []
- self._convert_metadata(metadata_vfc, metadata_openstack, True)
- resp_body["metadata"] = metadata_vfc
+ #metadata_openstack = resp_body.pop("metadata", None)
+ #if metadata_openstack:
+ # metadata_vfc = []
+ # self._convert_metadata(metadata_vfc, metadata_openstack, True)
+ # resp_body["metadata"] = metadata_vfc
VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping)
vim_dict = {
@@ -546,7 +555,7 @@ class ServerAction(APIView):
endpoint_filter=self.service,
headers={"Content-Type": "application/json",
"Accept": "application/json"})
- resp_body = resp.json()
+ resp_body = {}
return Response(data=resp_body, status=resp.status_code)
except VimDriverKiloException as e:
diff --git a/share/newton_base/openoapi/subnet.py b/share/newton_base/openoapi/subnet.py
index a5cef9c7..06e029a8 100644
--- a/share/newton_base/openoapi/subnet.py
+++ b/share/newton_base/openoapi/subnet.py
@@ -48,7 +48,11 @@ class Subnets(APIView):
pass
try:
# prepare request resource to vim instance
- query = VimDriverUtils.get_query_part(request)
+ querystr = VimDriverUtils.get_query_part(request)
+ query = "project_id=%s" % (tenantid)
+ if querystr:
+ query += "&" + querystr
+
content, status_code = self._get_subnets(query, vimid, tenantid, subnetid)
logger.info("request returns with status %s" % status_code)
return Response(data=content, status=status_code)
@@ -117,7 +121,11 @@ class Subnets(APIView):
pass
try:
#check if created already: check name
- query = "name=%s" % request.data["name"]
+ query = "project_id=%s&name=%s" % (tenantid, request.data["name"])
+ networkid = request.data.get("networkId", None)
+ if networkid:
+ query += "&network_id=%s" % networkid
+
content, status_code = self._get_subnets(query, vimid, tenantid)
existed = False
if status_code == 200:
diff --git a/share/newton_base/openoapi/tenants.py b/share/newton_base/openoapi/tenants.py
index 71439411..a048a36e 100644
--- a/share/newton_base/openoapi/tenants.py
+++ b/share/newton_base/openoapi/tenants.py
@@ -48,8 +48,8 @@ class Tenants(APIView):
vim = VimDriverUtils.get_vim_info(vimid)
req_resouce = "/projects"
- if '/v2' in vim["url"]:
- req_resouce = "/v2.0/tenants"
+ if '/v2.0' in vim["url"]:
+ req_resouce = "/tenants"
sess = VimDriverUtils.get_session(vim)
diff --git a/share/newton_base/openoapi/vport.py b/share/newton_base/openoapi/vport.py
index 5a799262..ca6297dc 100644
--- a/share/newton_base/openoapi/vport.py
+++ b/share/newton_base/openoapi/vport.py
@@ -47,7 +47,11 @@ class Vports(APIView):
pass
try:
# prepare request resource to vim instance
- query = VimDriverUtils.get_query_part(request)
+ querystr = VimDriverUtils.get_query_part(request)
+ query = "project_id=%s" % (tenantid)
+ if querystr:
+ query += "&" + querystr
+
content, status_code = self._get_ports(query, vimid, tenantid, portid)
logger.info("response with status = %s" % status_code)
return Response(data=content, status=status_code)
@@ -127,7 +131,10 @@ class Vports(APIView):
pass
try:
#check if already created: name
- query = "name=%s" % request.data["name"]
+ query = "project_id=%s&name=%s" % (tenantid, request.data["name"])
+ networkid = request.data.get("networkId", None)
+ if networkid:
+ query += "&network_id=%s" % networkid
content, status_code = self._get_ports(query, vimid, tenantid, portid)
existed = False
if status_code == 200:
diff --git a/share/newton_base/registration/registration.py b/share/newton_base/registration/registration.py
index 54b1d48c..35f3b982 100644
--- a/share/newton_base/registration/registration.py
+++ b/share/newton_base/registration/registration.py
@@ -1007,7 +1007,7 @@ class Registry(APIView):
#self._discover_volumegroups(vimid, sess, viminfo)
# discover all snapshots
- self._discover_snapshots(vimid, sess, viminfo)
+ #self._discover_snapshots(vimid, sess, viminfo)
# discover all server groups
#self.discover_servergroups(request, vimid, sess, viminfo)
diff --git a/share/newton_base/util.py b/share/newton_base/util.py
index e925d99a..aa03b65b 100644
--- a/share/newton_base/util.py
+++ b/share/newton_base/util.py
@@ -22,6 +22,11 @@ from keystoneauth1 import session
from common.msapi import extsys
+# profiler decoration
+import cProfile
+import pstats
+import os
+
logger = logging.getLogger(__name__)
@@ -147,3 +152,28 @@ class VimDriverUtils(object):
def replace_key_by_mapping(dict_obj, mapping, reverse=False):
for k in mapping:
VimDriverUtils._replace_a_key(dict_obj, k, reverse)
+
+ # profiler decoration
+ @staticmethod
+ def do_cprofile(filename):
+ """
+ Decorator for function profiling.
+ """
+ def wrapper(func):
+ def profiled_func(*args, **kwargs):
+ # Flag for do profiling or not.
+ DO_PROF = True # os.getenv("PROFILING")
+ if DO_PROF:
+ profile = cProfile.Profile()
+ profile.enable()
+ result = func(*args, **kwargs)
+ profile.disable()
+ # Sort stat by internal time.
+ sortby = "tottime"
+ ps = pstats.Stats(profile).sort_stats(sortby)
+ ps.dump_stats(filename)
+ else:
+ result = func(*args, **kwargs)
+ return result
+ return profiled_func
+ return wrapper
diff --git a/windriver/titanium_cloud/requests/urls.py b/windriver/titanium_cloud/requests/urls.py
index 435ff239..73cc1543 100644
--- a/windriver/titanium_cloud/requests/urls.py
+++ b/windriver/titanium_cloud/requests/urls.py
@@ -34,6 +34,8 @@ urlpatterns = [
image.Images.as_view()),
url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?',
volume.Volumes.as_view()),
+ url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))/action/?$',
+ server.ServerAction.as_view()),
url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?',
server.Servers.as_view()),
url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?',