Adoption of base framework code for azure plugin
[multicloud/azure.git] / azure / azure / api_v2 / api_router / controller_builder.py
1 #    Copyright (c) 2018 Amdocs
2 #
3 #    Licensed under the Apache License, Version 2.0 (the "License");
4 #    you may not use this file except in compliance with the License.
5 #    You may obtain a copy of the License at
6 #
7 #        http://www.apache.org/licenses/LICENSE-2.0
8 #
9 #    Unless required by applicable law or agreed to in writing, software
10 #    distributed under the License is distributed on an "AS IS" BASIS,
11 #    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 #    See the License for the specific language governing permissions and
13 #    limitations under the License.
14
15 import json
16 from keystoneauth1.identity import v2 as keystone_v2
17 from keystoneauth1.identity import v3 as keystone_v3
18 from keystoneauth1 import session
19 import pecan
20 from pecan import rest
21 import re
22
23 from azure.api_v2.api_definition import utils
24 from azure.pub import exceptions
25 from azure.pub.msapi import extsys
26
27
28 OBJ_IN_ARRAY = "(\w+)\[(\d+)\]\.(\w+)"
29
30
31 def _get_vim_auth_session(vim_id, tenant_id):
32     """ Get the auth session to the given backend VIM """
33
34     try:
35         vim = extsys.get_vim_by_id(vim_id)
36     except exceptions.VimDriverAzureException as e:
37         return pecan.abort(500, str(e))
38
39     params = {
40         "auth_url": vim["url"],
41         "username": vim["userName"],
42         "password": vim["password"],
43     }
44     params["tenant_id"] = tenant_id
45
46     if '/v2' in params["auth_url"]:
47         auth = keystone_v2.Password(**params)
48     else:
49         params["user_domain_name"] = vim["domain"]
50         params["project_domain_name"] = vim["domain"]
51
52         if 'tenant_id' in params:
53             params["project_id"] = params.pop("tenant_id")
54         if 'tenant_name' in params:
55             params["project_name"] = params.pop("tenant_name")
56         if '/v3' not in params["auth_url"]:
57             params["auth_url"] = params["auth_url"] + "/v3",
58         auth = keystone_v3.Password(**params)
59
60     return session.Session(auth=auth)
61
62
63 def _convert_default_value(default):
64     return {"None": None, "true": True, "false": False}[default]
65
66
67 def _property_exists(resource, attr, required=False):
68     if attr not in resource:
69         if required:
70             raise Exception("Required field %s is missed in VIM "
71                             "resource %s", (attr, resource))
72         else:
73             return False
74
75     return True
76
77
78 def _convert_vim_res_to_mc_res(vim_resource, res_properties):
79     mc_resource = {}
80     for key in res_properties:
81         vim_res, attr = res_properties[key]["source"].split('.', 1)
82         # action = res_properties[key].get("action", "copy")
83         if re.match(OBJ_IN_ARRAY, attr):
84             attr, index, sub_attr = re.match(OBJ_IN_ARRAY, attr).groups()
85             if _property_exists(vim_resource[vim_res], attr):
86                 mc_resource[key] = (
87                     vim_resource[vim_res][attr][int(index)][sub_attr])
88         else:
89             if _property_exists(vim_resource[vim_res], attr,
90                                 res_properties[key].get("required")):
91                 mc_resource[key] = vim_resource[vim_res][attr]
92             else:
93                 if "default" in res_properties[key]:
94                     mc_resource[key] = _convert_default_value(
95                         res_properties[key]["default"])
96
97     return mc_resource
98
99
100 def _convert_mc_res_to_vim_res(mc_resource, res_properties):
101     vim_resource = {}
102     for key in res_properties:
103         vim_res, attr = res_properties[key]["source"].split('.', 1)
104         # action = res_properties[key].get("action", "copy")
105         if re.match(OBJ_IN_ARRAY, attr):
106             attr, index, sub_attr = re.match(OBJ_IN_ARRAY, attr).groups()
107             if _property_exists(mc_resource, key):
108                 vim_resource[attr] = vim_resource.get(attr, [])
109                 if vim_resource[attr]:
110                     vim_resource[attr][0].update({sub_attr: mc_resource[key]})
111                 else:
112                     vim_resource[attr].append({sub_attr: mc_resource[key]})
113         else:
114             if _property_exists(mc_resource, key,
115                                 res_properties[key].get("required")):
116                 vim_resource[attr] = mc_resource[key]
117
118     return vim_resource
119
120
121 def _build_api_controller(api_meta):
122     # Assume that only one path
123     path, path_meta = api_meta['paths'].items()[0]
124     # url path is behind third slash. The first is vimid, the second is
125     # tenantid.
126     path = path.split("/")[3]
127     controller_name = path.upper() + "Controller"
128     delimiter = path_meta["vim_path"].find("/", 1)
129     service_type = path_meta["vim_path"][1:delimiter]
130     resource_url = path_meta["vim_path"][delimiter:]
131
132     # Assume there is only one resource.
133     name, resource_meta = api_meta['definitions'].items()[0]
134     resource_properties = resource_meta['properties']
135
136     controller_meta = {}
137     if "get" in path_meta:
138         # Add the get method to controller.
139         @pecan.expose("json")
140         def _get(self, vim_id, tenant_id, resource_id):
141             """ General GET """
142             session = _get_vim_auth_session(vim_id, tenant_id)
143             service = {'service_type': service_type,
144                        'interface': 'public'}
145             full_url = resource_url + "/%s" % resource_id
146             resp = session.get(full_url, endpoint_filter=service)
147             mc_res = _convert_vim_res_to_mc_res(resp.json(),
148                                                 resource_properties)
149             mc_res.update({"vimName": vim_id,
150                            "vimId": vim_id,
151                            "tenantId": tenant_id,
152                            "returnCode": 0})
153             return mc_res
154
155         controller_meta["get"] = _get
156
157     if "get_all" in path_meta:
158         # Add the get_all method to controller.
159         @pecan.expose("json")
160         def _get_all(self, vim_id, tenant_id):
161             """ General GET all """
162             session = _get_vim_auth_session(vim_id, tenant_id)
163             service = {'service_type': service_type,
164                        'interface': 'public'}
165             resp = session.get(resource_url, endpoint_filter=service)
166             vim_res = resp.json()[resource_meta['plural_vim_resource']]
167             mc_res = [_convert_vim_res_to_mc_res(
168                           {resource_meta['vim_resource']: v},
169                           resource_properties)
170                       for v in vim_res]
171             return {"vimName": vim_id,
172                     resource_meta['plural']: mc_res,
173                     "tenantId": tenant_id,
174                     "vimid": vim_id}
175
176         controller_meta["get_all"] = _get_all
177
178     if "post" in path_meta:
179         # Add the post method to controller.
180         @pecan.expose("json")
181         def _post(self, vim_id, tenant_id):
182             """ General POST """
183             session = _get_vim_auth_session(vim_id, tenant_id)
184             service = {'service_type': service_type,
185                        'interface': 'public'}
186             vim_res = _convert_mc_res_to_vim_res(pecan.request.json_body,
187                                                  resource_properties)
188
189             req_body = json.JSONEncoder().encode(
190                 {resource_meta['vim_resource']: vim_res})
191             resp = session.post(resource_url,
192                                 data=req_body,
193                                 endpoint_filter=service)
194             mc_res = _convert_vim_res_to_mc_res(resp.json(),
195                                                 resource_properties)
196             mc_res.update({"vimName": vim_id,
197                            "vimId": vim_id,
198                            "tenantId": tenant_id,
199                            "returnCode": 0})
200             return mc_res
201
202         controller_meta["post"] = _post
203
204     if "delete" in path_meta:
205         # Add the delete method to controller.
206         @pecan.expose("json")
207         def _delete(self, vim_id, tenant_id, resource_id):
208             """ General DELETE """
209             session = _get_vim_auth_session(vim_id, tenant_id)
210             service = {'service_type': service_type,
211                        'interface': 'public'}
212             full_url = resource_url + "/%s" % resource_id
213             session.delete(full_url, endpoint_filter=service)
214
215         controller_meta["delete"] = _delete
216
217     return path, type(controller_name, (rest.RestController,), controller_meta)
218
219
220 def insert_dynamic_controller(root_controller):
221     api_defs = utils.get_definition_list()
222     for d in api_defs:
223         path, con_class = _build_api_controller(d)
224         setattr(root_controller, path, con_class())