1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefónica Investigación y Desarrollo, S.A.U.
5 # This file is part of openvim
8 # Licensed under the Apache License, Version 2.0 (the "License"); you may
9 # not use this file except in compliance with the License. You may obtain
10 # a copy of the License at
12 # http://www.apache.org/licenses/LICENSE-2.0
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17 # License for the specific language governing permissions and limitations
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact with: nfvlabs@tid.es
25 This is the thread for the http server North API.
26 Two thread will be launched, with normal and administrative permissions.
29 __author__
="Alfonso Tierno, Gerardo Garcia, Leonardo Mirabal"
30 __date__
="$10-jul-2014 12:07:15$"
41 from netaddr
import IPNetwork
, IPAddress
, all_matching_cidrs
42 #import only if needed because not needed in test mode. To allow an easier installation import RADclass
43 from jsonschema
import validate
as js_v
, exceptions
as js_e
44 import host_thread
as ht
45 from vim_schema
import host_new_schema
, host_edit_schema
, tenant_new_schema
, \
47 flavor_new_schema
, flavor_update_schema
, \
48 image_new_schema
, image_update_schema
, \
49 server_new_schema
, server_action_schema
, network_new_schema
, network_update_schema
, \
50 port_new_schema
, port_update_schema
, openflow_controller_schema
, of_port_map_new_schema
57 global RADclass_module
58 RADclass_module
=None #RADclass module is charged only if not in test mode
62 HTTP_Bad_Request
= 400
63 HTTP_Unauthorized
= 401
66 HTTP_Method_Not_Allowed
= 405
67 HTTP_Not_Acceptable
= 406
68 HTTP_Request_Timeout
= 408
70 HTTP_Service_Unavailable
= 503
71 HTTP_Internal_Server_Error
= 500
74 hash_md5
= hashlib
.md5()
75 with
open(fname
, "rb") as f
:
76 for chunk
in iter(lambda: f
.read(4096), b
""):
77 hash_md5
.update(chunk
)
78 return hash_md5
.hexdigest()
80 def md5_string(fname
):
81 hash_md5
= hashlib
.md5()
82 hash_md5
.update(fname
)
83 return hash_md5
.hexdigest()
85 def check_extended(extended
, allow_net_attach
=False):
86 '''Makes and extra checking of extended input that cannot be done using jsonschema
88 allow_net_attach: for allowing or not the uuid field at interfaces
89 that are allowed for instance, but not for flavors
90 Return: (<0, error_text) if error; (0,None) if not error '''
91 if "numas" not in extended
: return 0, None
94 for numa
in extended
["numas"]:
98 if "cores-id" in numa
:
99 if len(numa
["cores-id"]) != numa
["cores"]:
100 return -HTTP_Bad_Request
, "different number of cores-id (%d) than cores (%d) at numa %d" % (len(numa
["cores-id"]), numa
["cores"],numaid
)
101 id_s
.extend(numa
["cores-id"])
102 if "threads" in numa
:
104 if "threads-id" in numa
:
105 if len(numa
["threads-id"]) != numa
["threads"]:
106 return -HTTP_Bad_Request
, "different number of threads-id (%d) than threads (%d) at numa %d" % (len(numa
["threads-id"]), numa
["threads"],numaid
)
107 id_s
.extend(numa
["threads-id"])
108 if "paired-threads" in numa
:
110 if "paired-threads-id" in numa
:
111 if len(numa
["paired-threads-id"]) != numa
["paired-threads"]:
112 return -HTTP_Bad_Request
, "different number of paired-threads-id (%d) than paired-threads (%d) at numa %d" % (len(numa
["paired-threads-id"]), numa
["paired-threads"],numaid
)
113 for pair
in numa
["paired-threads-id"]:
115 return -HTTP_Bad_Request
, "paired-threads-id must contain a list of two elements list at numa %d" % (numaid
)
118 return -HTTP_Service_Unavailable
, "only one of cores, threads, paired-threads are allowed in this version at numa %d" % numaid
120 if "interfaces" in numa
:
124 for interface
in numa
["interfaces"]:
125 if "uuid" in interface
and not allow_net_attach
:
126 return -HTTP_Bad_Request
, "uuid field is not allowed at numa %d interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
127 if "mac_address" in interface
and interface
["dedicated"]=="yes":
128 return -HTTP_Bad_Request
, "mac_address can not be set for dedicated (passthrough) at numa %d, interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
129 if "name" in interface
:
130 if interface
["name"] in names
:
131 return -HTTP_Bad_Request
, "name repeated at numa %d, interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
132 names
.append(interface
["name"])
133 if "vpci" in interface
:
134 if interface
["vpci"] in vpcis
:
135 return -HTTP_Bad_Request
, "vpci %s repeated at numa %d, interface %s position %d" % (interface
["vpci"], numaid
, interface
.get("name",""), ifaceid
)
136 vpcis
.append(interface
["vpci"])
140 return -HTTP_Service_Unavailable
, "only one numa can be defined in this version "
141 for a
in range(0,len(id_s
)):
143 return -HTTP_Bad_Request
, "core/thread identifiers must start at 0 and gaps are not alloed. Missing id number %d" % a
148 # dictionaries that change from HTTP API to database naming
150 http2db_id
={'id':'uuid'}
151 http2db_host
={'id':'uuid'}
152 http2db_tenant
={'id':'uuid'}
153 http2db_flavor
={'id':'uuid','imageRef':'image_id'}
154 http2db_image
={'id':'uuid', 'created':'created_at', 'updated':'modified_at', 'public': 'public'}
155 http2db_server
={'id':'uuid','hostId':'host_id','flavorRef':'flavor_id','imageRef':'image_id','created':'created_at'}
156 http2db_network
={'id':'uuid','provider:vlan':'vlan', 'provider:physical': 'provider'}
157 http2db_ofc
= {'id': 'uuid'}
158 http2db_port
={'id':'uuid', 'network_id':'net_id', 'mac_address':'mac', 'device_owner':'type','device_id':'instance_id','binding:switch_port':'switch_port','binding:vlan':'vlan', 'bandwidth':'Mbps'}
160 def remove_extra_items(data
, schema
):
162 if type(data
) is tuple or type(data
) is list:
164 a
= remove_extra_items(d
, schema
['items'])
165 if a
is not None: deleted
.append(a
)
166 elif type(data
) is dict:
167 for k
in data
.keys():
168 if 'properties' not in schema
or k
not in schema
['properties'].keys():
172 a
= remove_extra_items(data
[k
], schema
['properties'][k
])
173 if a
is not None: deleted
.append({k
:a
})
174 if len(deleted
) == 0: return None
175 elif len(deleted
) == 1: return deleted
[0]
178 def delete_nulls(var
):
179 if type(var
) is dict:
181 if var
[k
] is None: del var
[k
]
182 elif type(var
[k
]) is dict or type(var
[k
]) is list or type(var
[k
]) is tuple:
183 if delete_nulls(var
[k
]): del var
[k
]
184 if len(var
) == 0: return True
185 elif type(var
) is list or type(var
) is tuple:
187 if type(k
) is dict: delete_nulls(k
)
188 if len(var
) == 0: return True
192 class httpserver(threading
.Thread
):
193 def __init__(self
, ovim
, name
="http", host
='localhost', port
=8080, admin
=False, config_
=None):
195 Creates a new thread to attend the http connections
197 db_conn: database connection
198 name: name of this thread
199 host: ip or name where to listen
200 port: port where to listen
201 admin: if this has privileges of administrator or not
202 config_: unless the first thread must be provided. It is a global dictionary where to allocate the self variable
208 if config_
is not None:
210 if 'http_threads' not in config_dic
:
211 config_dic
['http_threads'] = {}
212 threading
.Thread
.__init
__(self
)
215 self
.db
= ovim
.db
#TODO OVIM remove
218 if name
in config_dic
:
219 print "httpserver Warning!!! Onether thread with the same name", name
221 while name
+str(n
) in config_dic
:
225 self
.url_preffix
= 'http://' + self
.host
+ ':' + str(self
.port
) + url_base
226 config_dic
['http_threads'][name
] = self
228 #Ensure that when the main program exits the thread will also exit
231 self
.logger
= logging
.getLogger("openvim.http")
234 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=True) #quiet=True
236 def gethost(self
, host_id
):
237 result
, content
= self
.db
.get_host(host_id
)
239 print "httpserver.gethost error %d %s" % (result
, content
)
240 bottle
.abort(-result
, content
)
242 print "httpserver.gethost host '%s' not found" % host_id
243 bottle
.abort(HTTP_Not_Found
, content
)
245 data
={'host' : content
}
246 convert_boolean(content
, ('admin_state_up',) )
247 change_keys_http2db(content
, http2db_host
, reverse
=True)
249 return format_out(data
)
251 @bottle.route(url_base
+ '/', method
='GET')
254 return 'works' #TODO: put links or redirection to /openvim???
260 def change_keys_http2db(data
, http_db
, reverse
=False):
261 '''Change keys of dictionary data according to the key_dict values
262 This allow change from http interface names to database names.
263 When reverse is True, the change is otherwise
265 data: can be a dictionary or a list
266 http_db: is a dictionary with hhtp names as keys and database names as value
267 reverse: by default change is done from http API to database. If True change is done otherwise
268 Return: None, but data is modified'''
269 if type(data
) is tuple or type(data
) is list:
271 change_keys_http2db(d
, http_db
, reverse
)
272 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
274 for k
,v
in http_db
.items():
275 if v
in data
: data
[k
]=data
.pop(v
)
277 for k
,v
in http_db
.items():
278 if k
in data
: data
[v
]=data
.pop(k
)
282 def format_out(data
):
283 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
284 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
285 bottle
.response
.content_type
='application/yaml'
286 return yaml
.safe_dump(data
, explicit_start
=True, indent
=4, default_flow_style
=False, tags
=False, encoding
='utf-8', allow_unicode
=True) #, canonical=True, default_style='"'
287 else: #by default json
288 bottle
.response
.content_type
='application/json'
289 #return data #json no style
290 return json
.dumps(data
, indent
=4) + "\n"
292 def format_in(schema
):
294 error_text
= "Invalid header format "
295 format_type
= bottle
.request
.headers
.get('Content-Type', 'application/json')
296 if 'application/json' in format_type
:
297 error_text
= "Invalid json format "
298 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
299 client_data
= json
.load(bottle
.request
.body
)
300 #client_data = bottle.request.json()
301 elif 'application/yaml' in format_type
:
302 error_text
= "Invalid yaml format "
303 client_data
= yaml
.load(bottle
.request
.body
)
304 elif format_type
== 'application/xml':
305 bottle
.abort(501, "Content-Type: application/xml not supported yet.")
307 print "HTTP HEADERS: " + str(bottle
.request
.headers
.items())
308 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
310 #if client_data == None:
311 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
315 #print "HTTP input data: ", str(client_data)
316 error_text
= "Invalid content "
317 js_v(client_data
, schema
)
320 except (ValueError, yaml
.YAMLError
) as exc
:
321 error_text
+= str(exc
)
323 bottle
.abort(HTTP_Bad_Request
, error_text
)
324 except js_e
.ValidationError
as exc
:
325 print "HTTP validate_in error, jsonschema exception ", exc
.message
, "at", exc
.path
326 print " CONTENT: " + str(bottle
.request
.body
.readlines())
328 if len(exc
.path
)>0: error_pos
=" at '" + ":".join(map(str, exc
.path
)) + "'"
329 bottle
.abort(HTTP_Bad_Request
, error_text
+ error_pos
+": "+exc
.message
)
331 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
334 def filter_query_string(qs
, http2db
, allowed
):
335 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
337 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
338 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
339 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
340 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
341 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
342 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
343 limit: limit dictated by user with the query string 'limit'. 100 by default
344 abort if not permitted, using bottel.abort
349 if type(qs
) is not bottle
.FormsDict
:
350 print '!!!!!!!!!!!!!!invalid query string not a dictionary'
351 # bottle.abort(HTTP_Internal_Server_Error, "call programmer")
355 select
+= qs
.getall(k
)
358 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field=" + v
+ "'")
363 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit=" + qs
[k
] + "'")
366 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '" + k
+ "=" + qs
[k
] + "'")
371 if len(select
) == 0: select
+= allowed
372 # change from http api to database naming
373 for i
in range(0, len(select
)):
376 select
[i
] = http2db
[k
]
377 change_keys_http2db(where
, http2db
)
378 # print "filter_query_string", select,where,limit
380 return select
, where
, limit
382 def convert_bandwidth(data
, reverse
=False):
383 '''Check the field bandwidth recursively and when found, it removes units and convert to number
384 It assumes that bandwidth is well formed
386 'data': dictionary bottle.FormsDict variable to be checked. None or empty is considered valid
387 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
391 if type(data
) is dict:
392 for k
in data
.keys():
393 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
394 convert_bandwidth(data
[k
], reverse
)
395 if "bandwidth" in data
:
397 value
=str(data
["bandwidth"])
399 pos
= value
.find("bps")
401 if value
[pos
-1]=="G": data
["bandwidth"] = int(data
["bandwidth"][:pos
-1]) * 1000
402 elif value
[pos
-1]=="k": data
["bandwidth"]= int(data
["bandwidth"][:pos
-1]) / 1000
403 else: data
["bandwidth"]= int(data
["bandwidth"][:pos
-1])
405 value
= int(data
["bandwidth"])
406 if value
% 1000 == 0: data
["bandwidth"]=str(value
/1000) + " Gbps"
407 else: data
["bandwidth"]=str(value
) + " Mbps"
409 print "convert_bandwidth exception for type", type(data
["bandwidth"]), " data", data
["bandwidth"]
411 if type(data
) is tuple or type(data
) is list:
413 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
414 convert_bandwidth(k
, reverse
)
416 def convert_boolean(data
, items
): #TODO OVIM delete
417 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
418 It assumes that bandwidth is well formed
420 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
421 'items': tuple of keys to convert
425 if type(data
) is dict:
426 for k
in data
.keys():
427 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
428 convert_boolean(data
[k
], items
)
430 if type(data
[k
]) is str:
431 if data
[k
]=="false": data
[k
]=False
432 elif data
[k
]=="true": data
[k
]=True
433 if type(data
) is tuple or type(data
) is list:
435 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
436 convert_boolean(k
, items
)
438 def convert_datetime2str(var
):
439 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
440 It enters recursively in the dict var finding this kind of variables
442 if type(var
) is dict:
443 for k
,v
in var
.items():
444 if type(v
) is datetime
.datetime
:
445 var
[k
]= v
.strftime('%Y-%m-%dT%H:%M:%S')
446 elif type(v
) is dict or type(v
) is list or type(v
) is tuple:
447 convert_datetime2str(v
)
448 if len(var
) == 0: return True
449 elif type(var
) is list or type(var
) is tuple:
451 convert_datetime2str(v
)
453 def check_valid_tenant(my
, tenant_id
):
456 return HTTP_Unauthorized
, "Needed admin privileges"
458 result
, _
= my
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
})
460 return HTTP_Not_Found
, "tenant '%s' not found" % tenant_id
465 Check if string value is a well-wormed url
466 :param url: string url
467 :return: True if is a valid url, False if is not well-formed
470 parsed_url
= urlparse
.urlparse(url
)
485 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
488 @bottle.hook('after_request')
490 #TODO: Alf: Is it needed??
491 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
497 @bottle.route(url_base
+ '/hosts', method
='GET')
498 def http_get_hosts():
499 return format_out(get_hosts())
503 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_host
,
504 ('id', 'name', 'description', 'status', 'admin_state_up', 'ip_name'))
506 myself
= config_dic
['http_threads'][ threading
.current_thread().name
]
507 result
, content
= myself
.db
.get_table(FROM
='hosts', SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
509 print "http_get_hosts Error", content
510 bottle
.abort(-result
, content
)
512 convert_boolean(content
, ('admin_state_up',) )
513 change_keys_http2db(content
, http2db_host
, reverse
=True)
515 row
['links'] = ( {'href': myself
.url_preffix
+ '/hosts/' + str(row
['id']), 'rel': 'bookmark'}, )
516 data
={'hosts' : content
}
519 @bottle.route(url_base
+ '/hosts/<host_id>', method
='GET')
520 def http_get_host_id(host_id
):
521 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
522 return my
.gethost(host_id
)
524 @bottle.route(url_base
+ '/hosts', method
='POST')
525 def http_post_hosts():
526 '''insert a host into the database. All resources are got and inserted'''
527 global RADclass_module
528 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
531 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
534 http_content
= format_in( host_new_schema
)
535 r
= remove_extra_items(http_content
, host_new_schema
)
536 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
537 change_keys_http2db(http_content
['host'], http2db_host
)
539 if 'host' in http_content
:
540 host
= http_content
['host']
541 if 'host-data' in http_content
:
542 host
.update(http_content
['host-data'])
544 host
= http_content
['host-data']
546 ip_name
= host
['ip_name']
548 password
= host
.get('password')
549 if host
.get('autodiscover'):
550 if not RADclass_module
:
552 RADclass_module
= imp
.find_module("RADclass")
553 except (IOError, ImportError) as e
:
554 raise ImportError("Cannot import RADclass.py Openvim not properly installed" +str(e
))
557 rad
= RADclass_module
.RADclass()
558 (return_status
, code
) = rad
.obtain_RAD(user
, password
, ip_name
)
561 if not return_status
:
562 print 'http_post_hosts ERROR obtaining RAD', code
563 bottle
.abort(HTTP_Bad_Request
, code
)
566 rad_structure
= yaml
.load(rad
.to_text())
567 print 'rad_structure\n---------------------'
568 print json
.dumps(rad_structure
, indent
=4)
569 print '---------------------'
571 WHERE_
={"family":rad_structure
['processor']['family'], 'manufacturer':rad_structure
['processor']['manufacturer'], 'version':rad_structure
['processor']['version']}
572 result
, content
= my
.db
.get_table(FROM
='host_ranking',
576 host
['ranking'] = content
[0]['ranking']
578 #error_text= "Host " + str(WHERE_)+ " not found in ranking table. Not valid for VIM management"
579 #bottle.abort(HTTP_Bad_Request, error_text)
581 warning_text
+= "Host " + str(WHERE_
)+ " not found in ranking table. Assuming lowest value 100\n"
582 host
['ranking'] = 100 #TODO: as not used in this version, set the lowest value
584 features
= rad_structure
['processor'].get('features', ())
585 host
['features'] = ",".join(features
)
588 for node
in (rad_structure
['resource topology']['nodes'] or {}).itervalues():
593 for core
in node
['cpu']['eligible_cores']:
594 eligible_cores
.extend(core
)
595 for core
in node
['cpu']['cores']:
596 for thread_id
in core
:
597 c
={'core_id': count
, 'thread_id': thread_id
}
598 if thread_id
not in eligible_cores
: c
['status'] = 'noteligible'
603 for port_k
, port_v
in node
['nics']['nic 0']['ports'].iteritems():
604 if port_v
['virtual']:
608 for port_k2
, port_v2
in node
['nics']['nic 0']['ports'].iteritems():
609 if port_v2
['virtual'] and port_v2
['PF_pci_id']==port_k
:
610 sriovs
.append({'pci':port_k2
, 'mac':port_v2
['mac'], 'source_name':port_v2
['source_name']})
612 #sort sriov according to pci and rename them to the vf number
613 new_sriovs
= sorted(sriovs
, key
=lambda k
: k
['pci'])
615 for sriov
in new_sriovs
:
616 sriov
['source_name'] = index
618 interfaces
.append ({'pci':str(port_k
), 'Mbps': port_v
['speed']/1000000, 'sriovs': new_sriovs
, 'mac':port_v
['mac'], 'source_name':port_v
['source_name']})
619 memory
=node
['memory']['node_size'] / (1024*1024*1024)
620 #memory=get_next_2pow(node['memory']['hugepage_nr'])
621 host
['numas'].append( {'numa_socket': node
['id'], 'hugepages': node
['memory']['hugepage_nr'], 'memory':memory
, 'interfaces': interfaces
, 'cores': cores
} )
622 # print json.dumps(host, indent=4)
623 # insert in data base
624 if "created_at" in host
:
625 del host
["created_at"]
626 for numa
in host
.get("numas", ()):
627 if "hugepages_consumed" in numa
:
628 del numa
["hugepages_consumed"]
629 result
, content
= my
.db
.new_host(host
)
631 if content
['admin_state_up']:
633 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
634 host_develop_mode
= True if config_dic
['mode']=='development' else False
635 host_develop_bridge_iface
= config_dic
.get('development_bridge', None)
636 thread
= ht
.host_thread(name
=host
.get('name',ip_name
), user
=user
, host
=ip_name
,
637 password
=host
.get('password'),
638 keyfile
=host
.get('keyfile', config_dic
["host_ssh_keyfile"]),
639 db
=config_dic
['db'], db_lock
=config_dic
['db_lock'],
640 test
=host_test_mode
, image_path
=config_dic
['host_image_path'],
641 version
=config_dic
['version'], host_id
=content
['uuid'],
642 develop_mode
=host_develop_mode
, develop_bridge_iface
=host_develop_bridge_iface
)
644 config_dic
['host_threads'][ content
['uuid'] ] = thread
646 if config_dic
['network_type'] == 'ovs':
648 create_dhcp_ovs_bridge()
649 config_dic
['host_threads'][content
['uuid']].insert_task("new-ovsbridge")
650 # check if more host exist
651 create_vxlan_mesh(content
['uuid'])
654 change_keys_http2db(content
, http2db_host
, reverse
=True)
655 if len(warning_text
)>0:
656 content
["warning"]= warning_text
657 data
={'host' : content
}
658 return format_out(data
)
660 bottle
.abort(HTTP_Bad_Request
, content
)
664 def delete_dhcp_ovs_bridge(vlan
, net_uuid
):
666 Delete bridges and port created during dhcp launching at openvim controller
667 :param vlan: net vlan id
668 :param net_uuid: network identifier
671 dhcp_path
= config_dic
['ovs_controller_file_path']
673 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
674 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
676 dhcp_controller
.delete_dhcp_port(vlan
, net_uuid
)
677 dhcp_controller
.delete_dhcp_server(vlan
, net_uuid
, dhcp_path
)
680 def create_dhcp_ovs_bridge():
682 Initialize bridge to allocate the dhcp server at openvim controller
685 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
686 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
688 dhcp_controller
.create_ovs_bridge()
691 def set_mac_dhcp(vm_ip
, vlan
, first_ip
, last_ip
, cidr
, mac
):
693 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
694 :param vm_ip: IP address asigned to a VM
695 :param vlan: Segmentation id
696 :param first_ip: First dhcp range ip
697 :param last_ip: Last dhcp range ip
698 :param cidr: net cidr
699 :param mac: VM vnic mac to be macthed with the IP received
703 ip_tools
= IPNetwork(cidr
)
704 cidr_len
= ip_tools
.prefixlen
705 dhcp_netmask
= str(ip_tools
.netmask
)
706 dhcp_path
= config_dic
['ovs_controller_file_path']
708 new_cidr
= [first_ip
+ '/' + str(cidr_len
)]
709 if not len(all_matching_cidrs(vm_ip
, new_cidr
)):
712 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
713 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
715 dhcp_controller
.set_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_netmask
, dhcp_path
)
718 def delete_mac_dhcp(vm_ip
, vlan
, mac
):
720 Delete into dhcp conf file the ip assigned to a specific MAC address
721 :param vm_ip: IP address asigned to a VM
722 :param vlan: Segmentation id
723 :param mac: VM vnic mac to be macthed with the IP received
727 dhcp_path
= config_dic
['ovs_controller_file_path']
729 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
730 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
732 dhcp_controller
.delete_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_path
)
735 def create_vxlan_mesh(host_id
):
737 Create vxlan mesh across all openvimc controller and computes.
738 :param host_id: host identifier
739 :param host_id: host identifier
742 dhcp_compute_name
= get_vxlan_interface("dhcp")
743 existing_hosts
= get_hosts()
744 if len(existing_hosts
['hosts']) > 0:
745 # vlxan mesh creation between openvim controller and computes
746 computes_available
= existing_hosts
['hosts']
748 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
749 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
751 for compute
in computes_available
:
752 vxlan_interface_name
= get_vxlan_interface(compute
['id'][:8])
753 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan", dhcp_compute_name
, dhcp_controller
.host
)
754 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, compute
['ip_name'])
756 # vlxan mesh creation between openvim computes
757 for count
, compute_owner
in enumerate(computes_available
):
758 for compute
in computes_available
:
759 if compute_owner
['id'] == compute
['id']:
762 vxlan_interface_name
= get_vxlan_interface(compute_owner
['id'][:8])
763 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, compute_owner
['ip_name'])
764 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan",
765 vxlan_interface_name
,
766 compute_owner
['ip_name'])
769 def delete_vxlan_mesh(host_id
):
771 Create a task for remove a specific compute of the vlxan mesh
772 :param host_id: host id to be deleted.
774 existing_hosts
= get_hosts()
775 computes_available
= existing_hosts
['hosts']
777 vxlan_interface_name
= get_vxlan_interface(host_id
[:8])
779 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
780 dhcp_host
= http_controller
.ovim
.get_dhcp_controller()
782 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
783 # remove bridge from openvim controller if no more computes exist
784 if len(existing_hosts
):
785 dhcp_host
.delete_ovs_bridge()
787 for compute
in computes_available
:
788 if host_id
== compute
['id']:
791 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
792 config_dic
['host_threads'][compute
['id']].insert_task("del-vxlan", vxlan_interface_name
)
795 def get_vxlan_interface(local_uuid
):
797 Genearte a vxlan interface name
798 :param local_uuid: host id
799 :return: vlxan-8digits
801 return 'vxlan-' + local_uuid
[:8]
804 @bottle.route(url_base
+ '/hosts/<host_id>', method
='PUT')
805 def http_put_host_id(host_id
):
806 '''modify a host into the database. All resources are got and inserted'''
807 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
810 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
813 http_content
= format_in( host_edit_schema
)
814 r
= remove_extra_items(http_content
, host_edit_schema
)
815 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
816 change_keys_http2db(http_content
['host'], http2db_host
)
819 result
, content
= my
.db
.edit_host(host_id
, http_content
['host'])
821 convert_boolean(content
, ('admin_state_up',) )
822 change_keys_http2db(content
, http2db_host
, reverse
=True)
823 data
={'host' : content
}
825 if config_dic
['network_type'] == 'ovs':
826 delete_vxlan_mesh(host_id
)
827 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
830 config_dic
['host_threads'][host_id
].name
= content
.get('name',content
['ip_name'])
831 config_dic
['host_threads'][host_id
].user
= content
['user']
832 config_dic
['host_threads'][host_id
].host
= content
['ip_name']
833 config_dic
['host_threads'][host_id
].insert_task("reload")
835 if config_dic
['network_type'] == 'ovs':
836 # create mesh with new host data
837 config_dic
['host_threads'][host_id
].insert_task("new-ovsbridge")
838 create_vxlan_mesh(host_id
)
841 return format_out(data
)
843 bottle
.abort(HTTP_Bad_Request
, content
)
848 @bottle.route(url_base
+ '/hosts/<host_id>', method
='DELETE')
849 def http_delete_host_id(host_id
):
850 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
853 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
854 result
, content
= my
.db
.delete_row('hosts', host_id
)
856 bottle
.abort(HTTP_Not_Found
, content
)
858 if config_dic
['network_type'] == 'ovs':
859 delete_vxlan_mesh(host_id
)
861 if host_id
in config_dic
['host_threads']:
862 if config_dic
['network_type'] == 'ovs':
863 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
864 config_dic
['host_threads'][host_id
].insert_task("exit")
866 data
={'result' : content
}
867 return format_out(data
)
869 print "http_delete_host_id error",result
, content
870 bottle
.abort(-result
, content
)
877 @bottle.route(url_base
+ '/tenants', method
='GET')
878 def http_get_tenants():
880 Retreive tenant list from DB
883 my
= config_dic
['http_threads'][threading
.current_thread().name
]
886 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_tenant
,
887 ('id', 'name', 'description', 'enabled'))
888 tenants
= my
.ovim
.get_tenants(select_
, where_
)
889 delete_nulls(tenants
)
890 change_keys_http2db(tenants
, http2db_tenant
, reverse
=True)
891 data
= {'tenants': tenants
}
892 return format_out(data
)
893 except ovim
.ovimException
as e
:
894 my
.logger
.error(str(e
), exc_info
=True)
895 bottle
.abort(e
.http_code
, str(e
))
896 except Exception as e
:
897 my
.logger
.error(str(e
), exc_info
=True)
898 bottle
.abort(HTTP_Bad_Request
, str(e
))
901 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
902 def http_get_tenant_id(tenant_id
):
904 Get tenant from DB by id
905 :param tenant_id: tenant id
908 my
= config_dic
['http_threads'][threading
.current_thread().name
]
911 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
913 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
914 data
= {'tenant': tenant
}
915 return format_out(data
)
916 except ovim
.ovimException
as e
:
917 my
.logger
.error(str(e
), exc_info
=True)
918 bottle
.abort(e
.http_code
, str(e
))
919 except Exception as e
:
920 my
.logger
.error(str(e
), exc_info
=True)
921 bottle
.abort(HTTP_Bad_Request
, str(e
))
924 @bottle.route(url_base
+ '/tenants', method
='POST')
925 def http_post_tenants():
927 Insert a tenant into the database.
930 my
= config_dic
['http_threads'][threading
.current_thread().name
]
933 http_content
= format_in(tenant_new_schema
)
934 r
= remove_extra_items(http_content
, tenant_new_schema
)
936 my
.logger
.error("http_post_tenants: Warning: remove extra items " + str(r
), exc_info
=True)
937 # insert in data base
938 tenant_id
= my
.ovim
.new_tentant(http_content
['tenant'])
939 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
940 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
942 data
= {'tenant': tenant
}
943 return format_out(data
)
944 except ovim
.ovimException
as e
:
945 my
.logger
.error(str(e
), exc_info
=True)
946 bottle
.abort(e
.http_code
, str(e
))
947 except Exception as e
:
948 my
.logger
.error(str(e
), exc_info
=True)
949 bottle
.abort(HTTP_Bad_Request
, str(e
))
952 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
953 def http_put_tenant_id(tenant_id
):
955 Update a tenantinto DB.
956 :param tenant_id: tentant id
960 my
= config_dic
['http_threads'][threading
.current_thread().name
]
963 http_content
= format_in(tenant_edit_schema
)
964 r
= remove_extra_items(http_content
, tenant_edit_schema
)
966 print "http_put_tenant_id: Warning: remove extra items ", r
967 change_keys_http2db(http_content
['tenant'], http2db_tenant
)
968 # insert in data base
969 my
.ovim
.edit_tenant(tenant_id
, http_content
['tenant'])
970 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
971 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
972 data
= {'tenant': tenant
}
973 return format_out(data
)
974 except ovim
.ovimException
as e
:
975 my
.logger
.error(str(e
), exc_info
=True)
976 bottle
.abort(e
.http_code
, str(e
))
977 except Exception as e
:
978 my
.logger
.error(str(e
), exc_info
=True)
979 bottle
.abort(HTTP_Bad_Request
, str(e
))
982 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
983 def http_delete_tenant_id(tenant_id
):
985 Delete a tenant from the database.
986 :param tenant_id: tenant id
989 my
= config_dic
['http_threads'][threading
.current_thread().name
]
992 content
= my
.ovim
.delete_tentant(tenant_id
)
993 data
= {'result': content
}
994 return format_out(data
)
995 except ovim
.ovimException
as e
:
996 my
.logger
.error(str(e
), exc_info
=True)
997 bottle
.abort(e
.http_code
, str(e
))
998 except Exception as e
:
999 my
.logger
.error(str(e
), exc_info
=True)
1000 bottle
.abort(HTTP_Bad_Request
, str(e
))
1006 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='GET')
1007 def http_get_flavors(tenant_id
):
1008 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1009 #check valid tenant_id
1010 result
,content
= check_valid_tenant(my
, tenant_id
)
1012 bottle
.abort(result
, content
)
1014 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1015 ('id','name','description','public') )
1016 if tenant_id
=='any':
1019 from_
='tenants_flavors inner join flavors on tenants_flavors.flavor_id=flavors.uuid'
1020 where_
['tenant_id'] = tenant_id
1021 result
, content
= my
.db
.get_table(FROM
=from_
, SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1023 print "http_get_flavors Error", content
1024 bottle
.abort(-result
, content
)
1026 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1028 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(row
['id']) ) ), 'rel':'bookmark' } ]
1029 data
={'flavors' : content
}
1030 return format_out(data
)
1032 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='GET')
1033 def http_get_flavor_id(tenant_id
, flavor_id
):
1034 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1035 #check valid tenant_id
1036 result
,content
= check_valid_tenant(my
, tenant_id
)
1038 bottle
.abort(result
, content
)
1040 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1041 ('id','name','description','ram', 'vcpus', 'extended', 'disk', 'public') )
1042 if tenant_id
=='any':
1045 from_
='tenants_flavors as tf inner join flavors as f on tf.flavor_id=f.uuid'
1046 where_
['tenant_id'] = tenant_id
1047 where_
['uuid'] = flavor_id
1048 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
=from_
, WHERE
=where_
, LIMIT
=limit_
)
1051 print "http_get_flavor_id error %d %s" % (result
, content
)
1052 bottle
.abort(-result
, content
)
1054 print "http_get_flavors_id flavor '%s' not found" % str(flavor_id
)
1055 bottle
.abort(HTTP_Not_Found
, 'flavor %s not found' % flavor_id
)
1057 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1058 if 'extended' in content
[0] and content
[0]['extended'] is not None:
1059 extended
= json
.loads(content
[0]['extended'])
1060 if 'devices' in extended
:
1061 change_keys_http2db(extended
['devices'], http2db_flavor
, reverse
=True)
1062 content
[0]['extended']=extended
1063 convert_bandwidth(content
[0], reverse
=True)
1064 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1065 data
={'flavor' : content
[0]}
1066 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1067 return format_out(data
)
1070 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='POST')
1071 def http_post_flavors(tenant_id
):
1072 '''insert a flavor into the database, and attach to tenant.'''
1073 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1074 #check valid tenant_id
1075 result
,content
= check_valid_tenant(my
, tenant_id
)
1077 bottle
.abort(result
, content
)
1078 http_content
= format_in( flavor_new_schema
)
1079 r
= remove_extra_items(http_content
, flavor_new_schema
)
1080 if r
is not None: print "http_post_flavors: Warning: remove extra items ", r
1081 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1082 extended_dict
= http_content
['flavor'].pop('extended', None)
1083 if extended_dict
is not None:
1084 result
, content
= check_extended(extended_dict
)
1086 print "http_post_flavors wrong input extended error %d %s" % (result
, content
)
1087 bottle
.abort(-result
, content
)
1089 convert_bandwidth(extended_dict
)
1090 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1091 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1092 #insert in data base
1093 result
, content
= my
.db
.new_flavor(http_content
['flavor'], tenant_id
)
1095 return http_get_flavor_id(tenant_id
, content
)
1097 print "http_psot_flavors error %d %s" % (result
, content
)
1098 bottle
.abort(-result
, content
)
1101 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='DELETE')
1102 def http_delete_flavor_id(tenant_id
, flavor_id
):
1103 '''Deletes the flavor_id of a tenant. IT removes from tenants_flavors table.'''
1104 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1105 #check valid tenant_id
1106 result
,content
= check_valid_tenant(my
, tenant_id
)
1108 bottle
.abort(result
, content
)
1110 result
, content
= my
.db
.delete_image_flavor('flavor', flavor_id
, tenant_id
)
1112 bottle
.abort(HTTP_Not_Found
, content
)
1114 data
={'result' : content
}
1115 return format_out(data
)
1117 print "http_delete_flavor_id error",result
, content
1118 bottle
.abort(-result
, content
)
1121 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>/<action>', method
='POST')
1122 def http_attach_detach_flavors(tenant_id
, flavor_id
, action
):
1123 '''attach/detach an existing flavor in this tenant. That is insert/remove at tenants_flavors table.'''
1124 #TODO alf: not tested at all!!!
1125 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1126 #check valid tenant_id
1127 result
,content
= check_valid_tenant(my
, tenant_id
)
1129 bottle
.abort(result
, content
)
1130 if tenant_id
=='any':
1131 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1133 if action
!='attach' and action
!= 'detach':
1134 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1137 #Ensure that flavor exist
1138 from_
='tenants_flavors as tf right join flavors as f on tf.flavor_id=f.uuid'
1139 where_
={'uuid': flavor_id
}
1140 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1142 if action
=='attach':
1143 text_error
="Flavor '%s' not found" % flavor_id
1145 text_error
="Flavor '%s' not found for tenant '%s'" % (flavor_id
, tenant_id
)
1146 bottle
.abort(HTTP_Not_Found
, text_error
)
1150 if action
=='attach':
1151 if flavor
['tenant_id']!=None:
1152 bottle
.abort(HTTP_Conflict
, "Flavor '%s' already attached to tenant '%s'" % (flavor_id
, tenant_id
))
1153 if flavor
['public']=='no' and not my
.admin
:
1154 #allow only attaching public flavors
1155 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private flavor")
1157 #insert in data base
1158 result
, content
= my
.db
.new_row('tenants_flavors', {'flavor_id':flavor_id
, 'tenant_id': tenant_id
})
1160 return http_get_flavor_id(tenant_id
, flavor_id
)
1162 if flavor
['tenant_id']==None:
1163 bottle
.abort(HTTP_Not_Found
, "Flavor '%s' not attached to tenant '%s'" % (flavor_id
, tenant_id
))
1164 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_flavors', WHERE
={'flavor_id':flavor_id
, 'tenant_id':tenant_id
})
1166 if flavor
['public']=='no':
1167 #try to delete the flavor completely to avoid orphan flavors, IGNORE error
1168 my
.db
.delete_row_by_dict(FROM
='flavors', WHERE
={'uuid':flavor_id
})
1169 data
={'result' : "flavor detached"}
1170 return format_out(data
)
1172 #if get here is because an error
1173 print "http_attach_detach_flavors error %d %s" % (result
, content
)
1174 bottle
.abort(-result
, content
)
1177 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='PUT')
1178 def http_put_flavor_id(tenant_id
, flavor_id
):
1179 '''update a flavor_id into the database.'''
1180 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1181 #check valid tenant_id
1182 result
,content
= check_valid_tenant(my
, tenant_id
)
1184 bottle
.abort(result
, content
)
1186 http_content
= format_in( flavor_update_schema
)
1187 r
= remove_extra_items(http_content
, flavor_update_schema
)
1188 if r
is not None: print "http_put_flavor_id: Warning: remove extra items ", r
1189 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1190 extended_dict
= http_content
['flavor'].pop('extended', None)
1191 if extended_dict
is not None:
1192 result
, content
= check_extended(extended_dict
)
1194 print "http_put_flavor_id wrong input extended error %d %s" % (result
, content
)
1195 bottle
.abort(-result
, content
)
1197 convert_bandwidth(extended_dict
)
1198 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1199 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1200 #Ensure that flavor exist
1201 where_
={'uuid': flavor_id
}
1202 if tenant_id
=='any':
1205 from_
='tenants_flavors as ti inner join flavors as i on ti.flavor_id=i.uuid'
1206 where_
['tenant_id'] = tenant_id
1207 result
, content
= my
.db
.get_table(SELECT
=('public',), FROM
=from_
, WHERE
=where_
)
1209 text_error
="Flavor '%s' not found" % flavor_id
1210 if tenant_id
!='any':
1211 text_error
+=" for tenant '%s'" % flavor_id
1212 bottle
.abort(HTTP_Not_Found
, text_error
)
1215 if content
[0]['public']=='yes' and not my
.admin
:
1216 #allow only modifications over private flavors
1217 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public flavor")
1219 #insert in data base
1220 result
, content
= my
.db
.update_rows('flavors', http_content
['flavor'], {'uuid': flavor_id
})
1223 print "http_put_flavor_id error %d %s" % (result
, content
)
1224 bottle
.abort(-result
, content
)
1227 return http_get_flavor_id(tenant_id
, flavor_id
)
1235 @bottle.route(url_base
+ '/<tenant_id>/images', method
='GET')
1236 def http_get_images(tenant_id
):
1237 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1238 #check valid tenant_id
1239 result
,content
= check_valid_tenant(my
, tenant_id
)
1241 bottle
.abort(result
, content
)
1243 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1244 ('id','name','checksum','description','path','public') )
1245 if tenant_id
=='any':
1249 from_
='tenants_images right join images on tenants_images.image_id=images.uuid'
1250 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1251 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1253 print "http_get_images Error", content
1254 bottle
.abort(-result
, content
)
1256 change_keys_http2db(content
, http2db_image
, reverse
=True)
1257 #for row in content: row['links']=[ {'href': "/".join( (my.url_preffix, tenant_id, 'images', str(row['id']) ) ), 'rel':'bookmark' } ]
1258 data
={'images' : content
}
1259 return format_out(data
)
1261 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='GET')
1262 def http_get_image_id(tenant_id
, image_id
):
1263 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1264 #check valid tenant_id
1265 result
,content
= check_valid_tenant(my
, tenant_id
)
1267 bottle
.abort(result
, content
)
1269 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1270 ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
1271 if tenant_id
=='any':
1275 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1276 where_or_
= {'tenant_id': tenant_id
, 'public': "yes"}
1277 where_
['uuid'] = image_id
1278 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1281 print "http_get_images error %d %s" % (result
, content
)
1282 bottle
.abort(-result
, content
)
1284 print "http_get_images image '%s' not found" % str(image_id
)
1285 bottle
.abort(HTTP_Not_Found
, 'image %s not found' % image_id
)
1287 convert_datetime2str(content
)
1288 change_keys_http2db(content
, http2db_image
, reverse
=True)
1289 if 'metadata' in content
[0] and content
[0]['metadata'] is not None:
1290 metadata
= json
.loads(content
[0]['metadata'])
1291 content
[0]['metadata']=metadata
1292 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'images', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1293 data
={'image' : content
[0]}
1294 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1295 return format_out(data
)
1297 @bottle.route(url_base
+ '/<tenant_id>/images', method
='POST')
1298 def http_post_images(tenant_id
):
1299 '''insert a image into the database, and attach to tenant.'''
1300 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1301 #check valid tenant_id
1302 result
,content
= check_valid_tenant(my
, tenant_id
)
1304 bottle
.abort(result
, content
)
1305 http_content
= format_in(image_new_schema
)
1306 r
= remove_extra_items(http_content
, image_new_schema
)
1307 if r
is not None: print "http_post_images: Warning: remove extra items ", r
1308 change_keys_http2db(http_content
['image'], http2db_image
)
1309 metadata_dict
= http_content
['image'].pop('metadata', None)
1310 if metadata_dict
is not None:
1311 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1314 image_file
= http_content
['image'].get('path',None)
1315 parsed_url
= urlparse
.urlparse(image_file
)
1316 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "":
1317 # The path is a local file
1318 if os
.path
.exists(image_file
):
1319 http_content
['image']['checksum'] = md5(image_file
)
1321 # The path is a URL. Code should be added to download the image and calculate the checksum
1322 #http_content['image']['checksum'] = md5(downloaded_image)
1324 # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
1325 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
1327 if 'checksum' not in http_content
['image']:
1328 http_content
['image']['checksum'] = md5_string(image_file
)
1330 # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
1331 # If it is a URL, no error is sent. Checksum will be an empty string
1332 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "" and 'checksum' not in http_content
['image']:
1333 content
= "Image file not found"
1334 print "http_post_images error: %d %s" % (HTTP_Bad_Request
, content
)
1335 bottle
.abort(HTTP_Bad_Request
, content
)
1336 except Exception as e
:
1337 print "ERROR. Unexpected exception: %s" % (str(e
))
1338 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1339 #insert in data base
1340 result
, content
= my
.db
.new_image(http_content
['image'], tenant_id
)
1342 return http_get_image_id(tenant_id
, content
)
1344 print "http_post_images error %d %s" % (result
, content
)
1345 bottle
.abort(-result
, content
)
1348 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='DELETE')
1349 def http_delete_image_id(tenant_id
, image_id
):
1350 '''Deletes the image_id of a tenant. IT removes from tenants_images table.'''
1351 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1352 #check valid tenant_id
1353 result
,content
= check_valid_tenant(my
, tenant_id
)
1355 bottle
.abort(result
, content
)
1356 result
, content
= my
.db
.delete_image_flavor('image', image_id
, tenant_id
)
1358 bottle
.abort(HTTP_Not_Found
, content
)
1360 data
={'result' : content
}
1361 return format_out(data
)
1363 print "http_delete_image_id error",result
, content
1364 bottle
.abort(-result
, content
)
1367 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>/<action>', method
='POST')
1368 def http_attach_detach_images(tenant_id
, image_id
, action
):
1369 '''attach/detach an existing image in this tenant. That is insert/remove at tenants_images table.'''
1370 #TODO alf: not tested at all!!!
1371 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1372 #check valid tenant_id
1373 result
,content
= check_valid_tenant(my
, tenant_id
)
1375 bottle
.abort(result
, content
)
1376 if tenant_id
=='any':
1377 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1379 if action
!='attach' and action
!= 'detach':
1380 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1383 #Ensure that image exist
1384 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1385 where_
={'uuid': image_id
}
1386 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1388 if action
=='attach':
1389 text_error
="Image '%s' not found" % image_id
1391 text_error
="Image '%s' not found for tenant '%s'" % (image_id
, tenant_id
)
1392 bottle
.abort(HTTP_Not_Found
, text_error
)
1396 if action
=='attach':
1397 if image
['tenant_id']!=None:
1398 bottle
.abort(HTTP_Conflict
, "Image '%s' already attached to tenant '%s'" % (image_id
, tenant_id
))
1399 if image
['public']=='no' and not my
.admin
:
1400 #allow only attaching public images
1401 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private image")
1403 #insert in data base
1404 result
, content
= my
.db
.new_row('tenants_images', {'image_id':image_id
, 'tenant_id': tenant_id
})
1406 return http_get_image_id(tenant_id
, image_id
)
1408 if image
['tenant_id']==None:
1409 bottle
.abort(HTTP_Not_Found
, "Image '%s' not attached to tenant '%s'" % (image_id
, tenant_id
))
1410 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_images', WHERE
={'image_id':image_id
, 'tenant_id':tenant_id
})
1412 if image
['public']=='no':
1413 #try to delete the image completely to avoid orphan images, IGNORE error
1414 my
.db
.delete_row_by_dict(FROM
='images', WHERE
={'uuid':image_id
})
1415 data
={'result' : "image detached"}
1416 return format_out(data
)
1418 #if get here is because an error
1419 print "http_attach_detach_images error %d %s" % (result
, content
)
1420 bottle
.abort(-result
, content
)
1423 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='PUT')
1424 def http_put_image_id(tenant_id
, image_id
):
1425 '''update a image_id into the database.'''
1426 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1427 #check valid tenant_id
1428 result
,content
= check_valid_tenant(my
, tenant_id
)
1430 bottle
.abort(result
, content
)
1432 http_content
= format_in( image_update_schema
)
1433 r
= remove_extra_items(http_content
, image_update_schema
)
1434 if r
is not None: print "http_put_image_id: Warning: remove extra items ", r
1435 change_keys_http2db(http_content
['image'], http2db_image
)
1436 metadata_dict
= http_content
['image'].pop('metadata', None)
1437 if metadata_dict
is not None:
1438 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1439 #Ensure that image exist
1440 where_
={'uuid': image_id
}
1441 if tenant_id
=='any':
1445 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1446 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1447 result
, content
= my
.db
.get_table(SELECT
=('public',), DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND")
1449 text_error
="Image '%s' not found" % image_id
1450 if tenant_id
!='any':
1451 text_error
+=" for tenant '%s'" % image_id
1452 bottle
.abort(HTTP_Not_Found
, text_error
)
1455 if content
[0]['public']=='yes' and not my
.admin
:
1456 #allow only modifications over private images
1457 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public image")
1459 #insert in data base
1460 result
, content
= my
.db
.update_rows('images', http_content
['image'], {'uuid': image_id
})
1463 print "http_put_image_id error %d %s" % (result
, content
)
1464 bottle
.abort(-result
, content
)
1467 return http_get_image_id(tenant_id
, image_id
)
1474 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='GET')
1475 def http_get_servers(tenant_id
):
1476 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1477 result
,content
= check_valid_tenant(my
, tenant_id
)
1479 bottle
.abort(result
, content
)
1482 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_server
,
1483 ('id','name','description','hostId','imageRef','flavorRef','status', 'tenant_id') )
1484 if tenant_id
!='any':
1485 where_
['tenant_id'] = tenant_id
1486 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
='instances', WHERE
=where_
, LIMIT
=limit_
)
1488 print "http_get_servers Error", content
1489 bottle
.abort(-result
, content
)
1491 change_keys_http2db(content
, http2db_server
, reverse
=True)
1493 tenant_id
= row
.pop('tenant_id')
1494 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'servers', str(row
['id']) ) ), 'rel':'bookmark' } ]
1495 data
={'servers' : content
}
1496 return format_out(data
)
1498 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='GET')
1499 def http_get_server_id(tenant_id
, server_id
):
1500 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1501 #check valid tenant_id
1502 result
,content
= check_valid_tenant(my
, tenant_id
)
1504 bottle
.abort(result
, content
)
1507 result
, content
= my
.db
.get_instance(server_id
)
1509 bottle
.abort(HTTP_Not_Found
, content
)
1511 #change image/flavor-id to id and link
1512 convert_bandwidth(content
, reverse
=True)
1513 convert_datetime2str(content
)
1514 if content
["ram"]==0 : del content
["ram"]
1515 if content
["vcpus"]==0 : del content
["vcpus"]
1516 if 'flavor_id' in content
:
1517 if content
['flavor_id'] is not None:
1518 content
['flavor'] = {'id':content
['flavor_id'],
1519 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'flavors', str(content
['flavor_id']) ) ), 'rel':'bookmark'}]
1521 del content
['flavor_id']
1522 if 'image_id' in content
:
1523 if content
['image_id'] is not None:
1524 content
['image'] = {'id':content
['image_id'],
1525 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'images', str(content
['image_id']) ) ), 'rel':'bookmark'}]
1527 del content
['image_id']
1528 change_keys_http2db(content
, http2db_server
, reverse
=True)
1529 if 'extended' in content
:
1530 if 'devices' in content
['extended']: change_keys_http2db(content
['extended']['devices'], http2db_server
, reverse
=True)
1532 data
={'server' : content
}
1533 return format_out(data
)
1535 bottle
.abort(-result
, content
)
1538 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='POST')
1539 def http_post_server_id(tenant_id
):
1540 '''deploys a new server'''
1541 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1542 #check valid tenant_id
1543 result
,content
= check_valid_tenant(my
, tenant_id
)
1545 bottle
.abort(result
, content
)
1547 if tenant_id
=='any':
1548 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1550 http_content
= format_in( server_new_schema
)
1551 r
= remove_extra_items(http_content
, server_new_schema
)
1552 if r
is not None: print "http_post_serves: Warning: remove extra items ", r
1553 change_keys_http2db(http_content
['server'], http2db_server
)
1554 extended_dict
= http_content
['server'].get('extended', None)
1555 if extended_dict
is not None:
1556 result
, content
= check_extended(extended_dict
, True)
1558 print "http_post_servers wrong input extended error %d %s" % (result
, content
)
1559 bottle
.abort(-result
, content
)
1561 convert_bandwidth(extended_dict
)
1562 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_server
)
1564 server
= http_content
['server']
1565 server_start
= server
.get('start', 'yes')
1566 server
['tenant_id'] = tenant_id
1567 #check flavor valid and take info
1568 result
, content
= my
.db
.get_table(FROM
='tenants_flavors as tf join flavors as f on tf.flavor_id=f.uuid',
1569 SELECT
=('ram','vcpus','extended'), WHERE
={'uuid':server
['flavor_id'], 'tenant_id':tenant_id
})
1571 bottle
.abort(HTTP_Not_Found
, 'flavor_id %s not found' % server
['flavor_id'])
1573 server
['flavor']=content
[0]
1574 #check image valid and take info
1575 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1576 SELECT
=('path', 'metadata', 'image_id'),
1577 WHERE
={'uuid':server
['image_id'], "status":"ACTIVE"},
1578 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'},
1582 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % server
['image_id'])
1584 for image_dict
in content
:
1585 if image_dict
.get("image_id"):
1588 # insert in data base tenants_images
1589 r2
, c2
= my
.db
.new_row('tenants_images', {'image_id': server
['image_id'], 'tenant_id': tenant_id
})
1591 bottle
.abort(HTTP_Not_Found
, 'image_id %s cannot be used. Error %s' % (server
['image_id'], c2
))
1593 server
['image']={"path": content
[0]["path"], "metadata": content
[0]["metadata"]}
1594 if "hosts_id" in server
:
1595 result
, content
= my
.db
.get_table(FROM
='hosts', SELECT
=('uuid',), WHERE
={'uuid': server
['host_id']})
1597 bottle
.abort(HTTP_Not_Found
, 'hostId %s not found' % server
['host_id'])
1599 #print json.dumps(server, indent=4)
1601 result
, content
= ht
.create_server(server
, config_dic
['db'], config_dic
['db_lock'], config_dic
['mode']=='normal')
1604 #Insert instance to database
1607 print "inserting at DB"
1609 if server_start
== 'no':
1610 content
['status'] = 'INACTIVE'
1612 for net
in http_content
['server']['networks']:
1613 if net
['type'] == 'instance:ovs':
1614 dhcp_nets_id
.append(get_network_id(net
['net_id']))
1617 new_instance_result
, new_instance
= my
.db
.new_instance(content
, nets
, ports_to_free
)
1618 if new_instance_result
< 0:
1619 print "Error http_post_servers() :", new_instance_result
, new_instance
1620 bottle
.abort(-new_instance_result
, new_instance
)
1623 print "inserted at DB"
1625 for port
in ports_to_free
:
1626 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1628 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1632 my
.ovim
.net_update_ofc_thread(net_id
)
1633 except ovim
.ovimException
as e
:
1634 my
.logger
.error("http_post_servers, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1636 # look for dhcp ip address
1637 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "ip_address", "net_id"], WHERE
={"instance_id": new_instance
})
1640 if config_dic
.get("dhcp_server") and iface
["net_id"] in config_dic
["dhcp_nets"]:
1641 #print "dhcp insert add task"
1642 r
,c
= config_dic
['dhcp_thread'].insert_task("add", iface
["mac"])
1644 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1646 #ensure compute contain the bridge for ovs networks:
1647 if iface
.get("net_id"):
1648 server_net
= get_network_id(iface
['net_id'])
1649 if server_net
["network"].get('provider:physical', "")[:3] == 'OVS':
1650 vlan
= str(server_net
['network']['provider:vlan'])
1651 dhcp_enable
= bool(server_net
['network']['enable_dhcp'])
1653 dhcp_firt_ip
= str(server_net
['network']['dhcp_first_ip'])
1654 dhcp_last_ip
= str(server_net
['network']['dhcp_last_ip'])
1655 dhcp_cidr
= str(server_net
['network']['cidr'])
1656 gateway
= str(server_net
['network']['gateway_ip'])
1657 vm_dhcp_ip
= c2
[0]["ip_address"]
1658 config_dic
['host_threads'][server
['host_id']].insert_task("create-ovs-bridge-port", vlan
)
1660 set_mac_dhcp(vm_dhcp_ip
, vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, c2
[0]['mac'])
1661 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
1662 http_controller
.ovim
.launch_dhcp_server(vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, gateway
)
1665 server
['uuid'] = new_instance
1666 server_start
= server
.get('start', 'yes')
1668 if server_start
!= 'no':
1669 server
['paused'] = True if server_start
== 'paused' else False
1670 server
['action'] = {"start":None}
1671 server
['status'] = "CREATING"
1673 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1675 my
.db
.update_rows('instances', {'status':"ERROR"}, {'uuid':server
['uuid'], 'last_error':c
}, log
=True)
1677 return http_get_server_id(tenant_id
, new_instance
)
1679 bottle
.abort(HTTP_Bad_Request
, content
)
1682 def http_server_action(server_id
, tenant_id
, action
):
1683 '''Perform actions over a server as resume, reboot, terminate, ...'''
1684 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1685 server
={"uuid": server_id
, "action":action
}
1686 where
={'uuid': server_id
}
1687 if tenant_id
!='any':
1688 where
['tenant_id']= tenant_id
1689 result
, content
= my
.db
.get_table(FROM
='instances', WHERE
=where
)
1691 bottle
.abort(HTTP_Not_Found
, "server %s not found" % server_id
)
1694 print "http_post_server_action error getting data %d %s" % (result
, content
)
1695 bottle
.abort(HTTP_Internal_Server_Error
, content
)
1697 server
.update(content
[0])
1698 tenant_id
= server
["tenant_id"]
1700 #TODO check a right content
1702 if 'terminate' in action
:
1703 new_status
='DELETING'
1704 elif server
['status'] == 'ERROR': #or server['status'] == 'CREATING':
1705 if 'terminate' not in action
and 'rebuild' not in action
:
1706 bottle
.abort(HTTP_Method_Not_Allowed
, "Server is in ERROR status, must be rebuit or deleted ")
1708 # elif server['status'] == 'INACTIVE':
1709 # if 'start' not in action and 'createImage' not in action:
1710 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'INACTIVE' status is 'start'")
1712 # if 'start' in action:
1713 # new_status='CREATING'
1714 # server['paused']='no'
1715 # elif server['status'] == 'PAUSED':
1716 # if 'resume' not in action:
1717 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'PAUSED' status is 'resume'")
1719 # elif server['status'] == 'ACTIVE':
1720 # if 'pause' not in action and 'reboot'not in action and 'shutoff'not in action:
1721 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'ACTIVE' status is 'pause','reboot' or 'shutoff'")
1724 if 'start' in action
or 'createImage' in action
or 'rebuild' in action
:
1725 #check image valid and take info
1726 image_id
= server
['image_id']
1727 if 'createImage' in action
:
1728 if 'imageRef' in action
['createImage']:
1729 image_id
= action
['createImage']['imageRef']
1730 elif 'disk' in action
['createImage']:
1731 result
, content
= my
.db
.get_table(FROM
='instance_devices',
1732 SELECT
=('image_id','dev'), WHERE
={'instance_id':server
['uuid'],"type":"disk"})
1734 bottle
.abort(HTTP_Not_Found
, 'disk not found for server')
1738 if action
['createImage']['imageRef']['disk'] != None:
1739 for disk
in content
:
1740 if disk
['dev'] == action
['createImage']['imageRef']['disk']:
1741 disk_id
= disk
['image_id']
1744 bottle
.abort(HTTP_Not_Found
, 'disk %s not found for server' % action
['createImage']['imageRef']['disk'])
1747 bottle
.abort(HTTP_Not_Found
, 'more than one disk found for server' )
1751 image_id
= content
[0]['image_id']
1753 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1754 SELECT
=('path','metadata'), WHERE
={'uuid':image_id
, "status":"ACTIVE"},
1755 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'}, WHERE_AND_OR
="AND", DISTINCT
=True)
1757 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % image_id
)
1759 if content
[0]['metadata'] is not None:
1761 metadata
= json
.loads(content
[0]['metadata'])
1763 return -HTTP_Internal_Server_Error
, "Can not decode image metadata"
1764 content
[0]['metadata']=metadata
1766 content
[0]['metadata'] = {}
1767 server
['image']=content
[0]
1768 if 'createImage' in action
:
1769 action
['createImage']['source'] = {'image_id': image_id
, 'path': content
[0]['path']}
1770 if 'createImage' in action
:
1771 #Create an entry in Database for the new image
1772 new_image
={'status':'BUILD', 'progress': 0 }
1773 new_image_metadata
=content
[0]
1774 if 'metadata' in server
['image'] and server
['image']['metadata'] != None:
1775 new_image_metadata
.update(server
['image']['metadata'])
1776 new_image_metadata
= {"use_incremental":"no"}
1777 if 'metadata' in action
['createImage']:
1778 new_image_metadata
.update(action
['createImage']['metadata'])
1779 new_image
['metadata'] = json
.dumps(new_image_metadata
)
1780 new_image
['name'] = action
['createImage'].get('name', None)
1781 new_image
['description'] = action
['createImage'].get('description', None)
1782 new_image
['uuid']=my
.db
.new_uuid()
1783 if 'path' in action
['createImage']:
1784 new_image
['path'] = action
['createImage']['path']
1786 new_image
['path']="/provisional/path/" + new_image
['uuid']
1787 result
, image_uuid
= my
.db
.new_image(new_image
, tenant_id
)
1789 bottle
.abort(HTTP_Bad_Request
, 'Error: ' + image_uuid
)
1791 server
['new_image'] = new_image
1795 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1797 print "Task queue full at host ", server
['host_id']
1798 bottle
.abort(HTTP_Request_Timeout
, c
)
1799 if 'createImage' in action
and result
>= 0:
1800 return http_get_image_id(tenant_id
, image_uuid
)
1802 #Update DB only for CREATING or DELETING status
1803 data
={'result' : 'deleting in process'}
1805 if new_status
!= None and new_status
== 'DELETING':
1810 #look for dhcp ip address
1811 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "net_id"], WHERE
={"instance_id": server_id
})
1812 r
, c
= my
.db
.delete_instance(server_id
, tenant_id
, nets
, ports_to_free
, net_ovs_list
, "requested by http")
1813 for port
in ports_to_free
:
1814 r1
,c1
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1816 my
.logger
.error("http_post_server_action server deletion ERROR at resore-iface!!!! " + c1
)
1817 warn_text
+= "; Error iface '{}' cannot be restored '{}'".format(str(port
), str(e
))
1820 my
.ovim
.net_update_ofc_thread(net_id
)
1821 except ovim
.ovimException
as e
:
1822 my
.logger
.error("http_server_action, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1823 warn_text
+= "; Error openflow rules of network '{}' cannot be restore '{}'".format(net_id
, str (e
))
1825 # look for dhcp ip address
1826 if r2
>0 and config_dic
.get("dhcp_server"):
1828 if iface
["net_id"] in config_dic
["dhcp_nets"]:
1829 r
,c
= config_dic
['dhcp_thread'].insert_task("del", iface
["mac"])
1830 #print "dhcp insert del task"
1832 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1833 # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan)
1834 for net
in net_ovs_list
:
1839 delete_dhcp_ovs_bridge(vlan
, net_id
)
1840 delete_mac_dhcp(vm_ip
, vlan
, mac
)
1841 config_dic
['host_threads'][server
['host_id']].insert_task('del-ovs-port', vlan
, net_id
)
1843 data
["result"] += warn_text
1844 return format_out(data
)
1848 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='DELETE')
1849 def http_delete_server_id(tenant_id
, server_id
):
1850 '''delete a server'''
1851 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1852 #check valid tenant_id
1853 result
,content
= check_valid_tenant(my
, tenant_id
)
1855 bottle
.abort(result
, content
)
1858 return http_server_action(server_id
, tenant_id
, {"terminate":None} )
1861 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>/action', method
='POST')
1862 def http_post_server_action(tenant_id
, server_id
):
1863 '''take an action over a server'''
1864 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1865 #check valid tenant_id
1866 result
,content
= check_valid_tenant(my
, tenant_id
)
1868 bottle
.abort(result
, content
)
1870 http_content
= format_in( server_action_schema
)
1871 #r = remove_extra_items(http_content, server_action_schema)
1872 #if r is not None: print "http_post_server_action: Warning: remove extra items ", r
1874 return http_server_action(server_id
, tenant_id
, http_content
)
1881 @bottle.route(url_base
+ '/networks', method
='GET')
1882 def http_get_networks():
1884 Get all networks available
1887 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1891 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_network
,
1892 ('id', 'name', 'tenant_id', 'type',
1893 'shared', 'provider:vlan', 'status', 'last_error',
1894 'admin_state_up', 'provider:physical'))
1895 if "tenant_id" in where_
:
1896 del where_
["tenant_id"]
1898 content
= my
.ovim
.get_networks(select_
, where_
, limit_
)
1900 delete_nulls(content
)
1901 change_keys_http2db(content
, http2db_network
, reverse
=True)
1902 data
= {'networks': content
}
1903 return format_out(data
)
1905 except ovim
.ovimException
as e
:
1906 my
.logger
.error(str(e
), exc_info
=True)
1907 bottle
.abort(e
.http_code
, str(e
))
1908 except Exception as e
:
1909 my
.logger
.error(str(e
), exc_info
=True)
1910 bottle
.abort(HTTP_Bad_Request
, str(e
))
1913 @bottle.route(url_base
+ '/networks/<network_id>', method
='GET')
1914 def http_get_network_id(network_id
):
1916 Get a network data by id
1920 data
= get_network_id(network_id
)
1921 return format_out(data
)
1924 def get_network_id(network_id
):
1926 Get network from DB by id
1927 :param network_id: network Id
1930 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1934 where_
= bottle
.request
.query
1935 content
= my
.ovim
.show_network(network_id
, where_
)
1937 change_keys_http2db(content
, http2db_network
, reverse
=True)
1938 delete_nulls(content
)
1939 data
= {'network': content
}
1941 except ovim
.ovimException
as e
:
1942 my
.logger
.error(str(e
), exc_info
=True)
1943 bottle
.abort(e
.http_code
, str(e
))
1944 except Exception as e
:
1945 my
.logger
.error(str(e
), exc_info
=True)
1946 bottle
.abort(HTTP_Bad_Request
, str(e
))
1949 @bottle.route(url_base
+ '/networks', method
='POST')
1950 def http_post_networks():
1952 Insert a network into the database.
1955 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1959 http_content
= format_in(network_new_schema
)
1960 r
= remove_extra_items(http_content
, network_new_schema
)
1962 print "http_post_networks: Warning: remove extra items ", r
1963 change_keys_http2db(http_content
['network'], http2db_network
)
1964 network
= http_content
['network']
1965 content
= my
.ovim
.new_network(network
)
1966 return format_out(get_network_id(content
))
1967 except ovim
.ovimException
as e
:
1968 my
.logger
.error(str(e
), exc_info
=True)
1969 bottle
.abort(e
.http_code
, str(e
))
1970 except Exception as e
:
1971 my
.logger
.error(str(e
), exc_info
=True)
1972 bottle
.abort(HTTP_Bad_Request
, str(e
))
1975 @bottle.route(url_base
+ '/networks/<network_id>', method
='PUT')
1976 def http_put_network_id(network_id
):
1978 Update a network_id into DB.
1979 :param network_id: network id
1982 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1986 http_content
= format_in(network_update_schema
)
1987 change_keys_http2db(http_content
['network'], http2db_network
)
1988 network
= http_content
['network']
1989 return format_out(my
.ovim
.edit_network(network_id
, network
))
1991 except ovim
.ovimException
as e
:
1992 my
.logger
.error(str(e
), exc_info
=True)
1993 bottle
.abort(e
.http_code
, str(e
))
1994 except Exception as e
:
1995 my
.logger
.error(str(e
), exc_info
=True)
1996 bottle
.abort(HTTP_Bad_Request
, str(e
))
1999 @bottle.route(url_base
+ '/networks/<network_id>', method
='DELETE')
2000 def http_delete_network_id(network_id
):
2002 Delete a network_id from the database.
2003 :param network_id: Network id
2006 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2009 # delete from the data base
2010 content
= my
.ovim
.delete_network(network_id
)
2011 data
= {'result': content
}
2012 return format_out(data
)
2014 except ovim
.ovimException
as e
:
2015 my
.logger
.error(str(e
), exc_info
=True)
2016 bottle
.abort(e
.http_code
, str(e
))
2017 except Exception as e
:
2018 my
.logger
.error(str(e
), exc_info
=True)
2019 bottle
.abort(HTTP_Bad_Request
, str(e
))
2026 @bottle.route(url_base
+ '/openflow/controller', method
='GET')
2027 def http_get_openflow_controller():
2029 Retrieve a openflow controllers list from DB.
2032 # TODO check if show a proper list
2033 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2036 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_ofc
,
2037 ('id', 'name', 'dpid', 'ip', 'port', 'type',
2038 'version', 'user', 'password'))
2040 content
= my
.ovim
.get_of_controllers(select_
, where_
)
2041 delete_nulls(content
)
2042 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2043 data
= {'ofcs': content
}
2044 return format_out(data
)
2045 except ovim
.ovimException
as e
:
2046 my
.logger
.error(str(e
), exc_info
=True)
2047 bottle
.abort(e
.http_code
, str(e
))
2048 except Exception as e
:
2049 my
.logger
.error(str(e
), exc_info
=True)
2050 bottle
.abort(HTTP_Bad_Request
, str(e
))
2053 @bottle.route(url_base
+ '/openflow/controller/<uuid>', method
='GET')
2054 def http_get_openflow_controller_id(uuid
):
2056 Get an openflow controller by dpid from DB.get_of_controllers
2058 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2062 content
= my
.ovim
.show_of_controller(uuid
)
2063 delete_nulls(content
)
2064 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2065 data
= {'ofc': content
}
2066 return format_out(data
)
2067 except ovim
.ovimException
as e
:
2068 my
.logger
.error(str(e
), exc_info
=True)
2069 bottle
.abort(e
.http_code
, str(e
))
2070 except Exception as e
:
2071 my
.logger
.error(str(e
), exc_info
=True)
2072 bottle
.abort(HTTP_Bad_Request
, str(e
))
2075 @bottle.route(url_base
+ '/openflow/controller/', method
='POST')
2076 def http_post_openflow_controller():
2078 Create a new openflow controller into DB
2081 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2084 http_content
= format_in(openflow_controller_schema
)
2085 of_c
= http_content
['ofc']
2086 uuid
= my
.ovim
.new_of_controller(of_c
)
2087 content
= my
.ovim
.show_of_controller(uuid
)
2088 delete_nulls(content
)
2089 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2090 data
= {'ofc': content
}
2091 return format_out(data
)
2092 except ovim
.ovimException
as e
:
2093 my
.logger
.error(str(e
), exc_info
=True)
2094 bottle
.abort(e
.http_code
, str(e
))
2095 except Exception as e
:
2096 my
.logger
.error(str(e
), exc_info
=True)
2097 bottle
.abort(HTTP_Bad_Request
, str(e
))
2100 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='PUT')
2101 def http_put_openflow_controller_by_id(of_controller_id
):
2103 Create an openflow controller into DB
2104 :param of_controller_id: openflow controller dpid
2107 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2110 http_content
= format_in(openflow_controller_schema
)
2111 of_c
= http_content
['ofc']
2113 content
= my
.ovim
.edit_of_controller(of_controller_id
, of_c
)
2114 delete_nulls(content
)
2115 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2116 data
= {'ofc': content
}
2117 return format_out(data
)
2118 except ovim
.ovimException
as e
:
2119 my
.logger
.error(str(e
), exc_info
=True)
2120 bottle
.abort(e
.http_code
, str(e
))
2121 except Exception as e
:
2122 my
.logger
.error(str(e
), exc_info
=True)
2123 bottle
.abort(HTTP_Bad_Request
, str(e
))
2126 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='DELETE')
2127 def http_delete_openflow_controller(of_controller_id
):
2129 Delete an openflow controller from DB.
2130 :param of_controller_id: openflow controller dpid
2133 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2136 content
= my
.ovim
.delete_of_controller(of_controller_id
)
2137 data
= {'result': content
}
2138 return format_out(data
)
2139 except ovim
.ovimException
as e
:
2140 my
.logger
.error(str(e
), exc_info
=True)
2141 bottle
.abort(e
.http_code
, str(e
))
2142 except Exception as e
:
2143 my
.logger
.error(str(e
), exc_info
=True)
2144 bottle
.abort(HTTP_Bad_Request
, str(e
))
2147 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='GET')
2148 def http_get_openflow_id(network_id
):
2150 To obtain the list of openflow rules of a network
2151 :param network_id: network id
2154 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2157 if network_id
== 'all':
2160 content
= my
.ovim
.get_openflow_rules(network_id
)
2161 data
= {'openflow-rules': content
}
2162 except ovim
.ovimException
as e
:
2163 my
.logger
.error(str(e
), exc_info
=True)
2164 bottle
.abort(e
.http_code
, str(e
))
2165 except Exception as e
:
2166 my
.logger
.error(str(e
), exc_info
=True)
2167 bottle
.abort(HTTP_Bad_Request
, str(e
))
2169 return format_out(data
)
2172 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='PUT')
2173 def http_put_openflow_id(network_id
):
2175 To make actions over the net. The action is to reinstall the openflow rules
2176 network_id can be 'all'
2177 :param network_id: network id
2180 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2183 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2185 if network_id
== 'all':
2189 result
= my
.ovim
.edit_openflow_rules(network_id
)
2190 except ovim
.ovimException
as e
:
2191 my
.logger
.error(str(e
), exc_info
=True)
2192 bottle
.abort(e
.http_code
, str(e
))
2193 except Exception as e
:
2194 my
.logger
.error(str(e
), exc_info
=True)
2195 bottle
.abort(HTTP_Bad_Request
, str(e
))
2197 data
= {'result': str(result
) + " nets updates"}
2198 return format_out(data
)
2200 @bottle.route(url_base
+ '/networks/clear/openflow/<ofc_id>', method
='DELETE')
2201 @bottle.route(url_base
+ '/networks/clear/openflow', method
='DELETE')
2202 def http_clear_openflow_rules(ofc_id
=None):
2204 To make actions over the net. The action is to delete ALL openflow rules
2207 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2210 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2212 my
.ovim
.delete_openflow_rules(ofc_id
)
2213 except ovim
.ovimException
as e
:
2214 my
.logger
.error(str(e
), exc_info
=True)
2215 bottle
.abort(e
.http_code
, str(e
))
2216 except Exception as e
:
2217 my
.logger
.error(str(e
), exc_info
=True)
2218 bottle
.abort(HTTP_Bad_Request
, str(e
))
2220 data
= {'result': " Clearing openflow rules in process"}
2221 return format_out(data
)
2223 @bottle.route(url_base
+ '/networks/openflow/ports/<ofc_id>', method
='GET')
2224 @bottle.route(url_base
+ '/networks/openflow/ports', method
='GET')
2225 def http_get_openflow_ports(ofc_id
=None):
2227 Obtain switch ports names of openflow controller
2230 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2233 ports
= my
.ovim
.get_openflow_ports(ofc_id
)
2234 data
= {'ports': ports
}
2235 except ovim
.ovimException
as e
:
2236 my
.logger
.error(str(e
), exc_info
=True)
2237 bottle
.abort(e
.http_code
, str(e
))
2238 except Exception as e
:
2239 my
.logger
.error(str(e
), exc_info
=True)
2240 bottle
.abort(HTTP_Bad_Request
, str(e
))
2242 return format_out(data
)
2248 @bottle.route(url_base
+ '/ports', method
='GET')
2249 def http_get_ports():
2251 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2252 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_port
,
2253 ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
2254 'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
2256 ports
= my
.ovim
.get_ports(columns
=select_
, filter=where_
, limit
=limit_
)
2258 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2259 data
={'ports' : ports
}
2260 return format_out(data
)
2261 except ovim
.ovimException
as e
:
2262 my
.logger
.error(str(e
), exc_info
=True)
2263 bottle
.abort(e
.http_code
, str(e
))
2264 except Exception as e
:
2265 my
.logger
.error(str(e
), exc_info
=True)
2266 bottle
.abort(HTTP_Bad_Request
, str(e
))
2268 @bottle.route(url_base
+ '/ports/<port_id>', method
='GET')
2269 def http_get_port_id(port_id
):
2270 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2272 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2274 bottle
.abort(HTTP_Not_Found
, 'port %s not found' % port_id
)
2277 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2278 data
= {'port': ports
[0]}
2279 return format_out(data
)
2280 except ovim
.ovimException
as e
:
2281 my
.logger
.error(str(e
), exc_info
=True)
2282 bottle
.abort(e
.http_code
, str(e
))
2283 except Exception as e
:
2284 my
.logger
.error(str(e
), exc_info
=True)
2285 bottle
.abort(HTTP_Bad_Request
, str(e
))
2287 @bottle.route(url_base
+ '/ports', method
='POST')
2288 def http_post_ports():
2289 '''insert an external port into the database.'''
2290 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2292 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2294 http_content
= format_in( port_new_schema
)
2295 r
= remove_extra_items(http_content
, port_new_schema
)
2296 if r
is not None: print "http_post_ports: Warning: remove extra items ", r
2297 change_keys_http2db(http_content
['port'], http2db_port
)
2298 port
=http_content
['port']
2300 port_id
= my
.ovim
.new_port(port
)
2301 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2303 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' inserted but not found at database".format(port_id
))
2306 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2307 data
= {'port': ports
[0]}
2308 return format_out(data
)
2309 except ovim
.ovimException
as e
:
2310 my
.logger
.error(str(e
), exc_info
=True)
2311 bottle
.abort(e
.http_code
, str(e
))
2312 except Exception as e
:
2313 my
.logger
.error(str(e
), exc_info
=True)
2314 bottle
.abort(HTTP_Bad_Request
, str(e
))
2316 @bottle.route(url_base
+ '/ports/<port_id>', method
='PUT')
2317 def http_put_port_id(port_id
):
2318 '''update a port_id into the database.'''
2319 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2321 http_content
= format_in( port_update_schema
)
2322 change_keys_http2db(http_content
['port'], http2db_port
)
2323 port_dict
=http_content
['port']
2325 for k
in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
2326 if k
in port_dict
and not my
.admin
:
2327 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges for changing " + k
)
2330 port_id
= my
.ovim
.edit_port(port_id
, port_dict
, my
.admin
)
2331 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2333 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' edited but not found at database".format(port_id
))
2336 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2337 data
= {'port': ports
[0]}
2338 return format_out(data
)
2339 except ovim
.ovimException
as e
:
2340 my
.logger
.error(str(e
), exc_info
=True)
2341 bottle
.abort(e
.http_code
, str(e
))
2342 except Exception as e
:
2343 my
.logger
.error(str(e
), exc_info
=True)
2344 bottle
.abort(HTTP_Bad_Request
, str(e
))
2347 @bottle.route(url_base
+ '/ports/<port_id>', method
='DELETE')
2348 def http_delete_port_id(port_id
):
2349 '''delete a port_id from the database.'''
2350 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2352 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2355 result
= my
.ovim
.delete_port(port_id
)
2356 data
= {'result': result
}
2357 return format_out(data
)
2358 except ovim
.ovimException
as e
:
2359 my
.logger
.error(str(e
), exc_info
=True)
2360 bottle
.abort(e
.http_code
, str(e
))
2361 except Exception as e
:
2362 my
.logger
.error(str(e
), exc_info
=True)
2363 bottle
.abort(HTTP_Bad_Request
, str(e
))
2366 @bottle.route(url_base
+ '/openflow/mapping', method
='POST')
2367 def http_of_port_mapping():
2369 Create new compute port mapping entry
2372 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2375 http_content
= format_in(of_port_map_new_schema
)
2376 r
= remove_extra_items(http_content
, of_port_map_new_schema
)
2378 my
.logger
.error("http_of_port_mapping: Warning: remove extra items " + str(r
), exc_info
=True)
2380 # insert in data base
2381 port_mapping
= my
.ovim
.set_of_port_mapping(http_content
['of_port_mapings'])
2382 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2383 delete_nulls(port_mapping
)
2384 data
= {'of_port_mappings': port_mapping
}
2385 return format_out(data
)
2386 except ovim
.ovimException
as e
:
2387 my
.logger
.error(str(e
), exc_info
=True)
2388 bottle
.abort(e
.http_code
, str(e
))
2389 except Exception as e
:
2390 my
.logger
.error(str(e
), exc_info
=True)
2391 bottle
.abort(HTTP_Bad_Request
, str(e
))
2394 @bottle.route(url_base
+ '/openflow/mapping', method
='GET')
2395 def get_of_port_mapping():
2397 Get compute port mapping
2400 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2403 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_id
,
2404 ('id', 'ofc_id', 'region', 'compute_node', 'pci',
2405 'switch_dpid', 'switch_port', 'switch_mac'))
2406 # insert in data base
2407 port_mapping
= my
.ovim
.get_of_port_mappings(select_
, where_
)
2408 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2409 delete_nulls(port_mapping
)
2410 data
= {'of_port_mappings': port_mapping
}
2411 return format_out(data
)
2412 except ovim
.ovimException
as e
:
2413 my
.logger
.error(str(e
), exc_info
=True)
2414 bottle
.abort(e
.http_code
, str(e
))
2415 except Exception as e
:
2416 my
.logger
.error(str(e
), exc_info
=True)
2417 bottle
.abort(HTTP_Bad_Request
, str(e
))
2420 @bottle.route(url_base
+ '/openflow/mapping/<region>', method
='DELETE')
2421 def delete_of_port_mapping(region
):
2423 Insert a tenant into the database.
2426 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2429 # insert in data base
2430 db_filter
= {'region': region
}
2431 result
= my
.ovim
.clear_of_port_mapping(db_filter
)
2432 data
= {'result': result
}
2433 return format_out(data
)
2434 except ovim
.ovimException
as e
:
2435 my
.logger
.error(str(e
), exc_info
=True)
2436 bottle
.abort(e
.http_code
, str(e
))
2437 except Exception as e
:
2438 my
.logger
.error(str(e
), exc_info
=True)
2439 bottle
.abort(HTTP_Bad_Request
, str(e
))