diff options
author | Bin Yang <bin.yang@windriver.com> | 2017-09-20 11:18:35 +0800 |
---|---|---|
committer | Bin Yang <bin.yang@windriver.com> | 2017-09-20 11:18:35 +0800 |
commit | a7da9df2316973357945636cc4f122746722a492 (patch) | |
tree | 80cd43e5b401fefbdc3b15b53e508c277fde4ade /windriver/titanium_cloud | |
parent | 8d5f1fee55c1dcd3cab38441e8a1dd5743587303 (diff) |
Add windriver plugin
Change-Id: Icd27d7af12d6920810d7cbb163e8be70db49f121
Issue-Id: MULTICLOUD-89
Signed-off-by: Bin Yang <bin.yang@windriver.com>
Diffstat (limited to 'windriver/titanium_cloud')
31 files changed, 1131 insertions, 0 deletions
diff --git a/windriver/titanium_cloud/__init__.py b/windriver/titanium_cloud/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/extensions/__init__.py b/windriver/titanium_cloud/extensions/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/extensions/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/extensions/urls.py b/windriver/titanium_cloud/extensions/urls.py new file mode 100644 index 00000000..b0ffec89 --- /dev/null +++ b/windriver/titanium_cloud/extensions/urls.py @@ -0,0 +1,29 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +from django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns + +from titanium_cloud.extensions.views import extensions +from titanium_cloud.extensions.views import epacaps +from titanium_cloud.extensions.views import fcaps + + +urlpatterns = [ + url(r'^sions/?$', extensions.Extensions.as_view()), + url(r'^sions/epa-caps/?$', epacaps.EpaCaps.as_view()), + url(r'^sions/guest-monitor/(?P<vserverid>[0-9a-zA-Z_-]+)/?$', fcaps.GuestMonitor.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/windriver/titanium_cloud/extensions/views/__init__.py b/windriver/titanium_cloud/extensions/views/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/extensions/views/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/extensions/views/epacaps.py b/windriver/titanium_cloud/extensions/views/epacaps.py new file mode 100644 index 00000000..7f638fad --- /dev/null +++ b/windriver/titanium_cloud/extensions/views/epacaps.py @@ -0,0 +1,32 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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 logging +import json +import traceback + +from titanium_cloud.pub.config import config + + +from newton.extensions.views import epacaps as newton_epacaps + +logger = logging.getLogger(__name__) + +DEBUG=True + + +class EpaCaps(newton_epacaps.EpaCaps): + + def __init__(self): + self.proxy_prefix = config.MULTICLOUD_PREFIX + self._logger = logger diff --git a/windriver/titanium_cloud/extensions/views/extensions.py b/windriver/titanium_cloud/extensions/views/extensions.py new file mode 100644 index 00000000..2dd61fe9 --- /dev/null +++ b/windriver/titanium_cloud/extensions/views/extensions.py @@ -0,0 +1,73 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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 logging + +from titanium_cloud.pub.config import config +from newton.extensions.views import extensions as newton_extensions + +logger = logging.getLogger(__name__) + +DEBUG=True + +class Extensions(newton_extensions.Extensions): + + def __init__(self): + self._logger = logger + self.proxy_prefix = config.MULTICLOUD_PREFIX + + + def get(self, request, vimid=""): + logger.debug("Extensions--get::data> %s" % request.data) + logger.debug("Extensions--get::vimid> %s" + % vimid) + try: + cloud_owner, cloud_region_id = extsys.decode_vim_id(vimid) + registered_extensions = \ + [ + { + "alias": "epa-caps", + "description": "Multiple network support", + "name": "EPACapsQuery", + "url": self.proxy_prefix + "/%s/extensions/epa-caps" \ + % (vimid), + "spec": "" + }, + { + "alias": "guest-monitor", + "description": "Multiple network support", + "name": "EPACapsQuery", + "url": self.proxy_prefix +\ + "/%s/extensions/guest-monitor/{server_id}" \ + % (vimid), + "spec": "" + } + ] + + content = { + "cloud-owner":cloud_owner, + "cloud-region-id":cloud_region_id, + "vimid":vimid, + "extensions": registered_extensions + } + return Response(data=content, status=status.HTTP_200_OK) + + except VimDriverNewtonException as e: + return Response(data={'error': e.content}, status=e.status_code) + except HttpError as e: + logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json())) + return Response(data=e.response.json(), status=e.http_status) + except Exception as e: + logger.error(traceback.format_exc()) + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/windriver/titanium_cloud/extensions/views/fcaps.py b/windriver/titanium_cloud/extensions/views/fcaps.py new file mode 100644 index 00000000..ad054e95 --- /dev/null +++ b/windriver/titanium_cloud/extensions/views/fcaps.py @@ -0,0 +1,320 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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 logging +import json +import traceback +import threading + +from django.core.cache import cache + +from keystoneauth1.exceptions import HttpError +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from titanium_cloud.pub.config import config +from newton.pub.exceptions import VimDriverNewtonException +from newton.requests.views.util import VimDriverUtils +from newton.pub.msapi import extsys + + + +#from newton.extensions.views import fcaps as newton_fcaps + +logger = logging.getLogger(__name__) + +DEBUG=True + +#dict to store running worker threads +running_threads = {} +running_thread_lock = threading.Lock() + +class GuestMonitorWorker (threading.Thread): + service = {'service_type': 'platform', + 'interface': 'public'} + def __init__(self, vimid, tenantid=None): + threading.Thread.__init__(self) + self.vimid = vimid + self.tenantid = tenantid + self.eventid = '700.213' #Guest Heartbeat failed for instance + + def run(self): + logger.debug("start GuestMonitorWorker %s,%s" % (self.vimid, self.tenantid)) + + viminfo = VimDriverUtils.get_vim_info(self.vimid) + sess = VimDriverUtils.get_session(viminfo, tenantid=self.tenantid) + + thread_info = running_threads.get(self.vimid) + if not thread_info: + return + + while thread_info.get('state') == 'start': + #wait for jobs + vservers = thread_info.get('vservers') if thread_info else None + if not vservers: + continue + + # do jobs + for (vserverid, vserverinfo) in vservers.items(): + status_code, heartbeat_event = \ + self.monitor_heartbeat(self.vimid, self.tenantid, vserverid, viminfo, sess) + + if status_code == status.HTTP_403_FORBIDDEN: + #invalid tenant, so remove this job + + running_thread_lock.acquire() + thread_info['state'] = 'error' + running_thread_lock.release() + + return #exit this thread since error + + if heartbeat_event: + #report to VES + #tbd + pass + else: + continue + + running_thread_lock.acquire() + thread_info['state'] = 'stopped' + running_thread_lock.release() + + logger.debug("stop GuestMonitorWorker %s, %s, %s" % (self.vimid, self.tenantid, self.vserverid)) +# running_thread_lock.acquire() +# running_threads.pop(self.vimid) +# running_thread_lock.release() + + def monitor_heartbeat(self, vimid, tenantid, vserverid, viminfo, session): + logger.debug("GuestMonitorWorker--monitor_heartbeat::> %s" % (vserverid)) + try: + # prepare request resource to vim instance + req_resouce = "/v1/event_log?q.field=entity_instance_id&\ + q.field=event_log_id&\ + q.op=eq&q.op=eq&q.type=&q.type=&\ + q.value=tenant\%%s.instance\%%s&\ + q.value=%s" % (tenantid, vserverid, self.eventid) + + resp = session.get(req_resouce, endpoint_filter=self.service, + headers={"Content-Type": "application/json", + "Accept": "application/json"}) + + logger.debug("response status code of monitor_heartbeat %s" % resp.status_code) + + return resp.status_code, resp.json() if resp.content else None + + except HttpError as e: + logger.error("monitor_heartbeat, HttpError: status:%s, response:%s" % (e.http_status, e.response.json())) + return e.http_status, e.response.json() + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("Failed to monitor_heartbeat:%s" % str(e)) + return e.http_status, e.response.json() + + +class GuestMonitor(APIView): + + def __init__(self): + self.proxy_prefix = config.MULTICLOUD_PREFIX + self._logger = logger + + def post(self, request, vimid="", vserverid=""): + '''Start guest monitoring on specified virtual server''' + self._logger.debug("GuestMonitor--post::data> %s" % request.data) + self._logger.debug("GuestMonitor--post::vimid > %s" % vimid) + + try: + # populate proxy identity url + cloud_owner, cloud_region_id = extsys.decode_vim_id(vimid) + + tenant_name = request.data.get('tenantName') + tenant_id = request.data.get('tenantID') + ves_url = request.data.get('vesurl') + + # prepare request resource to vim instance + # get token: + viminfo = VimDriverUtils.get_vim_info(vimid) + # the tenant should have the privilege to access the event-log API + # usually it is 'admin'. Otherwise the 403 will be returned. + sess = None + if tenant_id: + sess = VimDriverUtils.get_session(viminfo, tenantid=tenant_id) + else: + sess = VimDriverUtils.get_session(viminfo, tenantname=tenant_name) + + #now try to convert tenant_name to tenant_id + #tbd + + thread_info = running_threads[vimid] + + if thread_info and thread_info['state'] == 'error': + #the thread is in error state, so recreate with new tenant_id + running_thread_lock.acquire() + running_threads.pop(vimid) + running_thread_lock.release() + thread_info = None + + if not thread_info: + tmp_thread = GuestMonitorWorker(vimid, tenant_id) + if not tmp_thread: + raise VimDriverNewtonException(message="internal error", + content="Fail to spawn thread for Guest Monitoring", + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) + thread_info = { + 'thread': tmp_thread, + 'tenantid':tenant_id, + 'vservers':{}, + 'state':'start' + } + + running_thread_lock.acquire() + running_threads[vimid] = thread_info + running_thread_lock.release() + tmp_thread.start() + else: + thread_info['state'] = 'start' + + + vservers = thread_info.get('vservers') + vservers[vserverid] = {'vesurl': ves_url} + + return Response(status=status.HTTP_202_ACCEPTED) + + except VimDriverNewtonException as e: + return Response(data={'error': e.content}, status=e.status_code) + except HttpError as e: + self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json())) + return Response(data=e.response.json(), status=e.http_status) + except Exception as e: + self._logger.error(traceback.format_exc()) + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + + def GET(self, request, vimid="", vserverid=""): + '''query guest monitoring on specified virtual server''' + self._logger.debug("GuestMonitor--get::data> %s" % request.data) + self._logger.debug("GuestMonitor--get::vimid > %s" % vimid) + + try: + # populate proxy identity url + cloud_owner, cloud_region_id = extsys.decode_vim_id(vimid) + + tenant_name = request.data.get('tenantName') + tenant_id = request.data.get('tenantID') + vserver_id = vserverid + + # prepare request resource to vim instance + # get token: + viminfo = VimDriverUtils.get_vim_info(vimid) + # the tenant should have the privilege to access the event-log API + # usually it is 'admin'. Otherwise the 403 will be returned. + sess = None + if tenant_id: + sess = VimDriverUtils.get_session(viminfo, tenantid=tenant_id) + else: + sess = VimDriverUtils.get_session(viminfo, tenantname=tenant_name) + + #now try to convert tenant_name to tenant_id, and vserver_name to vserver_id + #tbd + + thread_info = running_threads[vimid] + if not thread_info \ + or not thread_info.get('vservers') \ + or not thread_info.get('vservers').get(vserverid): + status_code = status.HTTP_204_NO_CONTENT + content = {'error': + 'Guest Monitor job is not created for this virtual server,\ + vim id: %s, vserver id: %s' + % (self.vimid, vserverid)} + pass + elif thread_info['state'] == 'error': + status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + content = {'error': + 'Guest Monitor job for this virtual server \ + (vim id: %s, vserver id: %s) failed due to: %s' + % (self.vimid, vserverid, thread_info.get('message'))} + pass + else: + vserverinfo = thread_info.get('vservers').get(vserverid) + content = vserverinfo.get('message') + status_code = vserverinfo.get('status') or status.HTTP_200_OK + pass + + #return Response(status=status.HTTP_202_ACCEPTED) + return Response(status=status_code, data=content) + + except VimDriverNewtonException as e: + return Response(data={'error': e.content}, status=e.status_code) + except HttpError as e: + self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json())) + return Response(data=e.response.json(), status=e.http_status) + except Exception as e: + self._logger.error(traceback.format_exc()) + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + + + def DELETE(self, request, vimid="", vserverid=""): + '''Stop guest monitoring on specified virtual server''' + self._logger.debug("GuestMonitor--delete::data> %s" % request.data) + self._logger.debug("GuestMonitor--delete::vimid > %s" % vimid) + + try: + # populate proxy identity url + cloud_owner, cloud_region_id = extsys.decode_vim_id(vimid) + + tenant_name = request.data.get('tenantName') + tenant_id = request.data.get('tenantID') + + # prepare request resource to vim instance + # get token: + viminfo = VimDriverUtils.get_vim_info(vimid) + # the tenant should have the privilege to access the event-log API + # usually it is 'admin'. Otherwise the 403 will be returned. + sess = None + if tenant_id: + sess = VimDriverUtils.get_session(viminfo, tenantid=tenant_id) + else: + sess = VimDriverUtils.get_session(viminfo, tenantname=tenant_name) + + #now try to convert tenant_name to tenant_id, and vserver_name to vserver_id + #tbd + + thread_info = running_threads[vimid] + if not thread_info: + status_code = status.HTTP_204_NO_CONTENT + else: + vservers = thread_info.get('vservers') + if vservers.get(vserverid): + vservers.pop(vserverid) + + running_thread_lock.acquire() + if len(vservers.items()) == 0: + thread_info.stop() + running_threads.pop(vimid) + running_thread_lock.release() + status_code = status.HTTP_202_ACCEPTED + + return Response(status=status_code) + + except VimDriverNewtonException as e: + return Response(data={'error': e.content}, status=e.status_code) + except HttpError as e: + self._logger.error("HttpError: status:%s, response:%s" % (e.http_status, e.response.json())) + return Response(data=e.response.json(), status=e.http_status) + except Exception as e: + self._logger.error(traceback.format_exc()) + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR)
\ No newline at end of file diff --git a/windriver/titanium_cloud/proxy/__init__.py b/windriver/titanium_cloud/proxy/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/proxy/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/proxy/urls.py b/windriver/titanium_cloud/proxy/urls.py new file mode 100644 index 00000000..f848e76c --- /dev/null +++ b/windriver/titanium_cloud/proxy/urls.py @@ -0,0 +1,32 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +from django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns + +from titanium_cloud.proxy.views import identityV3 +from titanium_cloud.proxy.views import services + +urlpatterns = [ + # url(r'^identity/v2)$', + # identityV2.Tokens.as_view()), + url(r'^identity/v3/auth/tokens/?$', + identityV3.Tokens.as_view()), + url(r'^identity/(?:v2.0/|)tenants/?$', + services.GetTenants.as_view()), + url(r'^(?P<servicetype>[0-9a-zA-Z_-]+)/(?P<requri>[0-9a-zA-Z./_-]*)$', + services.Services.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/windriver/titanium_cloud/proxy/views/__init__.py b/windriver/titanium_cloud/proxy/views/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/proxy/views/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/proxy/views/identityV3.py b/windriver/titanium_cloud/proxy/views/identityV3.py new file mode 100644 index 00000000..a6efa6ac --- /dev/null +++ b/windriver/titanium_cloud/proxy/views/identityV3.py @@ -0,0 +1,27 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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 logging + +from titanium_cloud.pub.config import config +from newton.proxy.views import identityV3 as newton_identityV3 + +logger = logging.getLogger(__name__) + +DEBUG=True + +class Tokens(newton_identityV3.Tokens): + + def __init__(self): + self.proxy_prefix = config.MULTICLOUD_PREFIX + self._logger = logger diff --git a/windriver/titanium_cloud/proxy/views/services.py b/windriver/titanium_cloud/proxy/views/services.py new file mode 100644 index 00000000..9bd5cc4e --- /dev/null +++ b/windriver/titanium_cloud/proxy/views/services.py @@ -0,0 +1,45 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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 logging + +from rest_framework import status + +from titanium_cloud.pub.config import config +from newton.proxy.views import services as newton_services + +logger = logging.getLogger(__name__) + +DEBUG=True + +class Services(newton_services.Services): + + def __init__(self): + self._logger = logger + + +class GetTenants(newton_services.GetTenants): + ''' + Backward compatible API for /v2.0/tenants + ''' + + def __init__(self): + self._logger = logger + + def get(self, request, vimid="", servicetype="identity", requri='v3/projects'): + self._logger.debug("GetTenants--get::META> %s" % request.META) + self._logger.debug("GetTenants--get::data> %s" % request.data) + self._logger.debug("GetTenants--get::vimid, servicetype, requri> %s,%s,%s" + % (vimid, servicetype, requri)) + + return super(GetTenants,self).get(request, vimid, servicetype, requri) diff --git a/windriver/titanium_cloud/pub/__init__.py b/windriver/titanium_cloud/pub/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/pub/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/pub/config/__init__.py b/windriver/titanium_cloud/pub/config/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/pub/config/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/pub/config/config.py b/windriver/titanium_cloud/pub/config/config.py new file mode 100644 index 00000000..bc7e8553 --- /dev/null +++ b/windriver/titanium_cloud/pub/config/config.py @@ -0,0 +1,34 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +import os + +# [MSB] +MSB_SERVICE_ADDR = '127.0.0.1' +MSB_SERVICE_PORT = '80' + +#[Multicloud] +MULTICLOUD_PREFIX = "http://%s:%s/api/multicloud-titanium_cloud/v0" %(MSB_SERVICE_ADDR, MSB_SERVICE_PORT) + +# [A&AI] +AAI_ADDR = "aai.api.simpledemo.openecomp.org" +AAI_PORT = "8443" +AAI_SERVICE_URL = 'https://%s:%s/aai' % (AAI_ADDR, AAI_PORT) +AAI_SCHEMA_VERSION = "v11" +AAI_USERNAME = 'AAI' +AAI_PASSWORD = 'AAI' + +AAI_BASE_URL = "%s/%s" % (AAI_SERVICE_URL, AAI_SCHEMA_VERSION) + +MULTICLOUD_APP_ID = 'MultiCloud-Titanium_Cloud' + +# [IMAGE LOCAL PATH] +ROOT_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) diff --git a/windriver/titanium_cloud/registration/__init__.py b/windriver/titanium_cloud/registration/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/registration/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/registration/views/__init__.py b/windriver/titanium_cloud/registration/views/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/registration/views/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/registration/views/registration.py b/windriver/titanium_cloud/registration/views/registration.py new file mode 100644 index 00000000..e58e170b --- /dev/null +++ b/windriver/titanium_cloud/registration/views/registration.py @@ -0,0 +1,28 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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 logging + +from titanium_cloud.pub.config import config + +from newton.registration.views import registration as newton_registration + +logger = logging.getLogger(__name__) + +DEBUG=True + +class Registry(newton_registration.Registry): + + def __init__(self): + self.proxy_prefix = config.MULTICLOUD_PREFIX + self._logger = logger diff --git a/windriver/titanium_cloud/requests/__init__.py b/windriver/titanium_cloud/requests/__init__.py new file mode 100644 index 00000000..48b6e44b --- /dev/null +++ b/windriver/titanium_cloud/requests/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/requests/urls.py b/windriver/titanium_cloud/requests/urls.py new file mode 100644 index 00000000..69f0e444 --- /dev/null +++ b/windriver/titanium_cloud/requests/urls.py @@ -0,0 +1,47 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +from django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns + +from newton.requests.views import network +from newton.requests.views import subnet +from newton.requests.views import image +from newton.requests.views import volume +from newton.requests.views import server +from newton.requests.views import vport +from newton.requests.views import limits +from newton.requests.views import hosts +from newton.requests.views import flavor + +urlpatterns = [ + url(r'^networks(/(?P<networkid>[0-9a-zA-Z_-]+))?', + network.Networks.as_view()), + url(r'^subnets(/(?P<subnetid>[0-9a-zA-Z_-]+))?', + subnet.Subnets.as_view()), + url(r'^images(/(?P<imageid>[0-9a-zA-Z_-]+))?', + 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_-]+))?', + server.Servers.as_view()), + url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?', + vport.Vports.as_view()), + url(r'^flavors(/(?P<flavorid>[0-9a-zA-Z_-]+))?', + flavor.Flavors.as_view()), + url(r'^limits$', limits.Limits.as_view()), + url(r'^hosts(/(?P<hostname>[0-9a-zA-Z_-]+))?', hosts.Hosts.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/windriver/titanium_cloud/samples/__init__.py b/windriver/titanium_cloud/samples/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/samples/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/samples/tests.py b/windriver/titanium_cloud/samples/tests.py new file mode 100644 index 00000000..d419efa5 --- /dev/null +++ b/windriver/titanium_cloud/samples/tests.py @@ -0,0 +1,29 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +import unittest +import json +from django.test import Client +from rest_framework import status + + +class SampleViewTest(unittest.TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + def test_sample(self): + response = self.client.get("/samples/") + self.assertEqual(status.HTTP_200_OK, response.status_code, response.content) + resp_data = response.json() + self.assertEqual({"status": "active"}, resp_data) diff --git a/windriver/titanium_cloud/samples/urls.py b/windriver/titanium_cloud/samples/urls.py new file mode 100644 index 00000000..ae67a819 --- /dev/null +++ b/windriver/titanium_cloud/samples/urls.py @@ -0,0 +1,16 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +from django.conf.urls import url +from titanium_cloud.samples import views + +urlpatterns = [ + url(r'^samples/?$', views.SampleList.as_view()), ] diff --git a/windriver/titanium_cloud/samples/views.py b/windriver/titanium_cloud/samples/views.py new file mode 100644 index 00000000..e51044ad --- /dev/null +++ b/windriver/titanium_cloud/samples/views.py @@ -0,0 +1,26 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +import logging + +from rest_framework.views import APIView +from rest_framework.response import Response + +logger = logging.getLogger(__name__) + + +class SampleList(APIView): + """ + List all samples. + """ + def get(self, request, format=None): + logger.debug("get") + return Response({"status": "active"}) diff --git a/windriver/titanium_cloud/settings.py b/windriver/titanium_cloud/settings.py new file mode 100644 index 00000000..393f994e --- /dev/null +++ b/windriver/titanium_cloud/settings.py @@ -0,0 +1,122 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +import os +import sys + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '3o-wney!99y)^h3v)0$j16l9=fdjxcb+a8g+q3tfbahcnu2b0o' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'titanium_cloud.urls' + +WSGI_APPLICATION = 'titanium_cloud.wsgi.application' + +REST_FRAMEWORK = { + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer', + ), + + 'DEFAULT_PARSER_CLASSES': ( + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.MultiPartParser', + # 'rest_framework.parsers.FormParser', + # 'rest_framework.parsers.FileUploadParser', + ) +} + +TIME_ZONE = 'UTC' + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.6/howto/static-files/ + +STATIC_URL = '/static/' + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'standard': { + 'format': '%(asctime)s:[%(name)s]:[%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s', + }, + }, + 'filters': { + }, + 'handlers': { + 'titanium_cloud_handler': { + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(BASE_DIR, 'logs/runtime_titanium_cloud.log'), + 'formatter': 'standard', + 'maxBytes': 1024 * 1024 * 50, + 'backupCount': 5, + }, + }, + + 'loggers': { + 'titanium_cloud': { + 'handlers': ['titanium_cloud_handler'], + 'level': 'DEBUG', + 'propagate': False + }, + } +} + +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': '127.0.0.1:11211', + } +} + +if 'test' in sys.argv: + from titanium_cloud.pub.config import config + + REST_FRAMEWORK = {} + import platform + + if platform.system() == 'Linux': + TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' + TEST_OUTPUT_VERBOSE = True + TEST_OUTPUT_DESCRIPTIONS = True + TEST_OUTPUT_DIR = 'test-reports' diff --git a/windriver/titanium_cloud/swagger/__init__.py b/windriver/titanium_cloud/swagger/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/windriver/titanium_cloud/swagger/__init__.py @@ -0,0 +1,10 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. diff --git a/windriver/titanium_cloud/swagger/tests.py b/windriver/titanium_cloud/swagger/tests.py new file mode 100644 index 00000000..ea9c9e50 --- /dev/null +++ b/windriver/titanium_cloud/swagger/tests.py @@ -0,0 +1,29 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +import unittest +import json +from django.test import Client +from rest_framework import status + + +class SampleViewTest(unittest.TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + def test_sample(self): + response = self.client.get("/api/multicloud-titanium_cloud/v0/swagger.json") + self.assertEqual(status.HTTP_200_OK, response.status_code, response.content) +# resp_data = response.json() +# self.assertEqual({"status": "active"}, resp_data) diff --git a/windriver/titanium_cloud/swagger/urls.py b/windriver/titanium_cloud/swagger/urls.py new file mode 100644 index 00000000..21ef4bde --- /dev/null +++ b/windriver/titanium_cloud/swagger/urls.py @@ -0,0 +1,21 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +from django.conf.urls import patterns, url +from rest_framework.urlpatterns import format_suffix_patterns + +from titanium_cloud.swagger.views import SwaggerJsonView + +urlpatterns = [ + url(r'^api/multicloud-titanium_cloud/v0/swagger.json$', SwaggerJsonView.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/windriver/titanium_cloud/swagger/views.py b/windriver/titanium_cloud/swagger/views.py new file mode 100644 index 00000000..01f9374e --- /dev/null +++ b/windriver/titanium_cloud/swagger/views.py @@ -0,0 +1,45 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +import json +import logging +import os +import traceback + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from newton.pub.exceptions import VimDriverNewtonException +from newton.swagger import views as newton_json_view + +logger = logging.getLogger(__name__) + + +class SwaggerJsonView(newton_json_view.SwaggerJsonView): + + def get(self, request): + ''' + reuse newton code and update the basePath + :param request: + :return: + ''' + + resp = super(SwaggerJsonView,self).get(request) + json_data = resp.data if resp else None + if json_data: + json_data["basePath"] = "/api/multicloud-titanium_cloud/v0/" + json_data["info"]["title"] = "Service NBI of MultiCloud plugin for OpenStack Newton" + return Response(data=json_data, status=200) + else: + return Response(data={'error':'internal error'}, status=500) + + diff --git a/windriver/titanium_cloud/urls.py b/windriver/titanium_cloud/urls.py new file mode 100644 index 00000000..664d8b3e --- /dev/null +++ b/windriver/titanium_cloud/urls.py @@ -0,0 +1,34 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + +from django.conf.urls import include, url + +from titanium_cloud.registration.views import registration +from newton.requests.views import tenants + +urlpatterns = [ + url(r'^', include('titanium_cloud.swagger.urls')), + url(r'^', include('titanium_cloud.samples.urls')), + url(r'^api/multicloud-titanium_cloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/registry/?$', + registration.Registry.as_view()), + url(r'^api/multicloud-titanium_cloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/?$', + registration.Registry.as_view()), + url(r'^api/multicloud-titanium_cloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/exten', + include('titanium_cloud.extensions.urls')), + url(r'^api/multicloud-titanium_cloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/', + include('titanium_cloud.proxy.urls')), + url(r'^api/multicloud-titanium_cloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/tenants/?$', + tenants.Tenants.as_view()), + url(r'^api/multicloud-titanium_cloud/v0/(?P<vimid>[0-9a-zA-Z_-]+)/' + '(?P<tenantid>[0-9a-zA-Z_-]{8,})/', include('titanium_cloud.requests.urls')), +] + + diff --git a/windriver/titanium_cloud/wsgi.py b/windriver/titanium_cloud/wsgi.py new file mode 100644 index 00000000..2962328d --- /dev/null +++ b/windriver/titanium_cloud/wsgi.py @@ -0,0 +1,19 @@ +# Copyright (c) 2017 Wind River Systems, Inc. +# +# Licensed 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. + + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "titanium_cloud.settings") + +application = get_wsgi_application() |