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', 'size': 'image_size'}
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'}
161 def remove_extra_items(data
, schema
):
165 if type(data
) is tuple or type(data
) is list:
167 a
= remove_extra_items(d
, schema
['items'])
168 if a
is not None: deleted
.append(a
)
169 elif type(data
) is dict:
171 for k
in data
.keys():
172 if 'patternProperties' in schema
and k
not in schema
['properties'].keys():
173 reg_ex_list
= schema
['patternProperties'].keys()
174 for reg_ex
in reg_ex_list
:
175 if not re
.match(reg_ex
, k
):
178 elif 'properties' not in schema
or k
not in schema
['properties'].keys(): # or k not in schema['patternProperties'].keys():
182 a
= remove_extra_items(data
[k
], schema
['properties'][k
])
183 if a
is not None: deleted
.append({k
:a
})
184 if len(deleted
) == 0: return None
185 elif len(deleted
) == 1: return deleted
[0]
189 def delete_nulls(var
):
190 if type(var
) is dict:
192 if var
[k
] is None: del var
[k
]
193 elif type(var
[k
]) is dict or type(var
[k
]) is list or type(var
[k
]) is tuple:
194 if delete_nulls(var
[k
]): del var
[k
]
195 if len(var
) == 0: return True
196 elif type(var
) is list or type(var
) is tuple:
198 if type(k
) is dict: delete_nulls(k
)
199 if len(var
) == 0: return True
203 class httpserver(threading
.Thread
):
204 def __init__(self
, ovim
, name
="http", host
='localhost', port
=8080, admin
=False, config_
=None):
206 Creates a new thread to attend the http connections
208 db_conn: database connection
209 name: name of this thread
210 host: ip or name where to listen
211 port: port where to listen
212 admin: if this has privileges of administrator or not
213 config_: unless the first thread must be provided. It is a global dictionary where to allocate the self variable
219 if config_
is not None:
221 if 'http_threads' not in config_dic
:
222 config_dic
['http_threads'] = {}
223 threading
.Thread
.__init
__(self
)
226 self
.db
= ovim
.db
#TODO OVIM remove
229 if name
in config_dic
:
230 print "httpserver Warning!!! Onether thread with the same name", name
232 while name
+str(n
) in config_dic
:
236 self
.url_preffix
= 'http://' + self
.host
+ ':' + str(self
.port
) + url_base
237 config_dic
['http_threads'][name
] = self
239 #Ensure that when the main program exits the thread will also exit
242 self
.logger
= logging
.getLogger("openvim.http")
245 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=True) #quiet=True
247 def gethost(self
, host_id
):
248 result
, content
= self
.db
.get_host(host_id
)
250 print "httpserver.gethost error %d %s" % (result
, content
)
251 bottle
.abort(-result
, content
)
253 print "httpserver.gethost host '%s' not found" % host_id
254 bottle
.abort(HTTP_Not_Found
, content
)
256 data
={'host' : content
}
257 convert_boolean(content
, ('admin_state_up',) )
258 change_keys_http2db(content
, http2db_host
, reverse
=True)
260 return format_out(data
)
262 @bottle.route(url_base
+ '/', method
='GET')
265 return 'works' #TODO: put links or redirection to /openvim???
271 def change_keys_http2db(data
, http_db
, reverse
=False):
272 '''Change keys of dictionary data according to the key_dict values
273 This allow change from http interface names to database names.
274 When reverse is True, the change is otherwise
276 data: can be a dictionary or a list
277 http_db: is a dictionary with hhtp names as keys and database names as value
278 reverse: by default change is done from http API to database. If True change is done otherwise
279 Return: None, but data is modified'''
280 if type(data
) is tuple or type(data
) is list:
282 change_keys_http2db(d
, http_db
, reverse
)
283 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
285 for k
,v
in http_db
.items():
286 if v
in data
: data
[k
]=data
.pop(v
)
288 for k
,v
in http_db
.items():
289 if k
in data
: data
[v
]=data
.pop(k
)
293 def format_out(data
):
294 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
295 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
296 bottle
.response
.content_type
='application/yaml'
297 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='"'
298 else: #by default json
299 bottle
.response
.content_type
='application/json'
300 #return data #json no style
301 return json
.dumps(data
, indent
=4) + "\n"
303 def format_in(schema
):
305 error_text
= "Invalid header format "
306 format_type
= bottle
.request
.headers
.get('Content-Type', 'application/json')
307 if 'application/json' in format_type
:
308 error_text
= "Invalid json format "
309 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
310 client_data
= json
.load(bottle
.request
.body
)
311 #client_data = bottle.request.json()
312 elif 'application/yaml' in format_type
:
313 error_text
= "Invalid yaml format "
314 client_data
= yaml
.load(bottle
.request
.body
)
315 elif format_type
== 'application/xml':
316 bottle
.abort(501, "Content-Type: application/xml not supported yet.")
318 print "HTTP HEADERS: " + str(bottle
.request
.headers
.items())
319 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
321 #if client_data == None:
322 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
326 #print "HTTP input data: ", str(client_data)
327 error_text
= "Invalid content "
328 js_v(client_data
, schema
)
331 except (ValueError, yaml
.YAMLError
) as exc
:
332 error_text
+= str(exc
)
334 bottle
.abort(HTTP_Bad_Request
, error_text
)
335 except js_e
.ValidationError
as exc
:
336 print "HTTP validate_in error, jsonschema exception ", exc
.message
, "at", exc
.path
337 print " CONTENT: " + str(bottle
.request
.body
.readlines())
339 if len(exc
.path
)>0: error_pos
=" at '" + ":".join(map(str, exc
.path
)) + "'"
340 bottle
.abort(HTTP_Bad_Request
, error_text
+ error_pos
+": "+exc
.message
)
342 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
345 def filter_query_string(qs
, http2db
, allowed
):
346 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
348 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
349 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
350 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
351 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
352 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
353 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
354 limit: limit dictated by user with the query string 'limit'. 100 by default
355 abort if not permitted, using bottel.abort
360 if type(qs
) is not bottle
.FormsDict
:
361 print '!!!!!!!!!!!!!!invalid query string not a dictionary'
362 # bottle.abort(HTTP_Internal_Server_Error, "call programmer")
366 select
+= qs
.getall(k
)
369 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field=" + v
+ "'")
374 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit=" + qs
[k
] + "'")
377 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '" + k
+ "=" + qs
[k
] + "'")
382 if len(select
) == 0: select
+= allowed
383 # change from http api to database naming
384 for i
in range(0, len(select
)):
387 select
[i
] = http2db
[k
]
388 change_keys_http2db(where
, http2db
)
389 # print "filter_query_string", select,where,limit
391 return select
, where
, limit
393 def convert_bandwidth(data
, reverse
=False):
394 '''Check the field bandwidth recursively and when found, it removes units and convert to number
395 It assumes that bandwidth is well formed
397 'data': dictionary bottle.FormsDict variable to be checked. None or empty is considered valid
398 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
402 if type(data
) is dict:
403 for k
in data
.keys():
404 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
405 convert_bandwidth(data
[k
], reverse
)
406 if "bandwidth" in data
:
408 value
=str(data
["bandwidth"])
410 pos
= value
.find("bps")
412 if value
[pos
-1]=="G": data
["bandwidth"] = int(data
["bandwidth"][:pos
-1]) * 1000
413 elif value
[pos
-1]=="k": data
["bandwidth"]= int(data
["bandwidth"][:pos
-1]) / 1000
414 else: data
["bandwidth"]= int(data
["bandwidth"][:pos
-1])
416 value
= int(data
["bandwidth"])
417 if value
% 1000 == 0: data
["bandwidth"]=str(value
/1000) + " Gbps"
418 else: data
["bandwidth"]=str(value
) + " Mbps"
420 print "convert_bandwidth exception for type", type(data
["bandwidth"]), " data", data
["bandwidth"]
422 if type(data
) is tuple or type(data
) is list:
424 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
425 convert_bandwidth(k
, reverse
)
427 def convert_boolean(data
, items
): #TODO OVIM delete
428 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
429 It assumes that bandwidth is well formed
431 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
432 'items': tuple of keys to convert
436 if type(data
) is dict:
437 for k
in data
.keys():
438 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
439 convert_boolean(data
[k
], items
)
441 if type(data
[k
]) is str:
442 if data
[k
]=="false": data
[k
]=False
443 elif data
[k
]=="true": data
[k
]=True
444 if type(data
) is tuple or type(data
) is list:
446 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
447 convert_boolean(k
, items
)
449 def convert_datetime2str(var
):
450 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
451 It enters recursively in the dict var finding this kind of variables
453 if type(var
) is dict:
454 for k
,v
in var
.items():
455 if type(v
) is datetime
.datetime
:
456 var
[k
]= v
.strftime('%Y-%m-%dT%H:%M:%S')
457 elif type(v
) is dict or type(v
) is list or type(v
) is tuple:
458 convert_datetime2str(v
)
459 if len(var
) == 0: return True
460 elif type(var
) is list or type(var
) is tuple:
462 convert_datetime2str(v
)
464 def check_valid_tenant(my
, tenant_id
):
467 return HTTP_Unauthorized
, "Needed admin privileges"
469 result
, _
= my
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
})
471 return HTTP_Not_Found
, "tenant '%s' not found" % tenant_id
476 Check if string value is a well-wormed url
477 :param url: string url
478 :return: True if is a valid url, False if is not well-formed
481 parsed_url
= urlparse
.urlparse(url
)
496 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
499 @bottle.hook('after_request')
501 #TODO: Alf: Is it needed??
502 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
508 @bottle.route(url_base
+ '/hosts', method
='GET')
509 def http_get_hosts():
510 return format_out(get_hosts())
514 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_host
,
515 ('id', 'name', 'description', 'status', 'admin_state_up', 'ip_name'))
517 myself
= config_dic
['http_threads'][ threading
.current_thread().name
]
518 result
, content
= myself
.db
.get_table(FROM
='hosts', SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
520 print "http_get_hosts Error", content
521 bottle
.abort(-result
, content
)
523 convert_boolean(content
, ('admin_state_up',) )
524 change_keys_http2db(content
, http2db_host
, reverse
=True)
526 row
['links'] = ( {'href': myself
.url_preffix
+ '/hosts/' + str(row
['id']), 'rel': 'bookmark'}, )
527 data
={'hosts' : content
}
530 @bottle.route(url_base
+ '/hosts/<host_id>', method
='GET')
531 def http_get_host_id(host_id
):
532 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
533 return my
.gethost(host_id
)
535 @bottle.route(url_base
+ '/hosts', method
='POST')
536 def http_post_hosts():
537 '''insert a host into the database. All resources are got and inserted'''
538 global RADclass_module
539 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
542 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
545 http_content
= format_in( host_new_schema
)
546 r
= remove_extra_items(http_content
, host_new_schema
)
547 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
548 change_keys_http2db(http_content
['host'], http2db_host
)
550 if 'host' in http_content
:
551 host
= http_content
['host']
552 if 'host-data' in http_content
:
553 host
.update(http_content
['host-data'])
555 host
= http_content
['host-data']
557 ip_name
= host
['ip_name']
559 password
= host
.get('password')
560 if host
.get('autodiscover'):
561 if not RADclass_module
:
563 RADclass_module
= imp
.find_module("RADclass")
564 except (IOError, ImportError) as e
:
565 raise ImportError("Cannot import RADclass.py Openvim not properly installed" +str(e
))
568 rad
= RADclass_module
.RADclass()
569 (return_status
, code
) = rad
.obtain_RAD(user
, password
, ip_name
)
572 if not return_status
:
573 print 'http_post_hosts ERROR obtaining RAD', code
574 bottle
.abort(HTTP_Bad_Request
, code
)
577 rad_structure
= yaml
.load(rad
.to_text())
578 print 'rad_structure\n---------------------'
579 print json
.dumps(rad_structure
, indent
=4)
580 print '---------------------'
582 WHERE_
={"family":rad_structure
['processor']['family'], 'manufacturer':rad_structure
['processor']['manufacturer'], 'version':rad_structure
['processor']['version']}
583 result
, content
= my
.db
.get_table(FROM
='host_ranking',
587 host
['ranking'] = content
[0]['ranking']
589 #error_text= "Host " + str(WHERE_)+ " not found in ranking table. Not valid for VIM management"
590 #bottle.abort(HTTP_Bad_Request, error_text)
592 warning_text
+= "Host " + str(WHERE_
)+ " not found in ranking table. Assuming lowest value 100\n"
593 host
['ranking'] = 100 #TODO: as not used in this version, set the lowest value
595 features
= rad_structure
['processor'].get('features', ())
596 host
['features'] = ",".join(features
)
599 for node
in (rad_structure
['resource topology']['nodes'] or {}).itervalues():
604 for core
in node
['cpu']['eligible_cores']:
605 eligible_cores
.extend(core
)
606 for core
in node
['cpu']['cores']:
607 for thread_id
in core
:
608 c
={'core_id': count
, 'thread_id': thread_id
}
609 if thread_id
not in eligible_cores
: c
['status'] = 'noteligible'
614 for port_k
, port_v
in node
['nics']['nic 0']['ports'].iteritems():
615 if port_v
['virtual']:
619 for port_k2
, port_v2
in node
['nics']['nic 0']['ports'].iteritems():
620 if port_v2
['virtual'] and port_v2
['PF_pci_id']==port_k
:
621 sriovs
.append({'pci':port_k2
, 'mac':port_v2
['mac'], 'source_name':port_v2
['source_name']})
623 #sort sriov according to pci and rename them to the vf number
624 new_sriovs
= sorted(sriovs
, key
=lambda k
: k
['pci'])
626 for sriov
in new_sriovs
:
627 sriov
['source_name'] = index
629 interfaces
.append ({'pci':str(port_k
), 'Mbps': port_v
['speed']/1000000, 'sriovs': new_sriovs
, 'mac':port_v
['mac'], 'source_name':port_v
['source_name']})
630 memory
=node
['memory']['node_size'] / (1024*1024*1024)
631 #memory=get_next_2pow(node['memory']['hugepage_nr'])
632 host
['numas'].append( {'numa_socket': node
['id'], 'hugepages': node
['memory']['hugepage_nr'], 'memory':memory
, 'interfaces': interfaces
, 'cores': cores
} )
633 # print json.dumps(host, indent=4)
634 # insert in data base
635 if "created_at" in host
:
636 del host
["created_at"]
637 for numa
in host
.get("numas", ()):
638 if "hugepages_consumed" in numa
:
639 del numa
["hugepages_consumed"]
640 result
, content
= my
.db
.new_host(host
)
642 if content
['admin_state_up']:
644 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
645 host_develop_mode
= True if config_dic
['mode']=='development' else False
646 host_develop_bridge_iface
= config_dic
.get('development_bridge', None)
647 thread
= ht
.host_thread(name
=host
.get('name',ip_name
), user
=user
, host
=ip_name
,
648 password
=host
.get('password'),
649 keyfile
=host
.get('keyfile', config_dic
["host_ssh_keyfile"]),
650 db
=config_dic
['db'], db_lock
=config_dic
['db_lock'],
651 test
=host_test_mode
, image_path
=config_dic
['host_image_path'],
652 version
=config_dic
['version'], host_id
=content
['uuid'],
653 develop_mode
=host_develop_mode
, develop_bridge_iface
=host_develop_bridge_iface
)
656 config_dic
['host_threads'][content
['uuid']] = thread
658 if config_dic
['network_type'] == 'ovs':
660 create_dhcp_ovs_bridge()
661 config_dic
['host_threads'][content
['uuid']].insert_task("new-ovsbridge")
662 # create vlxan bwt OVS controller and computes
663 create_vxlan_mesh(content
['uuid'])
666 change_keys_http2db(content
, http2db_host
, reverse
=True)
667 if len(warning_text
)>0:
668 content
["warning"]= warning_text
669 data
={'host' : content
}
670 return format_out(data
)
672 bottle
.abort(HTTP_Bad_Request
, content
)
676 def delete_dhcp_ovs_bridge(vlan
, net_uuid
):
678 Delete bridges and port created during dhcp launching at openvim controller
679 :param vlan: net vlan id
680 :param net_uuid: network identifier
683 dhcp_path
= config_dic
['ovs_controller_file_path']
685 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
686 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
688 dhcp_controller
.delete_dhcp_server(vlan
, net_uuid
, dhcp_path
)
689 dhcp_controller
.delete_dhcp_port(vlan
, net_uuid
)
692 def create_dhcp_ovs_bridge():
694 Initialize bridge to allocate the dhcp server at openvim controller
697 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
698 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
700 dhcp_controller
.create_ovs_bridge()
703 def set_mac_dhcp(vm_ip
, vlan
, first_ip
, last_ip
, cidr
, mac
):
705 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
706 :param vm_ip: IP address asigned to a VM
707 :param vlan: Segmentation id
708 :param first_ip: First dhcp range ip
709 :param last_ip: Last dhcp range ip
710 :param cidr: net cidr
711 :param mac: VM vnic mac to be macthed with the IP received
715 ip_tools
= IPNetwork(cidr
)
716 cidr_len
= ip_tools
.prefixlen
717 dhcp_netmask
= str(ip_tools
.netmask
)
718 dhcp_path
= config_dic
['ovs_controller_file_path']
720 new_cidr
= [first_ip
+ '/' + str(cidr_len
)]
721 if not len(all_matching_cidrs(vm_ip
, new_cidr
)):
724 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
725 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
727 dhcp_controller
.set_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_netmask
, first_ip
, dhcp_path
)
730 def delete_mac_dhcp(vm_ip
, vlan
, mac
):
732 Delete into dhcp conf file the ip assigned to a specific MAC address
733 :param vm_ip: IP address asigned to a VM
734 :param vlan: Segmentation id
735 :param mac: VM vnic mac to be macthed with the IP received
739 dhcp_path
= config_dic
['ovs_controller_file_path']
741 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
742 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
744 dhcp_controller
.delete_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_path
)
747 def create_vxlan_mesh(host_id
):
749 Create vxlan mesh across all openvimc controller and computes.
750 :param host_id: host identifier
751 :param host_id: host identifier
754 dhcp_compute_name
= get_vxlan_interface("dhcp")
755 existing_hosts
= get_hosts()
756 if len(existing_hosts
['hosts']) > 0:
757 # vlxan mesh creation between openvim controller and computes
758 computes_available
= existing_hosts
['hosts']
760 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
761 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
763 for compute
in computes_available
:
764 vxlan_interface_name
= get_vxlan_interface(compute
['id'][:8])
765 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan", dhcp_compute_name
, dhcp_controller
.host
)
766 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, compute
['ip_name'])
768 # vlxan mesh creation between openvim computes
769 for count
, compute_owner
in enumerate(computes_available
):
770 for compute
in computes_available
:
771 if compute_owner
['id'] == compute
['id']:
774 vxlan_interface_name
= get_vxlan_interface(compute_owner
['id'][:8])
775 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, compute_owner
['ip_name'])
776 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan",
777 vxlan_interface_name
,
778 compute_owner
['ip_name'])
781 def delete_vxlan_mesh(host_id
):
783 Create a task for remove a specific compute of the vlxan mesh
784 :param host_id: host id to be deleted.
786 existing_hosts
= get_hosts()
787 computes_available
= existing_hosts
['hosts']
789 vxlan_interface_name
= get_vxlan_interface(host_id
[:8])
791 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
792 dhcp_host
= http_controller
.ovim
.get_dhcp_controller()
794 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
795 # remove bridge from openvim controller if no more computes exist
796 if len(existing_hosts
):
797 dhcp_host
.delete_ovs_bridge()
799 for compute
in computes_available
:
800 if host_id
== compute
['id']:
803 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
804 config_dic
['host_threads'][compute
['id']].insert_task("del-vxlan", vxlan_interface_name
)
807 def get_vxlan_interface(local_uuid
):
809 Genearte a vxlan interface name
810 :param local_uuid: host id
811 :return: vlxan-8digits
813 return 'vxlan-' + local_uuid
[:8]
816 @bottle.route(url_base
+ '/hosts/<host_id>', method
='PUT')
817 def http_put_host_id(host_id
):
818 '''modify a host into the database. All resources are got and inserted'''
819 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
822 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
825 http_content
= format_in( host_edit_schema
)
826 r
= remove_extra_items(http_content
, host_edit_schema
)
827 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
828 change_keys_http2db(http_content
['host'], http2db_host
)
831 result
, content
= my
.db
.edit_host(host_id
, http_content
['host'])
833 convert_boolean(content
, ('admin_state_up',) )
834 change_keys_http2db(content
, http2db_host
, reverse
=True)
835 data
={'host' : content
}
837 if config_dic
['network_type'] == 'ovs':
838 delete_vxlan_mesh(host_id
)
839 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
842 config_dic
['host_threads'][host_id
].name
= content
.get('name',content
['ip_name'])
843 config_dic
['host_threads'][host_id
].user
= content
['user']
844 config_dic
['host_threads'][host_id
].host
= content
['ip_name']
845 config_dic
['host_threads'][host_id
].insert_task("reload")
847 if config_dic
['network_type'] == 'ovs':
848 # create mesh with new host data
849 config_dic
['host_threads'][host_id
].insert_task("new-ovsbridge")
850 create_vxlan_mesh(host_id
)
853 return format_out(data
)
855 bottle
.abort(HTTP_Bad_Request
, content
)
860 @bottle.route(url_base
+ '/hosts/<host_id>', method
='DELETE')
861 def http_delete_host_id(host_id
):
862 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
865 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
866 result
, content
= my
.db
.delete_row('hosts', host_id
)
868 bottle
.abort(HTTP_Not_Found
, content
)
870 if config_dic
['network_type'] == 'ovs':
871 delete_vxlan_mesh(host_id
)
873 if host_id
in config_dic
['host_threads']:
874 if config_dic
['network_type'] == 'ovs':
875 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
876 config_dic
['host_threads'][host_id
].insert_task("exit")
878 data
={'result' : content
}
879 return format_out(data
)
881 print "http_delete_host_id error",result
, content
882 bottle
.abort(-result
, content
)
889 @bottle.route(url_base
+ '/tenants', method
='GET')
890 def http_get_tenants():
892 Retreive tenant list from DB
895 my
= config_dic
['http_threads'][threading
.current_thread().name
]
898 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_tenant
,
899 ('id', 'name', 'description', 'enabled'))
900 tenants
= my
.ovim
.get_tenants(select_
, where_
)
901 delete_nulls(tenants
)
902 change_keys_http2db(tenants
, http2db_tenant
, reverse
=True)
903 data
= {'tenants': tenants
}
904 return format_out(data
)
905 except ovim
.ovimException
as e
:
906 my
.logger
.error(str(e
), exc_info
=True)
907 bottle
.abort(e
.http_code
, str(e
))
908 except Exception as e
:
909 my
.logger
.error(str(e
), exc_info
=True)
910 bottle
.abort(HTTP_Bad_Request
, str(e
))
913 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
914 def http_get_tenant_id(tenant_id
):
916 Get tenant from DB by id
917 :param tenant_id: tenant id
920 my
= config_dic
['http_threads'][threading
.current_thread().name
]
923 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
925 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
926 data
= {'tenant': tenant
}
927 return format_out(data
)
928 except ovim
.ovimException
as e
:
929 my
.logger
.error(str(e
), exc_info
=True)
930 bottle
.abort(e
.http_code
, str(e
))
931 except Exception as e
:
932 my
.logger
.error(str(e
), exc_info
=True)
933 bottle
.abort(HTTP_Bad_Request
, str(e
))
936 @bottle.route(url_base
+ '/tenants', method
='POST')
937 def http_post_tenants():
939 Insert a tenant into the database.
942 my
= config_dic
['http_threads'][threading
.current_thread().name
]
945 http_content
= format_in(tenant_new_schema
)
946 r
= remove_extra_items(http_content
, tenant_new_schema
)
948 my
.logger
.error("http_post_tenants: Warning: remove extra items " + str(r
), exc_info
=True)
949 # insert in data base
950 tenant_id
= my
.ovim
.new_tentant(http_content
['tenant'])
951 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
952 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
954 data
= {'tenant': tenant
}
955 return format_out(data
)
956 except ovim
.ovimException
as e
:
957 my
.logger
.error(str(e
), exc_info
=True)
958 bottle
.abort(e
.http_code
, str(e
))
959 except Exception as e
:
960 my
.logger
.error(str(e
), exc_info
=True)
961 bottle
.abort(HTTP_Bad_Request
, str(e
))
964 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
965 def http_put_tenant_id(tenant_id
):
967 Update a tenantinto DB.
968 :param tenant_id: tentant id
972 my
= config_dic
['http_threads'][threading
.current_thread().name
]
975 http_content
= format_in(tenant_edit_schema
)
976 r
= remove_extra_items(http_content
, tenant_edit_schema
)
978 print "http_put_tenant_id: Warning: remove extra items ", r
979 change_keys_http2db(http_content
['tenant'], http2db_tenant
)
980 # insert in data base
981 my
.ovim
.edit_tenant(tenant_id
, http_content
['tenant'])
982 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
983 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
984 data
= {'tenant': tenant
}
985 return format_out(data
)
986 except ovim
.ovimException
as e
:
987 my
.logger
.error(str(e
), exc_info
=True)
988 bottle
.abort(e
.http_code
, str(e
))
989 except Exception as e
:
990 my
.logger
.error(str(e
), exc_info
=True)
991 bottle
.abort(HTTP_Bad_Request
, str(e
))
994 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
995 def http_delete_tenant_id(tenant_id
):
997 Delete a tenant from the database.
998 :param tenant_id: tenant id
1001 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1004 content
= my
.ovim
.delete_tentant(tenant_id
)
1005 data
= {'result': content
}
1006 return format_out(data
)
1007 except ovim
.ovimException
as e
:
1008 my
.logger
.error(str(e
), exc_info
=True)
1009 bottle
.abort(e
.http_code
, str(e
))
1010 except Exception as e
:
1011 my
.logger
.error(str(e
), exc_info
=True)
1012 bottle
.abort(HTTP_Bad_Request
, str(e
))
1018 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='GET')
1019 def http_get_flavors(tenant_id
):
1020 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1021 #check valid tenant_id
1022 result
,content
= check_valid_tenant(my
, tenant_id
)
1024 bottle
.abort(result
, content
)
1026 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1027 ('id','name','description','public') )
1028 if tenant_id
=='any':
1031 from_
='tenants_flavors inner join flavors on tenants_flavors.flavor_id=flavors.uuid'
1032 where_
['tenant_id'] = tenant_id
1033 result
, content
= my
.db
.get_table(FROM
=from_
, SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1035 print "http_get_flavors Error", content
1036 bottle
.abort(-result
, content
)
1038 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1040 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(row
['id']) ) ), 'rel':'bookmark' } ]
1041 data
={'flavors' : content
}
1042 return format_out(data
)
1044 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='GET')
1045 def http_get_flavor_id(tenant_id
, flavor_id
):
1046 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1047 #check valid tenant_id
1048 result
,content
= check_valid_tenant(my
, tenant_id
)
1050 bottle
.abort(result
, content
)
1052 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1053 ('id','name','description','ram', 'vcpus', 'extended', 'disk', 'public') )
1054 if tenant_id
=='any':
1057 from_
='tenants_flavors as tf inner join flavors as f on tf.flavor_id=f.uuid'
1058 where_
['tenant_id'] = tenant_id
1059 where_
['uuid'] = flavor_id
1060 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
=from_
, WHERE
=where_
, LIMIT
=limit_
)
1063 print "http_get_flavor_id error %d %s" % (result
, content
)
1064 bottle
.abort(-result
, content
)
1066 print "http_get_flavors_id flavor '%s' not found" % str(flavor_id
)
1067 bottle
.abort(HTTP_Not_Found
, 'flavor %s not found' % flavor_id
)
1069 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1070 if 'extended' in content
[0] and content
[0]['extended'] is not None:
1071 extended
= json
.loads(content
[0]['extended'])
1072 if 'devices' in extended
:
1073 change_keys_http2db(extended
['devices'], http2db_flavor
, reverse
=True)
1074 content
[0]['extended']=extended
1075 convert_bandwidth(content
[0], reverse
=True)
1076 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1077 data
={'flavor' : content
[0]}
1078 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1079 return format_out(data
)
1082 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='POST')
1083 def http_post_flavors(tenant_id
):
1084 '''insert a flavor into the database, and attach to tenant.'''
1085 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1086 #check valid tenant_id
1087 result
,content
= check_valid_tenant(my
, tenant_id
)
1089 bottle
.abort(result
, content
)
1090 http_content
= format_in( flavor_new_schema
)
1091 r
= remove_extra_items(http_content
, flavor_new_schema
)
1092 if r
is not None: print "http_post_flavors: Warning: remove extra items ", r
1093 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1094 extended_dict
= http_content
['flavor'].pop('extended', None)
1095 if extended_dict
is not None:
1096 result
, content
= check_extended(extended_dict
)
1098 print "http_post_flavors wrong input extended error %d %s" % (result
, content
)
1099 bottle
.abort(-result
, content
)
1101 convert_bandwidth(extended_dict
)
1102 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1103 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1104 #insert in data base
1105 result
, content
= my
.db
.new_flavor(http_content
['flavor'], tenant_id
)
1107 return http_get_flavor_id(tenant_id
, content
)
1109 print "http_psot_flavors error %d %s" % (result
, content
)
1110 bottle
.abort(-result
, content
)
1113 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='DELETE')
1114 def http_delete_flavor_id(tenant_id
, flavor_id
):
1115 '''Deletes the flavor_id of a tenant. IT removes from tenants_flavors table.'''
1116 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1117 #check valid tenant_id
1118 result
,content
= check_valid_tenant(my
, tenant_id
)
1120 bottle
.abort(result
, content
)
1122 result
, content
= my
.db
.delete_image_flavor('flavor', flavor_id
, tenant_id
)
1124 bottle
.abort(HTTP_Not_Found
, content
)
1126 data
={'result' : content
}
1127 return format_out(data
)
1129 print "http_delete_flavor_id error",result
, content
1130 bottle
.abort(-result
, content
)
1133 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>/<action>', method
='POST')
1134 def http_attach_detach_flavors(tenant_id
, flavor_id
, action
):
1135 '''attach/detach an existing flavor in this tenant. That is insert/remove at tenants_flavors table.'''
1136 #TODO alf: not tested at all!!!
1137 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1138 #check valid tenant_id
1139 result
,content
= check_valid_tenant(my
, tenant_id
)
1141 bottle
.abort(result
, content
)
1142 if tenant_id
=='any':
1143 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1145 if action
!='attach' and action
!= 'detach':
1146 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1149 #Ensure that flavor exist
1150 from_
='tenants_flavors as tf right join flavors as f on tf.flavor_id=f.uuid'
1151 where_
={'uuid': flavor_id
}
1152 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1154 if action
=='attach':
1155 text_error
="Flavor '%s' not found" % flavor_id
1157 text_error
="Flavor '%s' not found for tenant '%s'" % (flavor_id
, tenant_id
)
1158 bottle
.abort(HTTP_Not_Found
, text_error
)
1162 if action
=='attach':
1163 if flavor
['tenant_id']!=None:
1164 bottle
.abort(HTTP_Conflict
, "Flavor '%s' already attached to tenant '%s'" % (flavor_id
, tenant_id
))
1165 if flavor
['public']=='no' and not my
.admin
:
1166 #allow only attaching public flavors
1167 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private flavor")
1169 #insert in data base
1170 result
, content
= my
.db
.new_row('tenants_flavors', {'flavor_id':flavor_id
, 'tenant_id': tenant_id
})
1172 return http_get_flavor_id(tenant_id
, flavor_id
)
1174 if flavor
['tenant_id']==None:
1175 bottle
.abort(HTTP_Not_Found
, "Flavor '%s' not attached to tenant '%s'" % (flavor_id
, tenant_id
))
1176 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_flavors', WHERE
={'flavor_id':flavor_id
, 'tenant_id':tenant_id
})
1178 if flavor
['public']=='no':
1179 #try to delete the flavor completely to avoid orphan flavors, IGNORE error
1180 my
.db
.delete_row_by_dict(FROM
='flavors', WHERE
={'uuid':flavor_id
})
1181 data
={'result' : "flavor detached"}
1182 return format_out(data
)
1184 #if get here is because an error
1185 print "http_attach_detach_flavors error %d %s" % (result
, content
)
1186 bottle
.abort(-result
, content
)
1189 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='PUT')
1190 def http_put_flavor_id(tenant_id
, flavor_id
):
1191 '''update a flavor_id into the database.'''
1192 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1193 #check valid tenant_id
1194 result
,content
= check_valid_tenant(my
, tenant_id
)
1196 bottle
.abort(result
, content
)
1198 http_content
= format_in( flavor_update_schema
)
1199 r
= remove_extra_items(http_content
, flavor_update_schema
)
1200 if r
is not None: print "http_put_flavor_id: Warning: remove extra items ", r
1201 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1202 extended_dict
= http_content
['flavor'].pop('extended', None)
1203 if extended_dict
is not None:
1204 result
, content
= check_extended(extended_dict
)
1206 print "http_put_flavor_id wrong input extended error %d %s" % (result
, content
)
1207 bottle
.abort(-result
, content
)
1209 convert_bandwidth(extended_dict
)
1210 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1211 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1212 #Ensure that flavor exist
1213 where_
={'uuid': flavor_id
}
1214 if tenant_id
=='any':
1217 from_
='tenants_flavors as ti inner join flavors as i on ti.flavor_id=i.uuid'
1218 where_
['tenant_id'] = tenant_id
1219 result
, content
= my
.db
.get_table(SELECT
=('public',), FROM
=from_
, WHERE
=where_
)
1221 text_error
="Flavor '%s' not found" % flavor_id
1222 if tenant_id
!='any':
1223 text_error
+=" for tenant '%s'" % flavor_id
1224 bottle
.abort(HTTP_Not_Found
, text_error
)
1227 if content
[0]['public']=='yes' and not my
.admin
:
1228 #allow only modifications over private flavors
1229 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public flavor")
1231 #insert in data base
1232 result
, content
= my
.db
.update_rows('flavors', http_content
['flavor'], {'uuid': flavor_id
})
1235 print "http_put_flavor_id error %d %s" % (result
, content
)
1236 bottle
.abort(-result
, content
)
1239 return http_get_flavor_id(tenant_id
, flavor_id
)
1247 @bottle.route(url_base
+ '/<tenant_id>/images', method
='GET')
1248 def http_get_images(tenant_id
):
1249 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1250 #check valid tenant_id
1251 result
,content
= check_valid_tenant(my
, tenant_id
)
1253 bottle
.abort(result
, content
)
1255 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1256 ('id','name','checksum','description','path','public') )
1257 if tenant_id
=='any':
1261 from_
='tenants_images right join images on tenants_images.image_id=images.uuid'
1262 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1263 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1265 print "http_get_images Error", content
1266 bottle
.abort(-result
, content
)
1268 change_keys_http2db(content
, http2db_image
, reverse
=True)
1269 #for row in content: row['links']=[ {'href': "/".join( (my.url_preffix, tenant_id, 'images', str(row['id']) ) ), 'rel':'bookmark' } ]
1270 data
={'images' : content
}
1271 return format_out(data
)
1273 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='GET')
1274 def http_get_image_id(tenant_id
, image_id
):
1275 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1276 #check valid tenant_id
1277 result
,content
= check_valid_tenant(my
, tenant_id
)
1279 bottle
.abort(result
, content
)
1281 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1282 ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
1283 if tenant_id
=='any':
1287 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1288 where_or_
= {'tenant_id': tenant_id
, 'public': "yes"}
1289 where_
['uuid'] = image_id
1290 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1293 print "http_get_images error %d %s" % (result
, content
)
1294 bottle
.abort(-result
, content
)
1296 print "http_get_images image '%s' not found" % str(image_id
)
1297 bottle
.abort(HTTP_Not_Found
, 'image %s not found' % image_id
)
1299 convert_datetime2str(content
)
1300 change_keys_http2db(content
, http2db_image
, reverse
=True)
1301 if 'metadata' in content
[0] and content
[0]['metadata'] is not None:
1302 metadata
= json
.loads(content
[0]['metadata'])
1303 content
[0]['metadata']=metadata
1304 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'images', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1305 data
={'image' : content
[0]}
1306 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1307 return format_out(data
)
1309 @bottle.route(url_base
+ '/<tenant_id>/images', method
='POST')
1310 def http_post_images(tenant_id
):
1311 '''insert a image into the database, and attach to tenant.'''
1312 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1313 #check valid tenant_id
1314 result
,content
= check_valid_tenant(my
, tenant_id
)
1316 bottle
.abort(result
, content
)
1317 http_content
= format_in(image_new_schema
)
1318 r
= remove_extra_items(http_content
, image_new_schema
)
1319 if r
is not None: print "http_post_images: Warning: remove extra items ", r
1320 change_keys_http2db(http_content
['image'], http2db_image
)
1321 metadata_dict
= http_content
['image'].pop('metadata', None)
1322 if metadata_dict
is not None:
1323 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1326 image_file
= http_content
['image'].get('path',None)
1327 parsed_url
= urlparse
.urlparse(image_file
)
1328 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "":
1329 # The path is a local file
1330 if os
.path
.exists(image_file
):
1331 http_content
['image']['checksum'] = md5(image_file
)
1333 # The path is a URL. Code should be added to download the image and calculate the checksum
1334 #http_content['image']['checksum'] = md5(downloaded_image)
1336 # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
1337 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
1339 if 'checksum' not in http_content
['image']:
1340 http_content
['image']['checksum'] = md5_string(image_file
)
1342 # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
1343 # If it is a URL, no error is sent. Checksum will be an empty string
1344 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "" and 'checksum' not in http_content
['image']:
1345 content
= "Image file not found"
1346 print "http_post_images error: %d %s" % (HTTP_Bad_Request
, content
)
1347 bottle
.abort(HTTP_Bad_Request
, content
)
1348 except Exception as e
:
1349 print "ERROR. Unexpected exception: %s" % (str(e
))
1350 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1351 #insert in data base
1352 result
, content
= my
.db
.new_image(http_content
['image'], tenant_id
)
1354 return http_get_image_id(tenant_id
, content
)
1356 print "http_post_images error %d %s" % (result
, content
)
1357 bottle
.abort(-result
, content
)
1360 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='DELETE')
1361 def http_delete_image_id(tenant_id
, image_id
):
1362 '''Deletes the image_id of a tenant. IT removes from tenants_images table.'''
1363 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1364 #check valid tenant_id
1365 result
,content
= check_valid_tenant(my
, tenant_id
)
1367 bottle
.abort(result
, content
)
1368 result
, content
= my
.db
.delete_image_flavor('image', image_id
, tenant_id
)
1370 bottle
.abort(HTTP_Not_Found
, content
)
1372 data
={'result' : content
}
1373 return format_out(data
)
1375 print "http_delete_image_id error",result
, content
1376 bottle
.abort(-result
, content
)
1379 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>/<action>', method
='POST')
1380 def http_attach_detach_images(tenant_id
, image_id
, action
):
1381 '''attach/detach an existing image in this tenant. That is insert/remove at tenants_images table.'''
1382 #TODO alf: not tested at all!!!
1383 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1384 #check valid tenant_id
1385 result
,content
= check_valid_tenant(my
, tenant_id
)
1387 bottle
.abort(result
, content
)
1388 if tenant_id
=='any':
1389 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1391 if action
!='attach' and action
!= 'detach':
1392 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1395 #Ensure that image exist
1396 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1397 where_
={'uuid': image_id
}
1398 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1400 if action
=='attach':
1401 text_error
="Image '%s' not found" % image_id
1403 text_error
="Image '%s' not found for tenant '%s'" % (image_id
, tenant_id
)
1404 bottle
.abort(HTTP_Not_Found
, text_error
)
1408 if action
=='attach':
1409 if image
['tenant_id']!=None:
1410 bottle
.abort(HTTP_Conflict
, "Image '%s' already attached to tenant '%s'" % (image_id
, tenant_id
))
1411 if image
['public']=='no' and not my
.admin
:
1412 #allow only attaching public images
1413 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private image")
1415 #insert in data base
1416 result
, content
= my
.db
.new_row('tenants_images', {'image_id':image_id
, 'tenant_id': tenant_id
})
1418 return http_get_image_id(tenant_id
, image_id
)
1420 if image
['tenant_id']==None:
1421 bottle
.abort(HTTP_Not_Found
, "Image '%s' not attached to tenant '%s'" % (image_id
, tenant_id
))
1422 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_images', WHERE
={'image_id':image_id
, 'tenant_id':tenant_id
})
1424 if image
['public']=='no':
1425 #try to delete the image completely to avoid orphan images, IGNORE error
1426 my
.db
.delete_row_by_dict(FROM
='images', WHERE
={'uuid':image_id
})
1427 data
={'result' : "image detached"}
1428 return format_out(data
)
1430 #if get here is because an error
1431 print "http_attach_detach_images error %d %s" % (result
, content
)
1432 bottle
.abort(-result
, content
)
1435 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='PUT')
1436 def http_put_image_id(tenant_id
, image_id
):
1437 '''update a image_id into the database.'''
1438 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1439 #check valid tenant_id
1440 result
,content
= check_valid_tenant(my
, tenant_id
)
1442 bottle
.abort(result
, content
)
1444 http_content
= format_in( image_update_schema
)
1445 r
= remove_extra_items(http_content
, image_update_schema
)
1446 if r
is not None: print "http_put_image_id: Warning: remove extra items ", r
1447 change_keys_http2db(http_content
['image'], http2db_image
)
1448 metadata_dict
= http_content
['image'].pop('metadata', None)
1449 if metadata_dict
is not None:
1450 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1451 #Ensure that image exist
1452 where_
={'uuid': image_id
}
1453 if tenant_id
=='any':
1457 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1458 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1459 result
, content
= my
.db
.get_table(SELECT
=('public',), DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND")
1461 text_error
="Image '%s' not found" % image_id
1462 if tenant_id
!='any':
1463 text_error
+=" for tenant '%s'" % image_id
1464 bottle
.abort(HTTP_Not_Found
, text_error
)
1467 if content
[0]['public']=='yes' and not my
.admin
:
1468 #allow only modifications over private images
1469 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public image")
1471 #insert in data base
1472 result
, content
= my
.db
.update_rows('images', http_content
['image'], {'uuid': image_id
})
1475 print "http_put_image_id error %d %s" % (result
, content
)
1476 bottle
.abort(-result
, content
)
1479 return http_get_image_id(tenant_id
, image_id
)
1486 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='GET')
1487 def http_get_servers(tenant_id
):
1488 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1489 result
,content
= check_valid_tenant(my
, tenant_id
)
1491 bottle
.abort(result
, content
)
1494 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_server
,
1495 ('id','name','description','hostId','imageRef','flavorRef','status', 'tenant_id') )
1496 if tenant_id
!='any':
1497 where_
['tenant_id'] = tenant_id
1498 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
='instances', WHERE
=where_
, LIMIT
=limit_
)
1500 print "http_get_servers Error", content
1501 bottle
.abort(-result
, content
)
1503 change_keys_http2db(content
, http2db_server
, reverse
=True)
1505 tenant_id
= row
.pop('tenant_id')
1506 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'servers', str(row
['id']) ) ), 'rel':'bookmark' } ]
1507 data
={'servers' : content
}
1508 return format_out(data
)
1510 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='GET')
1511 def http_get_server_id(tenant_id
, server_id
):
1512 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1513 #check valid tenant_id
1514 result
,content
= check_valid_tenant(my
, tenant_id
)
1516 bottle
.abort(result
, content
)
1519 result
, content
= my
.db
.get_instance(server_id
)
1521 bottle
.abort(HTTP_Not_Found
, content
)
1523 #change image/flavor-id to id and link
1524 convert_bandwidth(content
, reverse
=True)
1525 convert_datetime2str(content
)
1526 if content
["ram"]==0 : del content
["ram"]
1527 if content
["vcpus"]==0 : del content
["vcpus"]
1528 if 'flavor_id' in content
:
1529 if content
['flavor_id'] is not None:
1530 content
['flavor'] = {'id':content
['flavor_id'],
1531 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'flavors', str(content
['flavor_id']) ) ), 'rel':'bookmark'}]
1533 del content
['flavor_id']
1534 if 'image_id' in content
:
1535 if content
['image_id'] is not None:
1536 content
['image'] = {'id':content
['image_id'],
1537 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'images', str(content
['image_id']) ) ), 'rel':'bookmark'}]
1539 del content
['image_id']
1540 change_keys_http2db(content
, http2db_server
, reverse
=True)
1541 if 'extended' in content
:
1542 if 'devices' in content
['extended']: change_keys_http2db(content
['extended']['devices'], http2db_server
, reverse
=True)
1544 data
={'server' : content
}
1545 return format_out(data
)
1547 bottle
.abort(-result
, content
)
1550 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='POST')
1551 def http_post_server_id(tenant_id
):
1552 '''deploys a new server'''
1553 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1554 #check valid tenant_id
1555 result
,content
= check_valid_tenant(my
, tenant_id
)
1557 bottle
.abort(result
, content
)
1559 if tenant_id
=='any':
1560 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1562 http_content
= format_in( server_new_schema
)
1563 r
= remove_extra_items(http_content
, server_new_schema
)
1564 if r
is not None: print "http_post_serves: Warning: remove extra items ", r
1565 change_keys_http2db(http_content
['server'], http2db_server
)
1566 extended_dict
= http_content
['server'].get('extended', None)
1567 if extended_dict
is not None:
1568 result
, content
= check_extended(extended_dict
, True)
1570 print "http_post_servers wrong input extended error %d %s" % (result
, content
)
1571 bottle
.abort(-result
, content
)
1573 convert_bandwidth(extended_dict
)
1574 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_server
)
1576 server
= http_content
['server']
1577 server_start
= server
.get('start', 'yes')
1578 server
['tenant_id'] = tenant_id
1579 #check flavor valid and take info
1580 result
, content
= my
.db
.get_table(FROM
='tenants_flavors as tf join flavors as f on tf.flavor_id=f.uuid',
1581 SELECT
=('ram','vcpus','extended'), WHERE
={'uuid':server
['flavor_id'], 'tenant_id':tenant_id
})
1583 bottle
.abort(HTTP_Not_Found
, 'flavor_id %s not found' % server
['flavor_id'])
1585 server
['flavor']=content
[0]
1586 #check image valid and take info
1587 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1588 SELECT
=('path', 'metadata', 'image_id'),
1589 WHERE
={'uuid':server
['image_id'], "status":"ACTIVE"},
1590 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'},
1594 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % server
['image_id'])
1596 for image_dict
in content
:
1597 if image_dict
.get("image_id"):
1600 # insert in data base tenants_images
1601 r2
, c2
= my
.db
.new_row('tenants_images', {'image_id': server
['image_id'], 'tenant_id': tenant_id
})
1603 bottle
.abort(HTTP_Not_Found
, 'image_id %s cannot be used. Error %s' % (server
['image_id'], c2
))
1605 server
['image']={"path": content
[0]["path"], "metadata": content
[0]["metadata"]}
1606 if "hosts_id" in server
:
1607 result
, content
= my
.db
.get_table(FROM
='hosts', SELECT
=('uuid',), WHERE
={'uuid': server
['host_id']})
1609 bottle
.abort(HTTP_Not_Found
, 'hostId %s not found' % server
['host_id'])
1611 #print json.dumps(server, indent=4)
1613 result
, content
= ht
.create_server(server
, config_dic
['db'], config_dic
['db_lock'], config_dic
['mode']=='normal')
1616 #Insert instance to database
1619 print "inserting at DB"
1621 if server_start
== 'no':
1622 content
['status'] = 'INACTIVE'
1624 for net
in http_content
['server']['networks']:
1625 if net
['type'] == 'instance:ovs':
1626 dhcp_nets_id
.append(get_network_id(net
['net_id']))
1629 new_instance_result
, new_instance
= my
.db
.new_instance(content
, nets
, ports_to_free
)
1630 if new_instance_result
< 0:
1631 print "Error http_post_servers() :", new_instance_result
, new_instance
1632 bottle
.abort(-new_instance_result
, new_instance
)
1635 print "inserted at DB"
1639 for port
in ports_to_free
:
1640 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1642 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1646 my
.ovim
.net_update_ofc_thread(net_id
)
1647 except ovim
.ovimException
as e
:
1648 my
.logger
.error("http_post_servers, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1650 # look for dhcp ip address
1651 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "ip_address", "net_id"], WHERE
={"instance_id": new_instance
})
1654 if config_dic
.get("dhcp_server") and iface
["net_id"] in config_dic
["dhcp_nets"]:
1655 #print "dhcp insert add task"
1656 r
,c
= config_dic
['dhcp_thread'].insert_task("add", iface
["mac"])
1658 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1660 #ensure compute contain the bridge for ovs networks:
1661 if iface
.get("net_id"):
1662 server_net
= get_network_id(iface
['net_id'])
1663 if server_net
["network"].get('provider:physical', "")[:3] == 'OVS':
1664 vlan
= str(server_net
['network']['provider:vlan'])
1665 dhcp_enable
= bool(server_net
['network']['enable_dhcp'])
1666 vm_dhcp_ip
= c2
[0]["ip_address"]
1667 config_dic
['host_threads'][server
['host_id']].insert_task("create-ovs-bridge-port", vlan
)
1669 dns
= yaml
.safe_load(server_net
['network'].get("dns"))
1670 routes
= yaml
.safe_load(server_net
['network'].get("routes"))
1671 links
= yaml
.safe_load(server_net
['network'].get("links"))
1673 dhcp_firt_ip
= str(server_net
['network']['dhcp_first_ip'])
1674 dhcp_last_ip
= str(server_net
['network']['dhcp_last_ip'])
1675 dhcp_cidr
= str(server_net
['network']['cidr'])
1676 gateway
= str(server_net
['network']['gateway_ip'])
1678 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
1679 http_controller
.ovim
.launch_dhcp_server(vlan
, dhcp_firt_ip
, dhcp_last_ip
,
1680 dhcp_cidr
, gateway
, dns
, routes
)
1681 set_mac_dhcp(vm_dhcp_ip
, vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, c2
[0]['mac'])
1684 http_controller
.ovim
.launch_link_bridge_to_ovs(vlan
, gateway
, dhcp_cidr
, links
, routes
)
1688 server
['uuid'] = new_instance
1689 server_start
= server
.get('start', 'yes')
1691 if server_start
!= 'no':
1692 server
['paused'] = True if server_start
== 'paused' else False
1693 server
['action'] = {"start":None}
1694 server
['status'] = "CREATING"
1696 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1698 my
.db
.update_rows('instances', {'status':"ERROR"}, {'uuid':server
['uuid'], 'last_error':c
}, log
=True)
1700 return http_get_server_id(tenant_id
, new_instance
)
1702 bottle
.abort(HTTP_Bad_Request
, content
)
1706 def http_server_action(server_id
, tenant_id
, action
):
1707 '''Perform actions over a server as resume, reboot, terminate, ...'''
1708 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1709 server
={"uuid": server_id
, "action":action
}
1710 where
={'uuid': server_id
}
1711 if tenant_id
!='any':
1712 where
['tenant_id']= tenant_id
1713 result
, content
= my
.db
.get_table(FROM
='instances', WHERE
=where
)
1715 bottle
.abort(HTTP_Not_Found
, "server %s not found" % server_id
)
1718 print "http_post_server_action error getting data %d %s" % (result
, content
)
1719 bottle
.abort(HTTP_Internal_Server_Error
, content
)
1721 server
.update(content
[0])
1722 tenant_id
= server
["tenant_id"]
1724 #TODO check a right content
1726 if 'terminate' in action
:
1727 new_status
='DELETING'
1728 elif server
['status'] == 'ERROR': #or server['status'] == 'CREATING':
1729 if 'terminate' not in action
and 'rebuild' not in action
:
1730 bottle
.abort(HTTP_Method_Not_Allowed
, "Server is in ERROR status, must be rebuit or deleted ")
1732 # elif server['status'] == 'INACTIVE':
1733 # if 'start' not in action and 'createImage' not in action:
1734 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'INACTIVE' status is 'start'")
1736 # if 'start' in action:
1737 # new_status='CREATING'
1738 # server['paused']='no'
1739 # elif server['status'] == 'PAUSED':
1740 # if 'resume' not in action:
1741 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'PAUSED' status is 'resume'")
1743 # elif server['status'] == 'ACTIVE':
1744 # if 'pause' not in action and 'reboot'not in action and 'shutoff'not in action:
1745 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'ACTIVE' status is 'pause','reboot' or 'shutoff'")
1748 if 'start' in action
or 'createImage' in action
or 'rebuild' in action
:
1749 #check image valid and take info
1750 image_id
= server
['image_id']
1751 if 'createImage' in action
:
1752 if 'imageRef' in action
['createImage']:
1753 image_id
= action
['createImage']['imageRef']
1754 elif 'disk' in action
['createImage']:
1755 result
, content
= my
.db
.get_table(FROM
='instance_devices',
1756 SELECT
=('image_id','dev'), WHERE
={'instance_id':server
['uuid'],"type":"disk"})
1758 bottle
.abort(HTTP_Not_Found
, 'disk not found for server')
1762 if action
['createImage']['imageRef']['disk'] != None:
1763 for disk
in content
:
1764 if disk
['dev'] == action
['createImage']['imageRef']['disk']:
1765 disk_id
= disk
['image_id']
1768 bottle
.abort(HTTP_Not_Found
, 'disk %s not found for server' % action
['createImage']['imageRef']['disk'])
1771 bottle
.abort(HTTP_Not_Found
, 'more than one disk found for server' )
1775 image_id
= content
[0]['image_id']
1777 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1778 SELECT
=('path','metadata'), WHERE
={'uuid':image_id
, "status":"ACTIVE"},
1779 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'}, WHERE_AND_OR
="AND", DISTINCT
=True)
1781 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % image_id
)
1783 if content
[0]['metadata'] is not None:
1785 metadata
= json
.loads(content
[0]['metadata'])
1787 return -HTTP_Internal_Server_Error
, "Can not decode image metadata"
1788 content
[0]['metadata']=metadata
1790 content
[0]['metadata'] = {}
1791 server
['image']=content
[0]
1792 if 'createImage' in action
:
1793 action
['createImage']['source'] = {'image_id': image_id
, 'path': content
[0]['path']}
1794 if 'createImage' in action
:
1795 #Create an entry in Database for the new image
1796 new_image
={'status':'BUILD', 'progress': 0 }
1797 new_image_metadata
=content
[0]
1798 if 'metadata' in server
['image'] and server
['image']['metadata'] != None:
1799 new_image_metadata
.update(server
['image']['metadata'])
1800 new_image_metadata
= {"use_incremental":"no"}
1801 if 'metadata' in action
['createImage']:
1802 new_image_metadata
.update(action
['createImage']['metadata'])
1803 new_image
['metadata'] = json
.dumps(new_image_metadata
)
1804 new_image
['name'] = action
['createImage'].get('name', None)
1805 new_image
['description'] = action
['createImage'].get('description', None)
1806 new_image
['uuid']=my
.db
.new_uuid()
1807 if 'path' in action
['createImage']:
1808 new_image
['path'] = action
['createImage']['path']
1810 new_image
['path']="/provisional/path/" + new_image
['uuid']
1811 result
, image_uuid
= my
.db
.new_image(new_image
, tenant_id
)
1813 bottle
.abort(HTTP_Bad_Request
, 'Error: ' + image_uuid
)
1815 server
['new_image'] = new_image
1819 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1821 print "Task queue full at host ", server
['host_id']
1822 bottle
.abort(HTTP_Request_Timeout
, c
)
1823 if 'createImage' in action
and result
>= 0:
1824 return http_get_image_id(tenant_id
, image_uuid
)
1826 #Update DB only for CREATING or DELETING status
1827 data
={'result' : 'deleting in process'}
1829 if new_status
!= None and new_status
== 'DELETING':
1834 #look for dhcp ip address
1835 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "net_id"], WHERE
={"instance_id": server_id
})
1836 r
, c
= my
.db
.delete_instance(server_id
, tenant_id
, nets
, ports_to_free
, net_ovs_list
, "requested by http")
1837 for port
in ports_to_free
:
1838 r1
,c1
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1840 my
.logger
.error("http_post_server_action server deletion ERROR at resore-iface!!!! " + c1
)
1841 warn_text
+= "; Error iface '{}' cannot be restored '{}'".format(str(port
), str(e
))
1844 my
.ovim
.net_update_ofc_thread(net_id
)
1845 except ovim
.ovimException
as e
:
1846 my
.logger
.error("http_server_action, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1847 warn_text
+= "; Error openflow rules of network '{}' cannot be restore '{}'".format(net_id
, str (e
))
1849 # look for dhcp ip address
1850 if r2
>0 and config_dic
.get("dhcp_server"):
1852 if iface
["net_id"] in config_dic
["dhcp_nets"]:
1853 r
,c
= config_dic
['dhcp_thread'].insert_task("del", iface
["mac"])
1854 #print "dhcp insert del task"
1856 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1857 # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan, vm_ip, mac)
1859 for net
in net_ovs_list
:
1865 delete_dhcp_ovs_bridge(vlan
, net_id
)
1866 delete_mac_dhcp(vm_ip
, vlan
, mac
)
1868 net_data
= my
.ovim
.show_network(net_id
)
1869 if net_data
.get('links'):
1870 links
= yaml
.load(net_data
.get('links'))
1871 my
.ovim
.delete_link_bridge_to_ovs(vlan
, links
)
1873 config_dic
['host_threads'][server
['host_id']].insert_task('del-ovs-port', vlan
, net_id
)
1875 data
["result"] += warn_text
1876 return format_out(data
)
1880 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='DELETE')
1881 def http_delete_server_id(tenant_id
, server_id
):
1882 '''delete a server'''
1883 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1884 #check valid tenant_id
1885 result
,content
= check_valid_tenant(my
, tenant_id
)
1887 bottle
.abort(result
, content
)
1890 return http_server_action(server_id
, tenant_id
, {"terminate":None} )
1893 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>/action', method
='POST')
1894 def http_post_server_action(tenant_id
, server_id
):
1895 '''take an action over a server'''
1896 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1897 #check valid tenant_id
1898 result
,content
= check_valid_tenant(my
, tenant_id
)
1900 bottle
.abort(result
, content
)
1902 http_content
= format_in( server_action_schema
)
1903 #r = remove_extra_items(http_content, server_action_schema)
1904 #if r is not None: print "http_post_server_action: Warning: remove extra items ", r
1906 return http_server_action(server_id
, tenant_id
, http_content
)
1913 @bottle.route(url_base
+ '/networks', method
='GET')
1914 def http_get_networks():
1916 Get all networks available
1919 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1923 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_network
,
1924 ('id', 'name', 'tenant_id', 'type',
1925 'shared', 'provider:vlan', 'status', 'last_error',
1926 'admin_state_up', 'provider:physical'))
1927 if "tenant_id" in where_
:
1928 del where_
["tenant_id"]
1930 content
= my
.ovim
.get_networks(select_
, where_
, limit_
)
1932 delete_nulls(content
)
1933 change_keys_http2db(content
, http2db_network
, reverse
=True)
1934 data
= {'networks': content
}
1935 return format_out(data
)
1937 except ovim
.ovimException
as e
:
1938 my
.logger
.error(str(e
), exc_info
=True)
1939 bottle
.abort(e
.http_code
, str(e
))
1940 except Exception as e
:
1941 my
.logger
.error(str(e
), exc_info
=True)
1942 bottle
.abort(HTTP_Bad_Request
, str(e
))
1945 @bottle.route(url_base
+ '/networks/<network_id>', method
='GET')
1946 def http_get_network_id(network_id
):
1948 Get a network data by id
1952 data
= get_network_id(network_id
)
1953 return format_out(data
)
1956 def get_network_id(network_id
):
1958 Get network from DB by id
1959 :param network_id: network Id
1962 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1966 where_
= bottle
.request
.query
1967 content
= my
.ovim
.show_network(network_id
, where_
)
1969 change_keys_http2db(content
, http2db_network
, reverse
=True)
1970 delete_nulls(content
)
1971 data
= {'network': content
}
1973 except ovim
.ovimException
as e
:
1974 my
.logger
.error(str(e
), exc_info
=True)
1975 bottle
.abort(e
.http_code
, str(e
))
1976 except Exception as e
:
1977 my
.logger
.error(str(e
), exc_info
=True)
1978 bottle
.abort(HTTP_Bad_Request
, str(e
))
1981 @bottle.route(url_base
+ '/networks', method
='POST')
1982 def http_post_networks():
1984 Insert a network into the database.
1987 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1991 http_content
= format_in(network_new_schema
)
1992 r
= remove_extra_items(http_content
, network_new_schema
)
1994 print "http_post_networks: Warning: remove extra items ", r
1995 change_keys_http2db(http_content
['network'], http2db_network
)
1996 network
= http_content
['network']
1997 content
= my
.ovim
.new_network(network
)
1998 return format_out(get_network_id(content
))
1999 except ovim
.ovimException
as e
:
2000 my
.logger
.error(str(e
), exc_info
=True)
2001 bottle
.abort(e
.http_code
, str(e
))
2002 except Exception as e
:
2003 my
.logger
.error(str(e
), exc_info
=True)
2004 bottle
.abort(HTTP_Bad_Request
, str(e
))
2007 @bottle.route(url_base
+ '/networks/<network_id>', method
='PUT')
2008 def http_put_network_id(network_id
):
2010 Update a network_id into DB.
2011 :param network_id: network id
2014 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2018 http_content
= format_in(network_update_schema
)
2019 change_keys_http2db(http_content
['network'], http2db_network
)
2020 network
= http_content
['network']
2021 return format_out(my
.ovim
.edit_network(network_id
, network
))
2023 except ovim
.ovimException
as e
:
2024 my
.logger
.error(str(e
), exc_info
=True)
2025 bottle
.abort(e
.http_code
, str(e
))
2026 except Exception as e
:
2027 my
.logger
.error(str(e
), exc_info
=True)
2028 bottle
.abort(HTTP_Bad_Request
, str(e
))
2031 @bottle.route(url_base
+ '/networks/<network_id>', method
='DELETE')
2032 def http_delete_network_id(network_id
):
2034 Delete a network_id from the database.
2035 :param network_id: Network id
2038 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2041 # delete from the data base
2042 content
= my
.ovim
.delete_network(network_id
)
2043 data
= {'result': content
}
2044 return format_out(data
)
2046 except ovim
.ovimException
as e
:
2047 my
.logger
.error(str(e
), exc_info
=True)
2048 bottle
.abort(e
.http_code
, str(e
))
2049 except Exception as e
:
2050 my
.logger
.error(str(e
), exc_info
=True)
2051 bottle
.abort(HTTP_Bad_Request
, str(e
))
2058 @bottle.route(url_base
+ '/openflow/controller', method
='GET')
2059 def http_get_openflow_controller():
2061 Retrieve a openflow controllers list from DB.
2064 # TODO check if show a proper list
2065 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2068 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_ofc
,
2069 ('id', 'name', 'dpid', 'ip', 'port', 'type',
2070 'version', 'user', 'password'))
2072 content
= my
.ovim
.get_of_controllers(select_
, where_
)
2073 delete_nulls(content
)
2074 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2075 data
= {'ofcs': content
}
2076 return format_out(data
)
2077 except ovim
.ovimException
as e
:
2078 my
.logger
.error(str(e
), exc_info
=True)
2079 bottle
.abort(e
.http_code
, str(e
))
2080 except Exception as e
:
2081 my
.logger
.error(str(e
), exc_info
=True)
2082 bottle
.abort(HTTP_Bad_Request
, str(e
))
2085 @bottle.route(url_base
+ '/openflow/controller/<uuid>', method
='GET')
2086 def http_get_openflow_controller_id(uuid
):
2088 Get an openflow controller by dpid from DB.get_of_controllers
2090 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2094 content
= my
.ovim
.show_of_controller(uuid
)
2095 delete_nulls(content
)
2096 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2097 data
= {'ofc': content
}
2098 return format_out(data
)
2099 except ovim
.ovimException
as e
:
2100 my
.logger
.error(str(e
), exc_info
=True)
2101 bottle
.abort(e
.http_code
, str(e
))
2102 except Exception as e
:
2103 my
.logger
.error(str(e
), exc_info
=True)
2104 bottle
.abort(HTTP_Bad_Request
, str(e
))
2107 @bottle.route(url_base
+ '/openflow/controller/', method
='POST')
2108 def http_post_openflow_controller():
2110 Create a new openflow controller into DB
2113 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2116 http_content
= format_in(openflow_controller_schema
)
2117 of_c
= http_content
['ofc']
2118 uuid
= my
.ovim
.new_of_controller(of_c
)
2119 content
= my
.ovim
.show_of_controller(uuid
)
2120 delete_nulls(content
)
2121 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2122 data
= {'ofc': content
}
2123 return format_out(data
)
2124 except ovim
.ovimException
as e
:
2125 my
.logger
.error(str(e
), exc_info
=True)
2126 bottle
.abort(e
.http_code
, str(e
))
2127 except Exception as e
:
2128 my
.logger
.error(str(e
), exc_info
=True)
2129 bottle
.abort(HTTP_Bad_Request
, str(e
))
2132 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='PUT')
2133 def http_put_openflow_controller_by_id(of_controller_id
):
2135 Create an openflow controller into DB
2136 :param of_controller_id: openflow controller dpid
2139 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2142 http_content
= format_in(openflow_controller_schema
)
2143 of_c
= http_content
['ofc']
2145 content
= my
.ovim
.edit_of_controller(of_controller_id
, of_c
)
2146 delete_nulls(content
)
2147 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2148 data
= {'ofc': content
}
2149 return format_out(data
)
2150 except ovim
.ovimException
as e
:
2151 my
.logger
.error(str(e
), exc_info
=True)
2152 bottle
.abort(e
.http_code
, str(e
))
2153 except Exception as e
:
2154 my
.logger
.error(str(e
), exc_info
=True)
2155 bottle
.abort(HTTP_Bad_Request
, str(e
))
2158 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='DELETE')
2159 def http_delete_openflow_controller(of_controller_id
):
2161 Delete an openflow controller from DB.
2162 :param of_controller_id: openflow controller dpid
2165 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2168 content
= my
.ovim
.delete_of_controller(of_controller_id
)
2169 data
= {'result': content
}
2170 return format_out(data
)
2171 except ovim
.ovimException
as e
:
2172 my
.logger
.error(str(e
), exc_info
=True)
2173 bottle
.abort(e
.http_code
, str(e
))
2174 except Exception as e
:
2175 my
.logger
.error(str(e
), exc_info
=True)
2176 bottle
.abort(HTTP_Bad_Request
, str(e
))
2179 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='GET')
2180 def http_get_openflow_id(network_id
):
2182 To obtain the list of openflow rules of a network
2183 :param network_id: network id
2186 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2189 if network_id
== 'all':
2192 content
= my
.ovim
.get_openflow_rules(network_id
)
2193 data
= {'openflow-rules': content
}
2194 except ovim
.ovimException
as e
:
2195 my
.logger
.error(str(e
), exc_info
=True)
2196 bottle
.abort(e
.http_code
, str(e
))
2197 except Exception as e
:
2198 my
.logger
.error(str(e
), exc_info
=True)
2199 bottle
.abort(HTTP_Bad_Request
, str(e
))
2201 return format_out(data
)
2204 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='PUT')
2205 def http_put_openflow_id(network_id
):
2207 To make actions over the net. The action is to reinstall the openflow rules
2208 network_id can be 'all'
2209 :param network_id: network id
2212 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2215 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2217 if network_id
== 'all':
2221 result
= my
.ovim
.edit_openflow_rules(network_id
)
2222 except ovim
.ovimException
as e
:
2223 my
.logger
.error(str(e
), exc_info
=True)
2224 bottle
.abort(e
.http_code
, str(e
))
2225 except Exception as e
:
2226 my
.logger
.error(str(e
), exc_info
=True)
2227 bottle
.abort(HTTP_Bad_Request
, str(e
))
2229 data
= {'result': str(result
) + " nets updates"}
2230 return format_out(data
)
2232 @bottle.route(url_base
+ '/networks/clear/openflow/<ofc_id>', method
='DELETE')
2233 @bottle.route(url_base
+ '/networks/clear/openflow', method
='DELETE')
2234 def http_clear_openflow_rules(ofc_id
=None):
2236 To make actions over the net. The action is to delete ALL openflow rules
2239 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2242 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2244 my
.ovim
.delete_openflow_rules(ofc_id
)
2245 except ovim
.ovimException
as e
:
2246 my
.logger
.error(str(e
), exc_info
=True)
2247 bottle
.abort(e
.http_code
, str(e
))
2248 except Exception as e
:
2249 my
.logger
.error(str(e
), exc_info
=True)
2250 bottle
.abort(HTTP_Bad_Request
, str(e
))
2252 data
= {'result': " Clearing openflow rules in process"}
2253 return format_out(data
)
2255 @bottle.route(url_base
+ '/networks/openflow/ports/<ofc_id>', method
='GET')
2256 @bottle.route(url_base
+ '/networks/openflow/ports', method
='GET')
2257 def http_get_openflow_ports(ofc_id
=None):
2259 Obtain switch ports names of openflow controller
2262 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2265 ports
= my
.ovim
.get_openflow_ports(ofc_id
)
2266 data
= {'ports': ports
}
2267 except ovim
.ovimException
as e
:
2268 my
.logger
.error(str(e
), exc_info
=True)
2269 bottle
.abort(e
.http_code
, str(e
))
2270 except Exception as e
:
2271 my
.logger
.error(str(e
), exc_info
=True)
2272 bottle
.abort(HTTP_Bad_Request
, str(e
))
2274 return format_out(data
)
2280 @bottle.route(url_base
+ '/ports', method
='GET')
2281 def http_get_ports():
2283 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2284 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_port
,
2285 ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
2286 'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
2288 ports
= my
.ovim
.get_ports(columns
=select_
, filter=where_
, limit
=limit_
)
2290 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2291 data
={'ports' : ports
}
2292 return format_out(data
)
2293 except ovim
.ovimException
as e
:
2294 my
.logger
.error(str(e
), exc_info
=True)
2295 bottle
.abort(e
.http_code
, str(e
))
2296 except Exception as e
:
2297 my
.logger
.error(str(e
), exc_info
=True)
2298 bottle
.abort(HTTP_Bad_Request
, str(e
))
2300 @bottle.route(url_base
+ '/ports/<port_id>', method
='GET')
2301 def http_get_port_id(port_id
):
2302 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2304 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2306 bottle
.abort(HTTP_Not_Found
, 'port %s not found' % port_id
)
2309 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2310 data
= {'port': ports
[0]}
2311 return format_out(data
)
2312 except ovim
.ovimException
as e
:
2313 my
.logger
.error(str(e
), exc_info
=True)
2314 bottle
.abort(e
.http_code
, str(e
))
2315 except Exception as e
:
2316 my
.logger
.error(str(e
), exc_info
=True)
2317 bottle
.abort(HTTP_Bad_Request
, str(e
))
2319 @bottle.route(url_base
+ '/ports', method
='POST')
2320 def http_post_ports():
2321 '''insert an external port into the database.'''
2322 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2324 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2326 http_content
= format_in( port_new_schema
)
2327 r
= remove_extra_items(http_content
, port_new_schema
)
2328 if r
is not None: print "http_post_ports: Warning: remove extra items ", r
2329 change_keys_http2db(http_content
['port'], http2db_port
)
2330 port
=http_content
['port']
2332 port_id
= my
.ovim
.new_port(port
)
2333 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2335 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' inserted but not found at database".format(port_id
))
2338 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2339 data
= {'port': ports
[0]}
2340 return format_out(data
)
2341 except ovim
.ovimException
as e
:
2342 my
.logger
.error(str(e
), exc_info
=True)
2343 bottle
.abort(e
.http_code
, str(e
))
2344 except Exception as e
:
2345 my
.logger
.error(str(e
), exc_info
=True)
2346 bottle
.abort(HTTP_Bad_Request
, str(e
))
2348 @bottle.route(url_base
+ '/ports/<port_id>', method
='PUT')
2349 def http_put_port_id(port_id
):
2350 '''update a port_id into the database.'''
2351 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2353 http_content
= format_in( port_update_schema
)
2354 change_keys_http2db(http_content
['port'], http2db_port
)
2355 port_dict
=http_content
['port']
2357 for k
in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
2358 if k
in port_dict
and not my
.admin
:
2359 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges for changing " + k
)
2362 port_id
= my
.ovim
.edit_port(port_id
, port_dict
, my
.admin
)
2363 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2365 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' edited but not found at database".format(port_id
))
2368 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2369 data
= {'port': ports
[0]}
2370 return format_out(data
)
2371 except ovim
.ovimException
as e
:
2372 my
.logger
.error(str(e
), exc_info
=True)
2373 bottle
.abort(e
.http_code
, str(e
))
2374 except Exception as e
:
2375 my
.logger
.error(str(e
), exc_info
=True)
2376 bottle
.abort(HTTP_Bad_Request
, str(e
))
2379 @bottle.route(url_base
+ '/ports/<port_id>', method
='DELETE')
2380 def http_delete_port_id(port_id
):
2381 '''delete a port_id from the database.'''
2382 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2384 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2387 result
= my
.ovim
.delete_port(port_id
)
2388 data
= {'result': result
}
2389 return format_out(data
)
2390 except ovim
.ovimException
as e
:
2391 my
.logger
.error(str(e
), exc_info
=True)
2392 bottle
.abort(e
.http_code
, str(e
))
2393 except Exception as e
:
2394 my
.logger
.error(str(e
), exc_info
=True)
2395 bottle
.abort(HTTP_Bad_Request
, str(e
))
2398 @bottle.route(url_base
+ '/openflow/mapping', method
='POST')
2399 def http_of_port_mapping():
2401 Create new compute port mapping entry
2404 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2407 http_content
= format_in(of_port_map_new_schema
)
2408 r
= remove_extra_items(http_content
, of_port_map_new_schema
)
2410 my
.logger
.error("http_of_port_mapping: Warning: remove extra items " + str(r
), exc_info
=True)
2412 # insert in data base
2413 port_mapping
= my
.ovim
.set_of_port_mapping(http_content
['of_port_mapings'])
2414 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2415 delete_nulls(port_mapping
)
2416 data
= {'of_port_mappings': port_mapping
}
2417 return format_out(data
)
2418 except ovim
.ovimException
as e
:
2419 my
.logger
.error(str(e
), exc_info
=True)
2420 bottle
.abort(e
.http_code
, str(e
))
2421 except Exception as e
:
2422 my
.logger
.error(str(e
), exc_info
=True)
2423 bottle
.abort(HTTP_Bad_Request
, str(e
))
2426 @bottle.route(url_base
+ '/openflow/mapping', method
='GET')
2427 def get_of_port_mapping():
2429 Get compute port mapping
2432 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2435 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_id
,
2436 ('id', 'ofc_id', 'region', 'compute_node', 'pci',
2437 'switch_dpid', 'switch_port', 'switch_mac'))
2438 # insert in data base
2439 port_mapping
= my
.ovim
.get_of_port_mappings(select_
, where_
)
2440 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2441 delete_nulls(port_mapping
)
2442 data
= {'of_port_mappings': port_mapping
}
2443 return format_out(data
)
2444 except ovim
.ovimException
as e
:
2445 my
.logger
.error(str(e
), exc_info
=True)
2446 bottle
.abort(e
.http_code
, str(e
))
2447 except Exception as e
:
2448 my
.logger
.error(str(e
), exc_info
=True)
2449 bottle
.abort(HTTP_Bad_Request
, str(e
))
2452 @bottle.route(url_base
+ '/openflow/mapping/<region>', method
='DELETE')
2453 def delete_of_port_mapping(region
):
2455 Insert a tenant into the database.
2458 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2461 # insert in data base
2462 db_filter
= {'region': region
}
2463 result
= my
.ovim
.clear_of_port_mapping(db_filter
)
2464 data
= {'result': result
}
2465 return format_out(data
)
2466 except ovim
.ovimException
as e
:
2467 my
.logger
.error(str(e
), exc_info
=True)
2468 bottle
.abort(e
.http_code
, str(e
))
2469 except Exception as e
:
2470 my
.logger
.error(str(e
), exc_info
=True)
2471 bottle
.abort(HTTP_Bad_Request
, str(e
))