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
=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 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
530 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
533 http_content
= format_in( host_new_schema
)
534 r
= remove_extra_items(http_content
, host_new_schema
)
535 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
536 change_keys_http2db(http_content
['host'], http2db_host
)
538 host
= http_content
['host']
540 if 'host-data' in http_content
:
541 host
.update(http_content
['host-data'])
542 ip_name
=http_content
['host-data']['ip_name']
543 user
=http_content
['host-data']['user']
544 password
=http_content
['host-data'].get('password', None)
546 ip_name
=host
['ip_name']
548 password
=host
.get('password', None)
549 if not RADclass_module
:
551 RADclass_module
= imp
.find_module("RADclass")
552 except (IOError, ImportError) as e
:
553 raise ImportError("Cannot import RADclass.py Openvim not properly installed" +str(e
))
556 rad
= RADclass_module
.RADclass()
557 (return_status
, code
) = rad
.obtain_RAD(user
, password
, ip_name
)
560 if not return_status
:
561 print 'http_post_hosts ERROR obtaining RAD', code
562 bottle
.abort(HTTP_Bad_Request
, code
)
565 rad_structure
= yaml
.load(rad
.to_text())
566 print 'rad_structure\n---------------------'
567 print json
.dumps(rad_structure
, indent
=4)
568 print '---------------------'
570 WHERE_
={"family":rad_structure
['processor']['family'], 'manufacturer':rad_structure
['processor']['manufacturer'], 'version':rad_structure
['processor']['version']}
571 result
, content
= my
.db
.get_table(FROM
='host_ranking',
575 host
['ranking'] = content
[0]['ranking']
577 #error_text= "Host " + str(WHERE_)+ " not found in ranking table. Not valid for VIM management"
578 #bottle.abort(HTTP_Bad_Request, error_text)
580 warning_text
+= "Host " + str(WHERE_
)+ " not found in ranking table. Assuming lowest value 100\n"
581 host
['ranking'] = 100 #TODO: as not used in this version, set the lowest value
583 features
= rad_structure
['processor'].get('features', ())
584 host
['features'] = ",".join(features
)
587 for node
in (rad_structure
['resource topology']['nodes'] or {}).itervalues():
592 for core
in node
['cpu']['eligible_cores']:
593 eligible_cores
.extend(core
)
594 for core
in node
['cpu']['cores']:
595 for thread_id
in core
:
596 c
={'core_id': count
, 'thread_id': thread_id
}
597 if thread_id
not in eligible_cores
: c
['status'] = 'noteligible'
602 for port_k
, port_v
in node
['nics']['nic 0']['ports'].iteritems():
603 if port_v
['virtual']:
607 for port_k2
, port_v2
in node
['nics']['nic 0']['ports'].iteritems():
608 if port_v2
['virtual'] and port_v2
['PF_pci_id']==port_k
:
609 sriovs
.append({'pci':port_k2
, 'mac':port_v2
['mac'], 'source_name':port_v2
['source_name']})
611 #sort sriov according to pci and rename them to the vf number
612 new_sriovs
= sorted(sriovs
, key
=lambda k
: k
['pci'])
614 for sriov
in new_sriovs
:
615 sriov
['source_name'] = index
617 interfaces
.append ({'pci':str(port_k
), 'Mbps': port_v
['speed']/1000000, 'sriovs': new_sriovs
, 'mac':port_v
['mac'], 'source_name':port_v
['source_name']})
618 memory
=node
['memory']['node_size'] / (1024*1024*1024)
619 #memory=get_next_2pow(node['memory']['hugepage_nr'])
620 host
['numas'].append( {'numa_socket': node
['id'], 'hugepages': node
['memory']['hugepage_nr'], 'memory':memory
, 'interfaces': interfaces
, 'cores': cores
} )
621 print json
.dumps(host
, indent
=4)
625 result
, content
= my
.db
.new_host(host
)
627 if content
['admin_state_up']:
629 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
630 host_develop_mode
= True if config_dic
['mode']=='development' else False
631 host_develop_bridge_iface
= config_dic
.get('development_bridge', None)
632 thread
= ht
.host_thread(name
=host
.get('name',ip_name
), user
=user
, host
=ip_name
, db
=config_dic
['db'], db_lock
=config_dic
['db_lock'],
633 test
=host_test_mode
, image_path
=config_dic
['image_path'],
634 version
=config_dic
['version'], host_id
=content
['uuid'],
635 develop_mode
=host_develop_mode
, develop_bridge_iface
=host_develop_bridge_iface
)
637 config_dic
['host_threads'][ content
['uuid'] ] = thread
639 if config_dic
['network_type'] == 'ovs':
641 create_dhcp_ovs_bridge()
642 config_dic
['host_threads'][content
['uuid']].insert_task("new-ovsbridge")
643 # check if more host exist
644 create_vxlan_mesh(content
['uuid'])
647 change_keys_http2db(content
, http2db_host
, reverse
=True)
648 if len(warning_text
)>0:
649 content
["warning"]= warning_text
650 data
={'host' : content
}
651 return format_out(data
)
653 bottle
.abort(HTTP_Bad_Request
, content
)
657 def delete_dhcp_ovs_bridge(vlan
, net_uuid
):
659 Delete bridges and port created during dhcp launching at openvim controller
660 :param vlan: net vlan id
661 :param net_uuid: network identifier
664 dhcp_path
= config_dic
['ovs_controller_file_path']
666 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
667 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
669 dhcp_controller
.delete_dhcp_port(vlan
, net_uuid
)
670 dhcp_controller
.delete_dhcp_server(vlan
, net_uuid
, dhcp_path
)
673 def create_dhcp_ovs_bridge():
675 Initialize bridge to allocate the dhcp server at openvim controller
678 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
679 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
681 dhcp_controller
.create_ovs_bridge()
684 def set_mac_dhcp(vm_ip
, vlan
, first_ip
, last_ip
, cidr
, mac
):
686 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
687 :param vm_ip: IP address asigned to a VM
688 :param vlan: Segmentation id
689 :param first_ip: First dhcp range ip
690 :param last_ip: Last dhcp range ip
691 :param cidr: net cidr
692 :param mac: VM vnic mac to be macthed with the IP received
696 ip_tools
= IPNetwork(cidr
)
697 cidr_len
= ip_tools
.prefixlen
698 dhcp_netmask
= str(ip_tools
.netmask
)
699 dhcp_path
= config_dic
['ovs_controller_file_path']
701 new_cidr
= [first_ip
+ '/' + str(cidr_len
)]
702 if not len(all_matching_cidrs(vm_ip
, new_cidr
)):
705 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
706 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
708 dhcp_controller
.set_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_netmask
, dhcp_path
)
711 def delete_mac_dhcp(vm_ip
, vlan
, mac
):
713 Delete into dhcp conf file the ip assigned to a specific MAC address
714 :param vm_ip: IP address asigned to a VM
715 :param vlan: Segmentation id
716 :param mac: VM vnic mac to be macthed with the IP received
720 dhcp_path
= config_dic
['ovs_controller_file_path']
722 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
723 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
725 dhcp_controller
.delete_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_path
)
728 def create_vxlan_mesh(host_id
):
730 Create vxlan mesh across all openvimc controller and computes.
731 :param host_id: host identifier
732 :param host_id: host identifier
735 dhcp_compute_name
= get_vxlan_interface("dhcp")
736 existing_hosts
= get_hosts()
737 if len(existing_hosts
['hosts']) > 0:
738 # vlxan mesh creation between openvim controller and computes
739 computes_available
= existing_hosts
['hosts']
741 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
742 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
744 for compute
in computes_available
:
745 vxlan_interface_name
= get_vxlan_interface(compute
['id'][:8])
746 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan", dhcp_compute_name
, dhcp_controller
.host
)
747 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, compute
['ip_name'])
749 # vlxan mesh creation between openvim computes
750 for count
, compute_owner
in enumerate(computes_available
):
751 for compute
in computes_available
:
752 if compute_owner
['id'] == compute
['id']:
755 vxlan_interface_name
= get_vxlan_interface(compute_owner
['id'][:8])
756 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, compute_owner
['ip_name'])
757 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan",
758 vxlan_interface_name
,
759 compute_owner
['ip_name'])
762 def delete_vxlan_mesh(host_id
):
764 Create a task for remove a specific compute of the vlxan mesh
765 :param host_id: host id to be deleted.
767 existing_hosts
= get_hosts()
768 computes_available
= existing_hosts
['hosts']
770 vxlan_interface_name
= get_vxlan_interface(host_id
[:8])
772 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
773 dhcp_host
= http_controller
.ovim
.get_dhcp_controller()
775 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
776 # remove bridge from openvim controller if no more computes exist
777 if len(existing_hosts
):
778 dhcp_host
.delete_ovs_bridge()
780 for compute
in computes_available
:
781 if host_id
== compute
['id']:
784 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
785 config_dic
['host_threads'][compute
['id']].insert_task("del-vxlan", vxlan_interface_name
)
788 def get_vxlan_interface(local_uuid
):
790 Genearte a vxlan interface name
791 :param local_uuid: host id
792 :return: vlxan-8digits
794 return 'vxlan-' + local_uuid
[:8]
797 @bottle.route(url_base
+ '/hosts/<host_id>', method
='PUT')
798 def http_put_host_id(host_id
):
799 '''modify a host into the database. All resources are got and inserted'''
800 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
803 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
806 http_content
= format_in( host_edit_schema
)
807 r
= remove_extra_items(http_content
, host_edit_schema
)
808 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
809 change_keys_http2db(http_content
['host'], http2db_host
)
812 result
, content
= my
.db
.edit_host(host_id
, http_content
['host'])
814 convert_boolean(content
, ('admin_state_up',) )
815 change_keys_http2db(content
, http2db_host
, reverse
=True)
816 data
={'host' : content
}
818 if config_dic
['network_type'] == 'ovs':
819 delete_vxlan_mesh(host_id
)
820 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
823 config_dic
['host_threads'][host_id
].name
= content
.get('name',content
['ip_name'])
824 config_dic
['host_threads'][host_id
].user
= content
['user']
825 config_dic
['host_threads'][host_id
].host
= content
['ip_name']
826 config_dic
['host_threads'][host_id
].insert_task("reload")
828 if config_dic
['network_type'] == 'ovs':
829 # create mesh with new host data
830 config_dic
['host_threads'][host_id
].insert_task("new-ovsbridge")
831 create_vxlan_mesh(host_id
)
834 return format_out(data
)
836 bottle
.abort(HTTP_Bad_Request
, content
)
841 @bottle.route(url_base
+ '/hosts/<host_id>', method
='DELETE')
842 def http_delete_host_id(host_id
):
843 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
846 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
847 result
, content
= my
.db
.delete_row('hosts', host_id
)
849 bottle
.abort(HTTP_Not_Found
, content
)
851 if config_dic
['network_type'] == 'ovs':
852 delete_vxlan_mesh(host_id
)
854 if host_id
in config_dic
['host_threads']:
855 if config_dic
['network_type'] == 'ovs':
856 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
857 config_dic
['host_threads'][host_id
].insert_task("exit")
859 data
={'result' : content
}
860 return format_out(data
)
862 print "http_delete_host_id error",result
, content
863 bottle
.abort(-result
, content
)
870 @bottle.route(url_base
+ '/tenants', method
='GET')
871 def http_get_tenants():
873 Retreive tenant list from DB
876 my
= config_dic
['http_threads'][threading
.current_thread().name
]
879 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_tenant
,
880 ('id', 'name', 'description', 'enabled'))
881 tenants
= my
.ovim
.get_tenants(select_
, where_
)
882 delete_nulls(tenants
)
883 change_keys_http2db(tenants
, http2db_tenant
, reverse
=True)
884 data
= {'tenants': tenants
}
885 return format_out(data
)
886 except ovim
.ovimException
as e
:
887 my
.logger
.error(str(e
), exc_info
=True)
888 bottle
.abort(e
.http_code
, str(e
))
889 except Exception as e
:
890 my
.logger
.error(str(e
), exc_info
=True)
891 bottle
.abort(HTTP_Bad_Request
, str(e
))
894 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
895 def http_get_tenant_id(tenant_id
):
897 Get tenant from DB by id
898 :param tenant_id: tenant id
901 my
= config_dic
['http_threads'][threading
.current_thread().name
]
904 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
906 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
907 data
= {'tenant': tenant
}
908 return format_out(data
)
909 except ovim
.ovimException
as e
:
910 my
.logger
.error(str(e
), exc_info
=True)
911 bottle
.abort(e
.http_code
, str(e
))
912 except Exception as e
:
913 my
.logger
.error(str(e
), exc_info
=True)
914 bottle
.abort(HTTP_Bad_Request
, str(e
))
917 @bottle.route(url_base
+ '/tenants', method
='POST')
918 def http_post_tenants():
920 Insert a tenant into the database.
923 my
= config_dic
['http_threads'][threading
.current_thread().name
]
926 http_content
= format_in(tenant_new_schema
)
927 r
= remove_extra_items(http_content
, tenant_new_schema
)
929 my
.logger
.error("http_post_tenants: Warning: remove extra items " + str(r
), exc_info
=True)
930 # insert in data base
931 tenant_id
= my
.ovim
.new_tentant(http_content
['tenant'])
932 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
933 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
935 data
= {'tenant': tenant
}
936 return format_out(data
)
937 except ovim
.ovimException
as e
:
938 my
.logger
.error(str(e
), exc_info
=True)
939 bottle
.abort(e
.http_code
, str(e
))
940 except Exception as e
:
941 my
.logger
.error(str(e
), exc_info
=True)
942 bottle
.abort(HTTP_Bad_Request
, str(e
))
945 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
946 def http_put_tenant_id(tenant_id
):
948 Update a tenantinto DB.
949 :param tenant_id: tentant id
953 my
= config_dic
['http_threads'][threading
.current_thread().name
]
956 http_content
= format_in(tenant_edit_schema
)
957 r
= remove_extra_items(http_content
, tenant_edit_schema
)
959 print "http_put_tenant_id: Warning: remove extra items ", r
960 change_keys_http2db(http_content
['tenant'], http2db_tenant
)
961 # insert in data base
962 my
.ovim
.edit_tenant(tenant_id
, http_content
['tenant'])
963 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
964 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
965 data
= {'tenant': tenant
}
966 return format_out(data
)
967 except ovim
.ovimException
as e
:
968 my
.logger
.error(str(e
), exc_info
=True)
969 bottle
.abort(e
.http_code
, str(e
))
970 except Exception as e
:
971 my
.logger
.error(str(e
), exc_info
=True)
972 bottle
.abort(HTTP_Bad_Request
, str(e
))
975 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
976 def http_delete_tenant_id(tenant_id
):
978 Delete a tenant from the database.
979 :param tenant_id: tenant id
982 my
= config_dic
['http_threads'][threading
.current_thread().name
]
985 content
= my
.ovim
.delete_tentant(tenant_id
)
986 data
= {'result': content
}
987 return format_out(data
)
988 except ovim
.ovimException
as e
:
989 my
.logger
.error(str(e
), exc_info
=True)
990 bottle
.abort(e
.http_code
, str(e
))
991 except Exception as e
:
992 my
.logger
.error(str(e
), exc_info
=True)
993 bottle
.abort(HTTP_Bad_Request
, str(e
))
999 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='GET')
1000 def http_get_flavors(tenant_id
):
1001 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1002 #check valid tenant_id
1003 result
,content
= check_valid_tenant(my
, tenant_id
)
1005 bottle
.abort(result
, content
)
1007 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1008 ('id','name','description','public') )
1009 if tenant_id
=='any':
1012 from_
='tenants_flavors inner join flavors on tenants_flavors.flavor_id=flavors.uuid'
1013 where_
['tenant_id'] = tenant_id
1014 result
, content
= my
.db
.get_table(FROM
=from_
, SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1016 print "http_get_flavors Error", content
1017 bottle
.abort(-result
, content
)
1019 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1021 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(row
['id']) ) ), 'rel':'bookmark' } ]
1022 data
={'flavors' : content
}
1023 return format_out(data
)
1025 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='GET')
1026 def http_get_flavor_id(tenant_id
, flavor_id
):
1027 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1028 #check valid tenant_id
1029 result
,content
= check_valid_tenant(my
, tenant_id
)
1031 bottle
.abort(result
, content
)
1033 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1034 ('id','name','description','ram', 'vcpus', 'extended', 'disk', 'public') )
1035 if tenant_id
=='any':
1038 from_
='tenants_flavors as tf inner join flavors as f on tf.flavor_id=f.uuid'
1039 where_
['tenant_id'] = tenant_id
1040 where_
['uuid'] = flavor_id
1041 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
=from_
, WHERE
=where_
, LIMIT
=limit_
)
1044 print "http_get_flavor_id error %d %s" % (result
, content
)
1045 bottle
.abort(-result
, content
)
1047 print "http_get_flavors_id flavor '%s' not found" % str(flavor_id
)
1048 bottle
.abort(HTTP_Not_Found
, 'flavor %s not found' % flavor_id
)
1050 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1051 if 'extended' in content
[0] and content
[0]['extended'] is not None:
1052 extended
= json
.loads(content
[0]['extended'])
1053 if 'devices' in extended
:
1054 change_keys_http2db(extended
['devices'], http2db_flavor
, reverse
=True)
1055 content
[0]['extended']=extended
1056 convert_bandwidth(content
[0], reverse
=True)
1057 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1058 data
={'flavor' : content
[0]}
1059 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1060 return format_out(data
)
1063 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='POST')
1064 def http_post_flavors(tenant_id
):
1065 '''insert a flavor into the database, and attach to tenant.'''
1066 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1067 #check valid tenant_id
1068 result
,content
= check_valid_tenant(my
, tenant_id
)
1070 bottle
.abort(result
, content
)
1071 http_content
= format_in( flavor_new_schema
)
1072 r
= remove_extra_items(http_content
, flavor_new_schema
)
1073 if r
is not None: print "http_post_flavors: Warning: remove extra items ", r
1074 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1075 extended_dict
= http_content
['flavor'].pop('extended', None)
1076 if extended_dict
is not None:
1077 result
, content
= check_extended(extended_dict
)
1079 print "http_post_flavors wrong input extended error %d %s" % (result
, content
)
1080 bottle
.abort(-result
, content
)
1082 convert_bandwidth(extended_dict
)
1083 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1084 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1085 #insert in data base
1086 result
, content
= my
.db
.new_flavor(http_content
['flavor'], tenant_id
)
1088 return http_get_flavor_id(tenant_id
, content
)
1090 print "http_psot_flavors error %d %s" % (result
, content
)
1091 bottle
.abort(-result
, content
)
1094 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='DELETE')
1095 def http_delete_flavor_id(tenant_id
, flavor_id
):
1096 '''Deletes the flavor_id of a tenant. IT removes from tenants_flavors table.'''
1097 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1098 #check valid tenant_id
1099 result
,content
= check_valid_tenant(my
, tenant_id
)
1101 bottle
.abort(result
, content
)
1103 result
, content
= my
.db
.delete_image_flavor('flavor', flavor_id
, tenant_id
)
1105 bottle
.abort(HTTP_Not_Found
, content
)
1107 data
={'result' : content
}
1108 return format_out(data
)
1110 print "http_delete_flavor_id error",result
, content
1111 bottle
.abort(-result
, content
)
1114 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>/<action>', method
='POST')
1115 def http_attach_detach_flavors(tenant_id
, flavor_id
, action
):
1116 '''attach/detach an existing flavor in this tenant. That is insert/remove at tenants_flavors table.'''
1117 #TODO alf: not tested at all!!!
1118 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1119 #check valid tenant_id
1120 result
,content
= check_valid_tenant(my
, tenant_id
)
1122 bottle
.abort(result
, content
)
1123 if tenant_id
=='any':
1124 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1126 if action
!='attach' and action
!= 'detach':
1127 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1130 #Ensure that flavor exist
1131 from_
='tenants_flavors as tf right join flavors as f on tf.flavor_id=f.uuid'
1132 where_
={'uuid': flavor_id
}
1133 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1135 if action
=='attach':
1136 text_error
="Flavor '%s' not found" % flavor_id
1138 text_error
="Flavor '%s' not found for tenant '%s'" % (flavor_id
, tenant_id
)
1139 bottle
.abort(HTTP_Not_Found
, text_error
)
1143 if action
=='attach':
1144 if flavor
['tenant_id']!=None:
1145 bottle
.abort(HTTP_Conflict
, "Flavor '%s' already attached to tenant '%s'" % (flavor_id
, tenant_id
))
1146 if flavor
['public']=='no' and not my
.admin
:
1147 #allow only attaching public flavors
1148 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private flavor")
1150 #insert in data base
1151 result
, content
= my
.db
.new_row('tenants_flavors', {'flavor_id':flavor_id
, 'tenant_id': tenant_id
})
1153 return http_get_flavor_id(tenant_id
, flavor_id
)
1155 if flavor
['tenant_id']==None:
1156 bottle
.abort(HTTP_Not_Found
, "Flavor '%s' not attached to tenant '%s'" % (flavor_id
, tenant_id
))
1157 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_flavors', WHERE
={'flavor_id':flavor_id
, 'tenant_id':tenant_id
})
1159 if flavor
['public']=='no':
1160 #try to delete the flavor completely to avoid orphan flavors, IGNORE error
1161 my
.db
.delete_row_by_dict(FROM
='flavors', WHERE
={'uuid':flavor_id
})
1162 data
={'result' : "flavor detached"}
1163 return format_out(data
)
1165 #if get here is because an error
1166 print "http_attach_detach_flavors error %d %s" % (result
, content
)
1167 bottle
.abort(-result
, content
)
1170 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='PUT')
1171 def http_put_flavor_id(tenant_id
, flavor_id
):
1172 '''update a flavor_id into the database.'''
1173 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1174 #check valid tenant_id
1175 result
,content
= check_valid_tenant(my
, tenant_id
)
1177 bottle
.abort(result
, content
)
1179 http_content
= format_in( flavor_update_schema
)
1180 r
= remove_extra_items(http_content
, flavor_update_schema
)
1181 if r
is not None: print "http_put_flavor_id: Warning: remove extra items ", r
1182 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1183 extended_dict
= http_content
['flavor'].pop('extended', None)
1184 if extended_dict
is not None:
1185 result
, content
= check_extended(extended_dict
)
1187 print "http_put_flavor_id wrong input extended error %d %s" % (result
, content
)
1188 bottle
.abort(-result
, content
)
1190 convert_bandwidth(extended_dict
)
1191 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1192 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1193 #Ensure that flavor exist
1194 where_
={'uuid': flavor_id
}
1195 if tenant_id
=='any':
1198 from_
='tenants_flavors as ti inner join flavors as i on ti.flavor_id=i.uuid'
1199 where_
['tenant_id'] = tenant_id
1200 result
, content
= my
.db
.get_table(SELECT
=('public',), FROM
=from_
, WHERE
=where_
)
1202 text_error
="Flavor '%s' not found" % flavor_id
1203 if tenant_id
!='any':
1204 text_error
+=" for tenant '%s'" % flavor_id
1205 bottle
.abort(HTTP_Not_Found
, text_error
)
1208 if content
[0]['public']=='yes' and not my
.admin
:
1209 #allow only modifications over private flavors
1210 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public flavor")
1212 #insert in data base
1213 result
, content
= my
.db
.update_rows('flavors', http_content
['flavor'], {'uuid': flavor_id
})
1216 print "http_put_flavor_id error %d %s" % (result
, content
)
1217 bottle
.abort(-result
, content
)
1220 return http_get_flavor_id(tenant_id
, flavor_id
)
1228 @bottle.route(url_base
+ '/<tenant_id>/images', method
='GET')
1229 def http_get_images(tenant_id
):
1230 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1231 #check valid tenant_id
1232 result
,content
= check_valid_tenant(my
, tenant_id
)
1234 bottle
.abort(result
, content
)
1236 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1237 ('id','name','checksum','description','path','public') )
1238 if tenant_id
=='any':
1242 from_
='tenants_images right join images on tenants_images.image_id=images.uuid'
1243 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1244 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1246 print "http_get_images Error", content
1247 bottle
.abort(-result
, content
)
1249 change_keys_http2db(content
, http2db_image
, reverse
=True)
1250 #for row in content: row['links']=[ {'href': "/".join( (my.url_preffix, tenant_id, 'images', str(row['id']) ) ), 'rel':'bookmark' } ]
1251 data
={'images' : content
}
1252 return format_out(data
)
1254 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='GET')
1255 def http_get_image_id(tenant_id
, image_id
):
1256 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1257 #check valid tenant_id
1258 result
,content
= check_valid_tenant(my
, tenant_id
)
1260 bottle
.abort(result
, content
)
1262 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1263 ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
1264 if tenant_id
=='any':
1268 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1269 where_or_
= {'tenant_id': tenant_id
, 'public': "yes"}
1270 where_
['uuid'] = image_id
1271 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1274 print "http_get_images error %d %s" % (result
, content
)
1275 bottle
.abort(-result
, content
)
1277 print "http_get_images image '%s' not found" % str(image_id
)
1278 bottle
.abort(HTTP_Not_Found
, 'image %s not found' % image_id
)
1280 convert_datetime2str(content
)
1281 change_keys_http2db(content
, http2db_image
, reverse
=True)
1282 if 'metadata' in content
[0] and content
[0]['metadata'] is not None:
1283 metadata
= json
.loads(content
[0]['metadata'])
1284 content
[0]['metadata']=metadata
1285 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'images', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1286 data
={'image' : content
[0]}
1287 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1288 return format_out(data
)
1290 @bottle.route(url_base
+ '/<tenant_id>/images', method
='POST')
1291 def http_post_images(tenant_id
):
1292 '''insert a image into the database, and attach to tenant.'''
1293 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1294 #check valid tenant_id
1295 result
,content
= check_valid_tenant(my
, tenant_id
)
1297 bottle
.abort(result
, content
)
1298 http_content
= format_in(image_new_schema
)
1299 r
= remove_extra_items(http_content
, image_new_schema
)
1300 if r
is not None: print "http_post_images: Warning: remove extra items ", r
1301 change_keys_http2db(http_content
['image'], http2db_image
)
1302 metadata_dict
= http_content
['image'].pop('metadata', None)
1303 if metadata_dict
is not None:
1304 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1307 image_file
= http_content
['image'].get('path',None)
1308 parsed_url
= urlparse
.urlparse(image_file
)
1309 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "":
1310 # The path is a local file
1311 if os
.path
.exists(image_file
):
1312 http_content
['image']['checksum'] = md5(image_file
)
1314 # The path is a URL. Code should be added to download the image and calculate the checksum
1315 #http_content['image']['checksum'] = md5(downloaded_image)
1317 # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
1318 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
1320 if 'checksum' not in http_content
['image']:
1321 http_content
['image']['checksum'] = md5_string(image_file
)
1323 # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
1324 # If it is a URL, no error is sent. Checksum will be an empty string
1325 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "" and 'checksum' not in http_content
['image']:
1326 content
= "Image file not found"
1327 print "http_post_images error: %d %s" % (HTTP_Bad_Request
, content
)
1328 bottle
.abort(HTTP_Bad_Request
, content
)
1329 except Exception as e
:
1330 print "ERROR. Unexpected exception: %s" % (str(e
))
1331 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1332 #insert in data base
1333 result
, content
= my
.db
.new_image(http_content
['image'], tenant_id
)
1335 return http_get_image_id(tenant_id
, content
)
1337 print "http_post_images error %d %s" % (result
, content
)
1338 bottle
.abort(-result
, content
)
1341 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='DELETE')
1342 def http_delete_image_id(tenant_id
, image_id
):
1343 '''Deletes the image_id of a tenant. IT removes from tenants_images table.'''
1344 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1345 #check valid tenant_id
1346 result
,content
= check_valid_tenant(my
, tenant_id
)
1348 bottle
.abort(result
, content
)
1349 result
, content
= my
.db
.delete_image_flavor('image', image_id
, tenant_id
)
1351 bottle
.abort(HTTP_Not_Found
, content
)
1353 data
={'result' : content
}
1354 return format_out(data
)
1356 print "http_delete_image_id error",result
, content
1357 bottle
.abort(-result
, content
)
1360 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>/<action>', method
='POST')
1361 def http_attach_detach_images(tenant_id
, image_id
, action
):
1362 '''attach/detach an existing image in this tenant. That is insert/remove at tenants_images table.'''
1363 #TODO alf: not tested at all!!!
1364 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1365 #check valid tenant_id
1366 result
,content
= check_valid_tenant(my
, tenant_id
)
1368 bottle
.abort(result
, content
)
1369 if tenant_id
=='any':
1370 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1372 if action
!='attach' and action
!= 'detach':
1373 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1376 #Ensure that image exist
1377 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1378 where_
={'uuid': image_id
}
1379 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1381 if action
=='attach':
1382 text_error
="Image '%s' not found" % image_id
1384 text_error
="Image '%s' not found for tenant '%s'" % (image_id
, tenant_id
)
1385 bottle
.abort(HTTP_Not_Found
, text_error
)
1389 if action
=='attach':
1390 if image
['tenant_id']!=None:
1391 bottle
.abort(HTTP_Conflict
, "Image '%s' already attached to tenant '%s'" % (image_id
, tenant_id
))
1392 if image
['public']=='no' and not my
.admin
:
1393 #allow only attaching public images
1394 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private image")
1396 #insert in data base
1397 result
, content
= my
.db
.new_row('tenants_images', {'image_id':image_id
, 'tenant_id': tenant_id
})
1399 return http_get_image_id(tenant_id
, image_id
)
1401 if image
['tenant_id']==None:
1402 bottle
.abort(HTTP_Not_Found
, "Image '%s' not attached to tenant '%s'" % (image_id
, tenant_id
))
1403 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_images', WHERE
={'image_id':image_id
, 'tenant_id':tenant_id
})
1405 if image
['public']=='no':
1406 #try to delete the image completely to avoid orphan images, IGNORE error
1407 my
.db
.delete_row_by_dict(FROM
='images', WHERE
={'uuid':image_id
})
1408 data
={'result' : "image detached"}
1409 return format_out(data
)
1411 #if get here is because an error
1412 print "http_attach_detach_images error %d %s" % (result
, content
)
1413 bottle
.abort(-result
, content
)
1416 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='PUT')
1417 def http_put_image_id(tenant_id
, image_id
):
1418 '''update a image_id into the database.'''
1419 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1420 #check valid tenant_id
1421 result
,content
= check_valid_tenant(my
, tenant_id
)
1423 bottle
.abort(result
, content
)
1425 http_content
= format_in( image_update_schema
)
1426 r
= remove_extra_items(http_content
, image_update_schema
)
1427 if r
is not None: print "http_put_image_id: Warning: remove extra items ", r
1428 change_keys_http2db(http_content
['image'], http2db_image
)
1429 metadata_dict
= http_content
['image'].pop('metadata', None)
1430 if metadata_dict
is not None:
1431 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1432 #Ensure that image exist
1433 where_
={'uuid': image_id
}
1434 if tenant_id
=='any':
1438 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1439 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1440 result
, content
= my
.db
.get_table(SELECT
=('public',), DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND")
1442 text_error
="Image '%s' not found" % image_id
1443 if tenant_id
!='any':
1444 text_error
+=" for tenant '%s'" % image_id
1445 bottle
.abort(HTTP_Not_Found
, text_error
)
1448 if content
[0]['public']=='yes' and not my
.admin
:
1449 #allow only modifications over private images
1450 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public image")
1452 #insert in data base
1453 result
, content
= my
.db
.update_rows('images', http_content
['image'], {'uuid': image_id
})
1456 print "http_put_image_id error %d %s" % (result
, content
)
1457 bottle
.abort(-result
, content
)
1460 return http_get_image_id(tenant_id
, image_id
)
1467 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='GET')
1468 def http_get_servers(tenant_id
):
1469 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1470 result
,content
= check_valid_tenant(my
, tenant_id
)
1472 bottle
.abort(result
, content
)
1475 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_server
,
1476 ('id','name','description','hostId','imageRef','flavorRef','status', 'tenant_id') )
1477 if tenant_id
!='any':
1478 where_
['tenant_id'] = tenant_id
1479 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
='instances', WHERE
=where_
, LIMIT
=limit_
)
1481 print "http_get_servers Error", content
1482 bottle
.abort(-result
, content
)
1484 change_keys_http2db(content
, http2db_server
, reverse
=True)
1486 tenant_id
= row
.pop('tenant_id')
1487 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'servers', str(row
['id']) ) ), 'rel':'bookmark' } ]
1488 data
={'servers' : content
}
1489 return format_out(data
)
1491 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='GET')
1492 def http_get_server_id(tenant_id
, server_id
):
1493 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1494 #check valid tenant_id
1495 result
,content
= check_valid_tenant(my
, tenant_id
)
1497 bottle
.abort(result
, content
)
1500 result
, content
= my
.db
.get_instance(server_id
)
1502 bottle
.abort(HTTP_Not_Found
, content
)
1504 #change image/flavor-id to id and link
1505 convert_bandwidth(content
, reverse
=True)
1506 convert_datetime2str(content
)
1507 if content
["ram"]==0 : del content
["ram"]
1508 if content
["vcpus"]==0 : del content
["vcpus"]
1509 if 'flavor_id' in content
:
1510 if content
['flavor_id'] is not None:
1511 content
['flavor'] = {'id':content
['flavor_id'],
1512 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'flavors', str(content
['flavor_id']) ) ), 'rel':'bookmark'}]
1514 del content
['flavor_id']
1515 if 'image_id' in content
:
1516 if content
['image_id'] is not None:
1517 content
['image'] = {'id':content
['image_id'],
1518 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'images', str(content
['image_id']) ) ), 'rel':'bookmark'}]
1520 del content
['image_id']
1521 change_keys_http2db(content
, http2db_server
, reverse
=True)
1522 if 'extended' in content
:
1523 if 'devices' in content
['extended']: change_keys_http2db(content
['extended']['devices'], http2db_server
, reverse
=True)
1525 data
={'server' : content
}
1526 return format_out(data
)
1528 bottle
.abort(-result
, content
)
1531 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='POST')
1532 def http_post_server_id(tenant_id
):
1533 '''deploys a new server'''
1534 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1535 #check valid tenant_id
1536 result
,content
= check_valid_tenant(my
, tenant_id
)
1538 bottle
.abort(result
, content
)
1540 if tenant_id
=='any':
1541 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1543 http_content
= format_in( server_new_schema
)
1544 r
= remove_extra_items(http_content
, server_new_schema
)
1545 if r
is not None: print "http_post_serves: Warning: remove extra items ", r
1546 change_keys_http2db(http_content
['server'], http2db_server
)
1547 extended_dict
= http_content
['server'].get('extended', None)
1548 if extended_dict
is not None:
1549 result
, content
= check_extended(extended_dict
, True)
1551 print "http_post_servers wrong input extended error %d %s" % (result
, content
)
1552 bottle
.abort(-result
, content
)
1554 convert_bandwidth(extended_dict
)
1555 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_server
)
1557 server
= http_content
['server']
1558 server_start
= server
.get('start', 'yes')
1559 server
['tenant_id'] = tenant_id
1560 #check flavor valid and take info
1561 result
, content
= my
.db
.get_table(FROM
='tenants_flavors as tf join flavors as f on tf.flavor_id=f.uuid',
1562 SELECT
=('ram','vcpus','extended'), WHERE
={'uuid':server
['flavor_id'], 'tenant_id':tenant_id
})
1564 bottle
.abort(HTTP_Not_Found
, 'flavor_id %s not found' % server
['flavor_id'])
1566 server
['flavor']=content
[0]
1567 #check image valid and take info
1568 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1569 SELECT
=('path', 'metadata', 'image_id'),
1570 WHERE
={'uuid':server
['image_id'], "status":"ACTIVE"},
1571 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'},
1575 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % server
['image_id'])
1577 for image_dict
in content
:
1578 if image_dict
.get("image_id"):
1581 # insert in data base tenants_images
1582 r2
, c2
= my
.db
.new_row('tenants_images', {'image_id': server
['image_id'], 'tenant_id': tenant_id
})
1584 bottle
.abort(HTTP_Not_Found
, 'image_id %s cannot be used. Error %s' % (server
['image_id'], c2
))
1586 server
['image']={"path": content
[0]["path"], "metadata": content
[0]["metadata"]}
1587 if "hosts_id" in server
:
1588 result
, content
= my
.db
.get_table(FROM
='hosts', SELECT
=('uuid',), WHERE
={'uuid': server
['host_id']})
1590 bottle
.abort(HTTP_Not_Found
, 'hostId %s not found' % server
['host_id'])
1592 #print json.dumps(server, indent=4)
1594 result
, content
= ht
.create_server(server
, config_dic
['db'], config_dic
['db_lock'], config_dic
['mode']=='normal')
1597 #Insert instance to database
1600 print "inserting at DB"
1602 if server_start
== 'no':
1603 content
['status'] = 'INACTIVE'
1605 for net
in http_content
['server']['networks']:
1606 if net
['type'] == 'instance:ovs':
1607 dhcp_nets_id
.append(get_network_id(net
['net_id']))
1610 new_instance_result
, new_instance
= my
.db
.new_instance(content
, nets
, ports_to_free
)
1611 if new_instance_result
< 0:
1612 print "Error http_post_servers() :", new_instance_result
, new_instance
1613 bottle
.abort(-new_instance_result
, new_instance
)
1616 print "inserted at DB"
1618 for port
in ports_to_free
:
1619 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1621 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1625 my
.ovim
.net_update_ofc_thread(net_id
)
1626 except ovim
.ovimException
as e
:
1627 raise ovim
.ovimException("http_post_servers, Error updating network with id '{}', '{}'".
1628 format(net_id
, str(e
)), HTTP_Internal_Server_Error
)
1629 except Exception as e
:
1630 raise ovim
.ovimException("http_post_servers, Error updating network with id '{}', '{}'".
1631 format(net_id
, str(e
)), HTTP_Internal_Server_Error
)
1633 # look for dhcp ip address
1634 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "ip_address", "net_id"], WHERE
={"instance_id": new_instance
})
1637 if config_dic
.get("dhcp_server") and iface
["net_id"] in config_dic
["dhcp_nets"]:
1638 #print "dhcp insert add task"
1639 r
,c
= config_dic
['dhcp_thread'].insert_task("add", iface
["mac"])
1641 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1643 #ensure compute contain the bridge for ovs networks:
1644 server_net
= get_network_id(iface
['net_id'])
1645 if server_net
["network"].get('provider:physical', "")[:3] == 'OVS':
1646 vlan
= str(server_net
['network']['provider:vlan'])
1647 dhcp_enable
= bool(server_net
['network']['enable_dhcp'])
1649 dhcp_firt_ip
= str(server_net
['network']['dhcp_first_ip'])
1650 dhcp_last_ip
= str(server_net
['network']['dhcp_last_ip'])
1651 dhcp_cidr
= str(server_net
['network']['cidr'])
1652 gateway
= str(server_net
['network']['gateway'])
1653 vm_dhcp_ip
= c2
[0]["ip_address"]
1654 config_dic
['host_threads'][server
['host_id']].insert_task("create-ovs-bridge-port", vlan
)
1656 set_mac_dhcp(vm_dhcp_ip
, vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, c2
[0]['mac'])
1657 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
1658 http_controller
.ovim
.launch_dhcp_server(vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, gateway
)
1661 server
['uuid'] = new_instance
1662 server_start
= server
.get('start', 'yes')
1664 if server_start
!= 'no':
1665 server
['paused'] = True if server_start
== 'paused' else False
1666 server
['action'] = {"start":None}
1667 server
['status'] = "CREATING"
1669 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1671 my
.db
.update_rows('instances', {'status':"ERROR"}, {'uuid':server
['uuid'], 'last_error':c
}, log
=True)
1673 return http_get_server_id(tenant_id
, new_instance
)
1675 bottle
.abort(HTTP_Bad_Request
, content
)
1678 def http_server_action(server_id
, tenant_id
, action
):
1679 '''Perform actions over a server as resume, reboot, terminate, ...'''
1680 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1681 server
={"uuid": server_id
, "action":action
}
1682 where
={'uuid': server_id
}
1683 if tenant_id
!='any':
1684 where
['tenant_id']= tenant_id
1685 result
, content
= my
.db
.get_table(FROM
='instances', WHERE
=where
)
1687 bottle
.abort(HTTP_Not_Found
, "server %s not found" % server_id
)
1690 print "http_post_server_action error getting data %d %s" % (result
, content
)
1691 bottle
.abort(HTTP_Internal_Server_Error
, content
)
1693 server
.update(content
[0])
1694 tenant_id
= server
["tenant_id"]
1696 #TODO check a right content
1698 if 'terminate' in action
:
1699 new_status
='DELETING'
1700 elif server
['status'] == 'ERROR': #or server['status'] == 'CREATING':
1701 if 'terminate' not in action
and 'rebuild' not in action
:
1702 bottle
.abort(HTTP_Method_Not_Allowed
, "Server is in ERROR status, must be rebuit or deleted ")
1704 # elif server['status'] == 'INACTIVE':
1705 # if 'start' not in action and 'createImage' not in action:
1706 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'INACTIVE' status is 'start'")
1708 # if 'start' in action:
1709 # new_status='CREATING'
1710 # server['paused']='no'
1711 # elif server['status'] == 'PAUSED':
1712 # if 'resume' not in action:
1713 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'PAUSED' status is 'resume'")
1715 # elif server['status'] == 'ACTIVE':
1716 # if 'pause' not in action and 'reboot'not in action and 'shutoff'not in action:
1717 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'ACTIVE' status is 'pause','reboot' or 'shutoff'")
1720 if 'start' in action
or 'createImage' in action
or 'rebuild' in action
:
1721 #check image valid and take info
1722 image_id
= server
['image_id']
1723 if 'createImage' in action
:
1724 if 'imageRef' in action
['createImage']:
1725 image_id
= action
['createImage']['imageRef']
1726 elif 'disk' in action
['createImage']:
1727 result
, content
= my
.db
.get_table(FROM
='instance_devices',
1728 SELECT
=('image_id','dev'), WHERE
={'instance_id':server
['uuid'],"type":"disk"})
1730 bottle
.abort(HTTP_Not_Found
, 'disk not found for server')
1734 if action
['createImage']['imageRef']['disk'] != None:
1735 for disk
in content
:
1736 if disk
['dev'] == action
['createImage']['imageRef']['disk']:
1737 disk_id
= disk
['image_id']
1740 bottle
.abort(HTTP_Not_Found
, 'disk %s not found for server' % action
['createImage']['imageRef']['disk'])
1743 bottle
.abort(HTTP_Not_Found
, 'more than one disk found for server' )
1747 image_id
= content
[0]['image_id']
1749 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1750 SELECT
=('path','metadata'), WHERE
={'uuid':image_id
, "status":"ACTIVE"},
1751 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'}, WHERE_AND_OR
="AND", DISTINCT
=True)
1753 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % image_id
)
1755 if content
[0]['metadata'] is not None:
1757 metadata
= json
.loads(content
[0]['metadata'])
1759 return -HTTP_Internal_Server_Error
, "Can not decode image metadata"
1760 content
[0]['metadata']=metadata
1762 content
[0]['metadata'] = {}
1763 server
['image']=content
[0]
1764 if 'createImage' in action
:
1765 action
['createImage']['source'] = {'image_id': image_id
, 'path': content
[0]['path']}
1766 if 'createImage' in action
:
1767 #Create an entry in Database for the new image
1768 new_image
={'status':'BUILD', 'progress': 0 }
1769 new_image_metadata
=content
[0]
1770 if 'metadata' in server
['image'] and server
['image']['metadata'] != None:
1771 new_image_metadata
.update(server
['image']['metadata'])
1772 new_image_metadata
= {"use_incremental":"no"}
1773 if 'metadata' in action
['createImage']:
1774 new_image_metadata
.update(action
['createImage']['metadata'])
1775 new_image
['metadata'] = json
.dumps(new_image_metadata
)
1776 new_image
['name'] = action
['createImage'].get('name', None)
1777 new_image
['description'] = action
['createImage'].get('description', None)
1778 new_image
['uuid']=my
.db
.new_uuid()
1779 if 'path' in action
['createImage']:
1780 new_image
['path'] = action
['createImage']['path']
1782 new_image
['path']="/provisional/path/" + new_image
['uuid']
1783 result
, image_uuid
= my
.db
.new_image(new_image
, tenant_id
)
1785 bottle
.abort(HTTP_Bad_Request
, 'Error: ' + image_uuid
)
1787 server
['new_image'] = new_image
1791 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1793 print "Task queue full at host ", server
['host_id']
1794 bottle
.abort(HTTP_Request_Timeout
, c
)
1795 if 'createImage' in action
and result
>= 0:
1796 return http_get_image_id(tenant_id
, image_uuid
)
1798 #Update DB only for CREATING or DELETING status
1799 data
={'result' : 'in process'}
1800 if new_status
!= None and new_status
== 'DELETING':
1805 #look for dhcp ip address
1806 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "net_id"], WHERE
={"instance_id": server_id
})
1807 r
, c
= my
.db
.delete_instance(server_id
, tenant_id
, nets
, ports_to_free
, net_ovs_list
, "requested by http")
1808 for port
in ports_to_free
:
1809 r1
,c1
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1811 print ' http_post_server_action error at server deletion ERROR resore-iface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c1
1812 data
={'result' : 'deleting in process, but ifaces cannot be restored!!!!!'}
1815 my
.ovim
.net_update_ofc_thread(net_id
)
1816 except ovim
.ovimException
as e
:
1817 raise ovim
.ovimException("http_post_servers, Error updating network with id '{}', '{}'".
1818 format(net_id
, str(e
)), HTTP_Internal_Server_Error
)
1819 except Exception as e
:
1820 raise ovim
.ovimException("http_post_server_action error at server deletion "
1821 "ERROR UPDATING NET '{}', '{}'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!".
1822 format(net_id
, str(e
)), HTTP_Internal_Server_Error
)
1824 data
= {'result': 'deleting in process, but openflow rules cannot be deleted!!!!!'}
1826 # look for dhcp ip address
1827 if r2
>0 and config_dic
.get("dhcp_server"):
1829 if iface
["net_id"] in config_dic
["dhcp_nets"]:
1830 r
,c
= config_dic
['dhcp_thread'].insert_task("del", iface
["mac"])
1831 #print "dhcp insert del task"
1833 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1834 # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan)
1835 for net
in net_ovs_list
:
1840 delete_dhcp_ovs_bridge(vlan
, net_id
)
1841 delete_mac_dhcp(vm_ip
, vlan
, mac
)
1842 config_dic
['host_threads'][server
['host_id']].insert_task('del-ovs-port', vlan
, net_id
)
1843 return format_out(data
)
1847 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='DELETE')
1848 def http_delete_server_id(tenant_id
, server_id
):
1849 '''delete a server'''
1850 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1851 #check valid tenant_id
1852 result
,content
= check_valid_tenant(my
, tenant_id
)
1854 bottle
.abort(result
, content
)
1857 return http_server_action(server_id
, tenant_id
, {"terminate":None} )
1860 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>/action', method
='POST')
1861 def http_post_server_action(tenant_id
, server_id
):
1862 '''take an action over a server'''
1863 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1864 #check valid tenant_id
1865 result
,content
= check_valid_tenant(my
, tenant_id
)
1867 bottle
.abort(result
, content
)
1869 http_content
= format_in( server_action_schema
)
1870 #r = remove_extra_items(http_content, server_action_schema)
1871 #if r is not None: print "http_post_server_action: Warning: remove extra items ", r
1873 return http_server_action(server_id
, tenant_id
, http_content
)
1880 @bottle.route(url_base
+ '/networks', method
='GET')
1881 def http_get_networks():
1883 Get all networks available
1886 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1890 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_network
,
1891 ('id', 'name', 'tenant_id', 'type',
1892 'shared', 'provider:vlan', 'status', 'last_error',
1893 'admin_state_up', 'provider:physical'))
1894 if "tenant_id" in where_
:
1895 del where_
["tenant_id"]
1897 content
= my
.ovim
.get_networks(select_
, where_
, limit_
)
1899 delete_nulls(content
)
1900 change_keys_http2db(content
, http2db_network
, reverse
=True)
1901 data
= {'networks': content
}
1902 return format_out(data
)
1904 except ovim
.ovimException
as e
:
1905 my
.logger
.error(str(e
), exc_info
=True)
1906 bottle
.abort(e
.http_code
, str(e
))
1907 except Exception as e
:
1908 my
.logger
.error(str(e
), exc_info
=True)
1909 bottle
.abort(HTTP_Bad_Request
, str(e
))
1912 @bottle.route(url_base
+ '/networks/<network_id>', method
='GET')
1913 def http_get_network_id(network_id
):
1915 Get a network data by id
1919 data
= get_network_id(network_id
)
1920 return format_out(data
)
1923 def get_network_id(network_id
):
1925 Get network from DB by id
1926 :param network_id: network Id
1929 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1933 where_
= bottle
.request
.query
1934 content
= my
.ovim
.show_network(network_id
, where_
)
1936 change_keys_http2db(content
, http2db_network
, reverse
=True)
1937 delete_nulls(content
)
1938 data
= {'network': content
}
1940 except ovim
.ovimException
as e
:
1941 my
.logger
.error(str(e
), exc_info
=True)
1942 bottle
.abort(e
.http_code
, str(e
))
1943 except Exception as e
:
1944 my
.logger
.error(str(e
), exc_info
=True)
1945 bottle
.abort(HTTP_Bad_Request
, str(e
))
1948 @bottle.route(url_base
+ '/networks', method
='POST')
1949 def http_post_networks():
1951 Insert a network into the database.
1954 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1958 http_content
= format_in(network_new_schema
)
1959 r
= remove_extra_items(http_content
, network_new_schema
)
1961 print "http_post_networks: Warning: remove extra items ", r
1962 change_keys_http2db(http_content
['network'], http2db_network
)
1963 network
= http_content
['network']
1964 content
= my
.ovim
.new_network(network
)
1965 return format_out(get_network_id(content
))
1966 except ovim
.ovimException
as e
:
1967 my
.logger
.error(str(e
), exc_info
=True)
1968 bottle
.abort(e
.http_code
, str(e
))
1969 except Exception as e
:
1970 my
.logger
.error(str(e
), exc_info
=True)
1971 bottle
.abort(HTTP_Bad_Request
, str(e
))
1974 @bottle.route(url_base
+ '/networks/<network_id>', method
='PUT')
1975 def http_put_network_id(network_id
):
1977 Update a network_id into DB.
1978 :param network_id: network id
1981 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1985 http_content
= format_in(network_update_schema
)
1986 change_keys_http2db(http_content
['network'], http2db_network
)
1987 network
= http_content
['network']
1988 return format_out(my
.ovim
.edit_network(network_id
, network
))
1990 except ovim
.ovimException
as e
:
1991 my
.logger
.error(str(e
), exc_info
=True)
1992 bottle
.abort(e
.http_code
, str(e
))
1993 except Exception as e
:
1994 my
.logger
.error(str(e
), exc_info
=True)
1995 bottle
.abort(HTTP_Bad_Request
, str(e
))
1998 @bottle.route(url_base
+ '/networks/<network_id>', method
='DELETE')
1999 def http_delete_network_id(network_id
):
2001 Delete a network_id from the database.
2002 :param network_id: Network id
2005 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2008 # delete from the data base
2009 content
= my
.ovim
.delete_network(network_id
)
2010 data
= {'result': content
}
2011 return format_out(data
)
2013 except ovim
.ovimException
as e
:
2014 my
.logger
.error(str(e
), exc_info
=True)
2015 bottle
.abort(e
.http_code
, str(e
))
2016 except Exception as e
:
2017 my
.logger
.error(str(e
), exc_info
=True)
2018 bottle
.abort(HTTP_Bad_Request
, str(e
))
2025 @bottle.route(url_base
+ '/openflow/controller', method
='GET')
2026 def http_get_openflow_controller():
2028 Retrieve a openflow controllers list from DB.
2031 # TODO check if show a proper list
2032 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2035 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_ofc
,
2036 ('id', 'name', 'dpid', 'ip', 'port', 'type',
2037 'version', 'user', 'password'))
2039 content
= my
.ovim
.get_of_controllers(select_
, where_
)
2040 delete_nulls(content
)
2041 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2042 data
= {'ofcs': content
}
2043 return format_out(data
)
2044 except ovim
.ovimException
as e
:
2045 my
.logger
.error(str(e
), exc_info
=True)
2046 bottle
.abort(e
.http_code
, str(e
))
2047 except Exception as e
:
2048 my
.logger
.error(str(e
), exc_info
=True)
2049 bottle
.abort(HTTP_Bad_Request
, str(e
))
2052 @bottle.route(url_base
+ '/openflow/controller/<uuid>', method
='GET')
2053 def http_get_openflow_controller_id(uuid
):
2055 Get an openflow controller by dpid from DB.get_of_controllers
2057 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2061 content
= my
.ovim
.show_of_controller(uuid
)
2062 delete_nulls(content
)
2063 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2064 data
= {'ofc': content
}
2065 return format_out(data
)
2066 except ovim
.ovimException
as e
:
2067 my
.logger
.error(str(e
), exc_info
=True)
2068 bottle
.abort(e
.http_code
, str(e
))
2069 except Exception as e
:
2070 my
.logger
.error(str(e
), exc_info
=True)
2071 bottle
.abort(HTTP_Bad_Request
, str(e
))
2074 @bottle.route(url_base
+ '/openflow/controller/', method
='POST')
2075 def http_post_openflow_controller():
2077 Create a new openflow controller into DB
2080 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2083 http_content
= format_in(openflow_controller_schema
)
2084 of_c
= http_content
['ofc']
2085 uuid
= my
.ovim
.new_of_controller(of_c
)
2086 content
= my
.ovim
.show_of_controller(uuid
)
2087 delete_nulls(content
)
2088 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2089 data
= {'ofc': content
}
2090 return format_out(data
)
2091 except ovim
.ovimException
as e
:
2092 my
.logger
.error(str(e
), exc_info
=True)
2093 bottle
.abort(e
.http_code
, str(e
))
2094 except Exception as e
:
2095 my
.logger
.error(str(e
), exc_info
=True)
2096 bottle
.abort(HTTP_Bad_Request
, str(e
))
2099 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='PUT')
2100 def http_put_openflow_controller_by_id(of_controller_id
):
2102 Create an openflow controller into DB
2103 :param of_controller_id: openflow controller dpid
2106 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2109 http_content
= format_in(openflow_controller_schema
)
2110 of_c
= http_content
['ofc']
2112 content
= my
.ovim
.edit_of_controller(of_controller_id
, of_c
)
2113 delete_nulls(content
)
2114 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2115 data
= {'ofc': content
}
2116 return format_out(data
)
2117 except ovim
.ovimException
as e
:
2118 my
.logger
.error(str(e
), exc_info
=True)
2119 bottle
.abort(e
.http_code
, str(e
))
2120 except Exception as e
:
2121 my
.logger
.error(str(e
), exc_info
=True)
2122 bottle
.abort(HTTP_Bad_Request
, str(e
))
2125 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='DELETE')
2126 def http_delete_openflow_controller(of_controller_id
):
2128 Delete an openflow controller from DB.
2129 :param of_controller_id: openflow controller dpid
2132 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2135 content
= my
.ovim
.delete_of_controller(of_controller_id
)
2136 data
= {'result': content
}
2137 return format_out(data
)
2138 except ovim
.ovimException
as e
:
2139 my
.logger
.error(str(e
), exc_info
=True)
2140 bottle
.abort(e
.http_code
, str(e
))
2141 except Exception as e
:
2142 my
.logger
.error(str(e
), exc_info
=True)
2143 bottle
.abort(HTTP_Bad_Request
, str(e
))
2146 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='GET')
2147 def http_get_openflow_id(network_id
):
2149 To obtain the list of openflow rules of a network
2150 :param network_id: network id
2153 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2156 if network_id
== 'all':
2159 content
= my
.ovim
.get_openflow_rules(network_id
)
2160 data
= {'openflow-rules': content
}
2161 except ovim
.ovimException
as e
:
2162 my
.logger
.error(str(e
), exc_info
=True)
2163 bottle
.abort(e
.http_code
, str(e
))
2164 except Exception as e
:
2165 my
.logger
.error(str(e
), exc_info
=True)
2166 bottle
.abort(HTTP_Bad_Request
, str(e
))
2168 return format_out(data
)
2171 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='PUT')
2172 def http_put_openflow_id(network_id
):
2174 To make actions over the net. The action is to reinstall the openflow rules
2175 network_id can be 'all'
2176 :param network_id: network id
2179 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2182 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2184 if network_id
== 'all':
2188 result
= my
.ovim
.edit_openflow_rules(network_id
)
2189 except ovim
.ovimException
as e
:
2190 my
.logger
.error(str(e
), exc_info
=True)
2191 bottle
.abort(e
.http_code
, str(e
))
2192 except Exception as e
:
2193 my
.logger
.error(str(e
), exc_info
=True)
2194 bottle
.abort(HTTP_Bad_Request
, str(e
))
2196 data
= {'result': str(result
) + " nets updates"}
2197 return format_out(data
)
2199 @bottle.route(url_base
+ '/networks/clear/openflow/<ofc_id>', method
='DELETE')
2200 @bottle.route(url_base
+ '/networks/clear/openflow', method
='DELETE')
2201 def http_clear_openflow_rules(ofc_id
=None):
2203 To make actions over the net. The action is to delete ALL openflow rules
2206 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2209 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2211 my
.ovim
.delete_openflow_rules(ofc_id
)
2212 except ovim
.ovimException
as e
:
2213 my
.logger
.error(str(e
), exc_info
=True)
2214 bottle
.abort(e
.http_code
, str(e
))
2215 except Exception as e
:
2216 my
.logger
.error(str(e
), exc_info
=True)
2217 bottle
.abort(HTTP_Bad_Request
, str(e
))
2219 data
= {'result': " Clearing openflow rules in process"}
2220 return format_out(data
)
2222 @bottle.route(url_base
+ '/networks/openflow/ports/<ofc_id>', method
='GET')
2223 @bottle.route(url_base
+ '/networks/openflow/ports', method
='GET')
2224 def http_get_openflow_ports(ofc_id
=None):
2226 Obtain switch ports names of openflow controller
2229 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2232 ports
= my
.ovim
.get_openflow_ports(ofc_id
)
2233 data
= {'ports': ports
}
2234 except ovim
.ovimException
as e
:
2235 my
.logger
.error(str(e
), exc_info
=True)
2236 bottle
.abort(e
.http_code
, str(e
))
2237 except Exception as e
:
2238 my
.logger
.error(str(e
), exc_info
=True)
2239 bottle
.abort(HTTP_Bad_Request
, str(e
))
2241 return format_out(data
)
2247 @bottle.route(url_base
+ '/ports', method
='GET')
2248 def http_get_ports():
2250 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2251 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_port
,
2252 ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
2253 'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
2255 ports
= my
.ovim
.get_ports(columns
=select_
, filter=where_
, limit
=limit_
)
2257 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2258 data
={'ports' : ports
}
2259 return format_out(data
)
2260 except ovim
.ovimException
as e
:
2261 my
.logger
.error(str(e
), exc_info
=True)
2262 bottle
.abort(e
.http_code
, str(e
))
2263 except Exception as e
:
2264 my
.logger
.error(str(e
), exc_info
=True)
2265 bottle
.abort(HTTP_Bad_Request
, str(e
))
2267 @bottle.route(url_base
+ '/ports/<port_id>', method
='GET')
2268 def http_get_port_id(port_id
):
2269 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2271 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2273 bottle
.abort(HTTP_Not_Found
, 'port %s not found' % port_id
)
2276 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2277 data
= {'port': ports
[0]}
2278 return format_out(data
)
2279 except ovim
.ovimException
as e
:
2280 my
.logger
.error(str(e
), exc_info
=True)
2281 bottle
.abort(e
.http_code
, str(e
))
2282 except Exception as e
:
2283 my
.logger
.error(str(e
), exc_info
=True)
2284 bottle
.abort(HTTP_Bad_Request
, str(e
))
2286 @bottle.route(url_base
+ '/ports', method
='POST')
2287 def http_post_ports():
2288 '''insert an external port into the database.'''
2289 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2291 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2293 http_content
= format_in( port_new_schema
)
2294 r
= remove_extra_items(http_content
, port_new_schema
)
2295 if r
is not None: print "http_post_ports: Warning: remove extra items ", r
2296 change_keys_http2db(http_content
['port'], http2db_port
)
2297 port
=http_content
['port']
2299 port_id
= my
.ovim
.new_port(port
)
2300 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2302 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' inserted but not found at database".format(port_id
))
2305 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2306 data
= {'port': ports
[0]}
2307 return format_out(data
)
2308 except ovim
.ovimException
as e
:
2309 my
.logger
.error(str(e
), exc_info
=True)
2310 bottle
.abort(e
.http_code
, str(e
))
2311 except Exception as e
:
2312 my
.logger
.error(str(e
), exc_info
=True)
2313 bottle
.abort(HTTP_Bad_Request
, str(e
))
2315 @bottle.route(url_base
+ '/ports/<port_id>', method
='PUT')
2316 def http_put_port_id(port_id
):
2317 '''update a port_id into the database.'''
2318 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2320 http_content
= format_in( port_update_schema
)
2321 change_keys_http2db(http_content
['port'], http2db_port
)
2322 port_dict
=http_content
['port']
2324 for k
in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
2325 if k
in port_dict
and not my
.admin
:
2326 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges for changing " + k
)
2329 port_id
= my
.ovim
.edit_port(port_id
, port_dict
, my
.admin
)
2330 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2332 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' edited but not found at database".format(port_id
))
2335 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2336 data
= {'port': ports
[0]}
2337 return format_out(data
)
2338 except ovim
.ovimException
as e
:
2339 my
.logger
.error(str(e
), exc_info
=True)
2340 bottle
.abort(e
.http_code
, str(e
))
2341 except Exception as e
:
2342 my
.logger
.error(str(e
), exc_info
=True)
2343 bottle
.abort(HTTP_Bad_Request
, str(e
))
2346 @bottle.route(url_base
+ '/ports/<port_id>', method
='DELETE')
2347 def http_delete_port_id(port_id
):
2348 '''delete a port_id from the database.'''
2349 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2351 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2354 result
= my
.ovim
.delete_port(port_id
)
2355 data
= {'result': result
}
2356 return format_out(data
)
2357 except ovim
.ovimException
as e
:
2358 my
.logger
.error(str(e
), exc_info
=True)
2359 bottle
.abort(e
.http_code
, str(e
))
2360 except Exception as e
:
2361 my
.logger
.error(str(e
), exc_info
=True)
2362 bottle
.abort(HTTP_Bad_Request
, str(e
))
2365 @bottle.route(url_base
+ '/openflow/mapping', method
='POST')
2366 def http_of_port_mapping():
2368 Insert a tenant into the database.
2371 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2374 http_content
= format_in(of_port_map_new_schema
)
2375 r
= remove_extra_items(http_content
, of_port_map_new_schema
)
2377 my
.logger
.error("http_of_port_mapping: Warning: remove extra items " + str(r
), exc_info
=True)
2379 # insert in data base
2380 port_mapping
= my
.ovim
.set_of_port_mapping(http_content
['of_port_mapings'])
2381 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2382 delete_nulls(port_mapping
)
2383 data
= {'of_port_mappings': port_mapping
}
2384 return format_out(data
)
2385 except ovim
.ovimException
as e
:
2386 my
.logger
.error(str(e
), exc_info
=True)
2387 bottle
.abort(e
.http_code
, str(e
))
2388 except Exception as e
:
2389 my
.logger
.error(str(e
), exc_info
=True)
2390 bottle
.abort(HTTP_Bad_Request
, str(e
))
2393 @bottle.route(url_base
+ '/openflow/mapping', method
='GET')
2394 def get_of_port_mapping():
2396 Insert a tenant into the database.
2399 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2402 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_id
,
2403 ('id', 'ofc_id', 'region', 'compute_node', 'pci',
2404 'switch_dpid', 'switch_port', 'switch_mac'))
2405 # insert in data base
2406 port_mapping
= my
.ovim
.get_of_port_mappings(select_
, where_
)
2407 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2408 delete_nulls(port_mapping
)
2409 data
= {'of_port_mappings': port_mapping
}
2410 return format_out(data
)
2411 except ovim
.ovimException
as e
:
2412 my
.logger
.error(str(e
), exc_info
=True)
2413 bottle
.abort(e
.http_code
, str(e
))
2414 except Exception as e
:
2415 my
.logger
.error(str(e
), exc_info
=True)
2416 bottle
.abort(HTTP_Bad_Request
, str(e
))
2419 @bottle.route(url_base
+ '/openflow/mapping/<region>', method
='DELETE')
2420 def delete_of_port_mapping(region
):
2422 Insert a tenant into the database.
2425 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2428 # insert in data base
2429 db_filter
= {'region': region
}
2430 result
= my
.ovim
.clear_of_port_mapping(db_filter
)
2431 data
= {'result': result
}
2432 return format_out(data
)
2433 except ovim
.ovimException
as e
:
2434 my
.logger
.error(str(e
), exc_info
=True)
2435 bottle
.abort(e
.http_code
, str(e
))
2436 except Exception as e
:
2437 my
.logger
.error(str(e
), exc_info
=True)
2438 bottle
.abort(HTTP_Bad_Request
, str(e
))