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$"
42 from netaddr
import IPNetwork
, IPAddress
, all_matching_cidrs
43 #import only if needed because not needed in test mode. To allow an easier installation import RADclass
44 from jsonschema
import validate
as js_v
, exceptions
as js_e
45 import host_thread
as ht
46 from vim_schema
import host_new_schema
, host_edit_schema
, tenant_new_schema
, \
48 flavor_new_schema
, flavor_update_schema
, \
49 image_new_schema
, image_update_schema
, \
50 server_new_schema
, server_action_schema
, network_new_schema
, network_update_schema
, \
51 port_new_schema
, port_update_schema
, openflow_controller_schema
, of_port_map_new_schema
58 global RADclass_module
59 RADclass_module
=None #RADclass module is charged only if not in test mode
63 HTTP_Bad_Request
= 400
64 HTTP_Unauthorized
= 401
67 HTTP_Method_Not_Allowed
= 405
68 HTTP_Not_Acceptable
= 406
69 HTTP_Request_Timeout
= 408
71 HTTP_Service_Unavailable
= 503
72 HTTP_Internal_Server_Error
= 500
75 hash_md5
= hashlib
.md5()
76 with
open(fname
, "rb") as f
:
77 for chunk
in iter(lambda: f
.read(4096), b
""):
78 hash_md5
.update(chunk
)
79 return hash_md5
.hexdigest()
81 def md5_string(fname
):
82 hash_md5
= hashlib
.md5()
83 hash_md5
.update(fname
)
84 return hash_md5
.hexdigest()
86 def check_extended(extended
, allow_net_attach
=False):
87 '''Makes and extra checking of extended input that cannot be done using jsonschema
89 allow_net_attach: for allowing or not the uuid field at interfaces
90 that are allowed for instance, but not for flavors
91 Return: (<0, error_text) if error; (0,None) if not error '''
92 if "numas" not in extended
: return 0, None
95 for numa
in extended
["numas"]:
99 if "cores-id" in numa
:
100 if len(numa
["cores-id"]) != numa
["cores"]:
101 return -HTTP_Bad_Request
, "different number of cores-id (%d) than cores (%d) at numa %d" % (len(numa
["cores-id"]), numa
["cores"],numaid
)
102 id_s
.extend(numa
["cores-id"])
103 if "threads" in numa
:
105 if "threads-id" in numa
:
106 if len(numa
["threads-id"]) != numa
["threads"]:
107 return -HTTP_Bad_Request
, "different number of threads-id (%d) than threads (%d) at numa %d" % (len(numa
["threads-id"]), numa
["threads"],numaid
)
108 id_s
.extend(numa
["threads-id"])
109 if "paired-threads" in numa
:
111 if "paired-threads-id" in numa
:
112 if len(numa
["paired-threads-id"]) != numa
["paired-threads"]:
113 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
)
114 for pair
in numa
["paired-threads-id"]:
116 return -HTTP_Bad_Request
, "paired-threads-id must contain a list of two elements list at numa %d" % (numaid
)
119 return -HTTP_Service_Unavailable
, "only one of cores, threads, paired-threads are allowed in this version at numa %d" % numaid
121 if "interfaces" in numa
:
125 for interface
in numa
["interfaces"]:
126 if "uuid" in interface
and not allow_net_attach
:
127 return -HTTP_Bad_Request
, "uuid field is not allowed at numa %d interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
128 if "mac_address" in interface
and interface
["dedicated"]=="yes":
129 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
)
130 if "name" in interface
:
131 if interface
["name"] in names
:
132 return -HTTP_Bad_Request
, "name repeated at numa %d, interface %s position %d" % (numaid
, interface
.get("name",""), ifaceid
)
133 names
.append(interface
["name"])
134 if "vpci" in interface
:
135 if interface
["vpci"] in vpcis
:
136 return -HTTP_Bad_Request
, "vpci %s repeated at numa %d, interface %s position %d" % (interface
["vpci"], numaid
, interface
.get("name",""), ifaceid
)
137 vpcis
.append(interface
["vpci"])
141 return -HTTP_Service_Unavailable
, "only one numa can be defined in this version "
142 for a
in range(0,len(id_s
)):
144 return -HTTP_Bad_Request
, "core/thread identifiers must start at 0 and gaps are not alloed. Missing id number %d" % a
149 # dictionaries that change from HTTP API to database naming
151 http2db_id
={'id':'uuid'}
152 http2db_host
={'id':'uuid'}
153 http2db_tenant
={'id':'uuid'}
154 http2db_flavor
={'id':'uuid','imageRef':'image_id', 'size': 'image_size'}
155 http2db_image
={'id':'uuid', 'created':'created_at', 'updated':'modified_at', 'public': 'public'}
156 http2db_server
={'id':'uuid','hostId':'host_id','flavorRef':'flavor_id','imageRef':'image_id','created':'created_at'}
157 http2db_network
={'id':'uuid','provider:vlan':'vlan', 'provider:physical': 'provider'}
158 http2db_ofc
= {'id': 'uuid'}
159 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'}
162 def remove_extra_items(data
, schema
):
166 if type(data
) is tuple or type(data
) is list:
168 a
= remove_extra_items(d
, schema
['items'])
169 if a
is not None: deleted
.append(a
)
170 elif type(data
) is dict:
172 for k
in data
.keys():
173 if 'patternProperties' in schema
and k
not in schema
['properties'].keys():
174 reg_ex_list
= schema
['patternProperties'].keys()
175 for reg_ex
in reg_ex_list
:
176 if not re
.match(reg_ex
, k
):
179 elif 'properties' not in schema
or k
not in schema
['properties'].keys(): # or k not in schema['patternProperties'].keys():
183 a
= remove_extra_items(data
[k
], schema
['properties'][k
])
184 if a
is not None: deleted
.append({k
:a
})
185 if len(deleted
) == 0: return None
186 elif len(deleted
) == 1: return deleted
[0]
190 def delete_nulls(var
):
191 if type(var
) is dict:
193 if var
[k
] is None: del var
[k
]
194 elif type(var
[k
]) is dict or type(var
[k
]) is list or type(var
[k
]) is tuple:
195 if delete_nulls(var
[k
]): del var
[k
]
196 if len(var
) == 0: return True
197 elif type(var
) is list or type(var
) is tuple:
199 if type(k
) is dict: delete_nulls(k
)
200 if len(var
) == 0: return True
204 class httpserver(threading
.Thread
):
205 def __init__(self
, ovim
, name
="http", host
='localhost', port
=8080, admin
=False, config_
=None):
207 Creates a new thread to attend the http connections
209 db_conn: database connection
210 name: name of this thread
211 host: ip or name where to listen
212 port: port where to listen
213 admin: if this has privileges of administrator or not
214 config_: unless the first thread must be provided. It is a global dictionary where to allocate the self variable
220 if config_
is not None:
222 if 'http_threads' not in config_dic
:
223 config_dic
['http_threads'] = {}
224 threading
.Thread
.__init
__(self
)
227 self
.db
= ovim
.db
#TODO OVIM remove
230 if name
in config_dic
:
231 print "httpserver Warning!!! Onether thread with the same name", name
233 while name
+str(n
) in config_dic
:
237 self
.url_preffix
= 'http://' + self
.host
+ ':' + str(self
.port
) + url_base
238 config_dic
['http_threads'][name
] = self
240 #Ensure that when the main program exits the thread will also exit
243 self
.logger
= logging
.getLogger("openvim.http")
246 bottle
.run(host
=self
.host
, port
=self
.port
, debug
=True) #quiet=True
248 def gethost(self
, host_id
):
249 result
, content
= self
.db
.get_host(host_id
)
251 print "httpserver.gethost error %d %s" % (result
, content
)
252 bottle
.abort(-result
, content
)
254 print "httpserver.gethost host '%s' not found" % host_id
255 bottle
.abort(HTTP_Not_Found
, content
)
257 data
={'host' : content
}
258 convert_boolean(content
, ('admin_state_up',) )
259 change_keys_http2db(content
, http2db_host
, reverse
=True)
261 return format_out(data
)
263 @bottle.route(url_base
+ '/', method
='GET')
266 return 'works' #TODO: put links or redirection to /openvim???
272 def change_keys_http2db(data
, http_db
, reverse
=False):
273 '''Change keys of dictionary data according to the key_dict values
274 This allow change from http interface names to database names.
275 When reverse is True, the change is otherwise
277 data: can be a dictionary or a list
278 http_db: is a dictionary with hhtp names as keys and database names as value
279 reverse: by default change is done from http API to database. If True change is done otherwise
280 Return: None, but data is modified'''
281 if type(data
) is tuple or type(data
) is list:
283 change_keys_http2db(d
, http_db
, reverse
)
284 elif type(data
) is dict or type(data
) is bottle
.FormsDict
:
286 for k
,v
in http_db
.items():
287 if v
in data
: data
[k
]=data
.pop(v
)
289 for k
,v
in http_db
.items():
290 if k
in data
: data
[v
]=data
.pop(k
)
294 def format_out(data
):
295 '''return string of dictionary data according to requested json, yaml, xml. By default json'''
296 if 'application/yaml' in bottle
.request
.headers
.get('Accept'):
297 bottle
.response
.content_type
='application/yaml'
298 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='"'
299 else: #by default json
300 bottle
.response
.content_type
='application/json'
301 #return data #json no style
302 return json
.dumps(data
, indent
=4) + "\n"
304 def format_in(schema
):
306 error_text
= "Invalid header format "
307 format_type
= bottle
.request
.headers
.get('Content-Type', 'application/json')
308 if 'application/json' in format_type
:
309 error_text
= "Invalid json format "
310 #Use the json decoder instead of bottle decoder because it informs about the location of error formats with a ValueError exception
311 client_data
= json
.load(bottle
.request
.body
)
312 #client_data = bottle.request.json()
313 elif 'application/yaml' in format_type
:
314 error_text
= "Invalid yaml format "
315 client_data
= yaml
.load(bottle
.request
.body
)
316 elif format_type
== 'application/xml':
317 bottle
.abort(501, "Content-Type: application/xml not supported yet.")
319 print "HTTP HEADERS: " + str(bottle
.request
.headers
.items())
320 bottle
.abort(HTTP_Not_Acceptable
, 'Content-Type ' + str(format_type
) + ' not supported.')
322 #if client_data == None:
323 # bottle.abort(HTTP_Bad_Request, "Content error, empty")
327 #print "HTTP input data: ", str(client_data)
328 error_text
= "Invalid content "
329 js_v(client_data
, schema
)
332 except (ValueError, yaml
.YAMLError
) as exc
:
333 error_text
+= str(exc
)
335 bottle
.abort(HTTP_Bad_Request
, error_text
)
336 except js_e
.ValidationError
as exc
:
337 print "HTTP validate_in error, jsonschema exception ", exc
.message
, "at", exc
.path
338 print " CONTENT: " + str(bottle
.request
.body
.readlines())
340 if len(exc
.path
)>0: error_pos
=" at '" + ":".join(map(str, exc
.path
)) + "'"
341 bottle
.abort(HTTP_Bad_Request
, error_text
+ error_pos
+": "+exc
.message
)
343 # bottle.abort(HTTP_Bad_Request, "Content error: Failed to parse Content-Type", error_pos)
346 def filter_query_string(qs
, http2db
, allowed
):
347 '''Process query string (qs) checking that contains only valid tokens for avoiding SQL injection
349 'qs': bottle.FormsDict variable to be processed. None or empty is considered valid
350 'allowed': list of allowed string tokens (API http naming). All the keys of 'qs' must be one of 'allowed'
351 'http2db': dictionary with change from http API naming (dictionary key) to database naming(dictionary value)
352 Return: A tuple with the (select,where,limit) to be use in a database query. All of then transformed to the database naming
353 select: list of items to retrieve, filtered by query string 'field=token'. If no 'field' is present, allowed list is returned
354 where: dictionary with key, value, taken from the query string token=value. Empty if nothing is provided
355 limit: limit dictated by user with the query string 'limit'. 100 by default
356 abort if not permitted, using bottel.abort
361 if type(qs
) is not bottle
.FormsDict
:
362 print '!!!!!!!!!!!!!!invalid query string not a dictionary'
363 # bottle.abort(HTTP_Internal_Server_Error, "call programmer")
367 select
+= qs
.getall(k
)
370 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'field=" + v
+ "'")
375 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at 'limit=" + qs
[k
] + "'")
378 bottle
.abort(HTTP_Bad_Request
, "Invalid query string at '" + k
+ "=" + qs
[k
] + "'")
383 if len(select
) == 0: select
+= allowed
384 # change from http api to database naming
385 for i
in range(0, len(select
)):
388 select
[i
] = http2db
[k
]
389 change_keys_http2db(where
, http2db
)
390 # print "filter_query_string", select,where,limit
392 return select
, where
, limit
394 def convert_bandwidth(data
, reverse
=False):
395 '''Check the field bandwidth recursively and when found, it removes units and convert to number
396 It assumes that bandwidth is well formed
398 'data': dictionary bottle.FormsDict variable to be checked. None or empty is considered valid
399 'reverse': by default convert form str to int (Mbps), if True it convert from number to units
403 if type(data
) is dict:
404 for k
in data
.keys():
405 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
406 convert_bandwidth(data
[k
], reverse
)
407 if "bandwidth" in data
:
409 value
=str(data
["bandwidth"])
411 pos
= value
.find("bps")
413 if value
[pos
-1]=="G": data
["bandwidth"] = int(data
["bandwidth"][:pos
-1]) * 1000
414 elif value
[pos
-1]=="k": data
["bandwidth"]= int(data
["bandwidth"][:pos
-1]) / 1000
415 else: data
["bandwidth"]= int(data
["bandwidth"][:pos
-1])
417 value
= int(data
["bandwidth"])
418 if value
% 1000 == 0: data
["bandwidth"]=str(value
/1000) + " Gbps"
419 else: data
["bandwidth"]=str(value
) + " Mbps"
421 print "convert_bandwidth exception for type", type(data
["bandwidth"]), " data", data
["bandwidth"]
423 if type(data
) is tuple or type(data
) is list:
425 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
426 convert_bandwidth(k
, reverse
)
428 def convert_boolean(data
, items
): #TODO OVIM delete
429 '''Check recursively the content of data, and if there is an key contained in items, convert value from string to boolean
430 It assumes that bandwidth is well formed
432 'data': dictionary bottle.FormsDict variable to be checked. None or empty is consideted valid
433 'items': tuple of keys to convert
437 if type(data
) is dict:
438 for k
in data
.keys():
439 if type(data
[k
]) is dict or type(data
[k
]) is tuple or type(data
[k
]) is list:
440 convert_boolean(data
[k
], items
)
442 if type(data
[k
]) is str:
443 if data
[k
]=="false": data
[k
]=False
444 elif data
[k
]=="true": data
[k
]=True
445 if type(data
) is tuple or type(data
) is list:
447 if type(k
) is dict or type(k
) is tuple or type(k
) is list:
448 convert_boolean(k
, items
)
450 def convert_datetime2str(var
):
451 '''Converts a datetime variable to a string with the format '%Y-%m-%dT%H:%i:%s'
452 It enters recursively in the dict var finding this kind of variables
454 if type(var
) is dict:
455 for k
,v
in var
.items():
456 if type(v
) is datetime
.datetime
:
457 var
[k
]= v
.strftime('%Y-%m-%dT%H:%M:%S')
458 elif type(v
) is dict or type(v
) is list or type(v
) is tuple:
459 convert_datetime2str(v
)
460 if len(var
) == 0: return True
461 elif type(var
) is list or type(var
) is tuple:
463 convert_datetime2str(v
)
465 def check_valid_tenant(my
, tenant_id
):
468 return HTTP_Unauthorized
, "Needed admin privileges"
470 result
, _
= my
.db
.get_table(FROM
='tenants', SELECT
=('uuid',), WHERE
={'uuid': tenant_id
})
472 return HTTP_Not_Found
, "tenant '%s' not found" % tenant_id
477 Check if string value is a well-wormed url
478 :param url: string url
479 :return: True if is a valid url, False if is not well-formed
482 parsed_url
= urlparse
.urlparse(url
)
497 e
={"error":{"code":error
.status_code
, "type":error
.status
, "description":error
.body
}}
500 @bottle.hook('after_request')
502 #TODO: Alf: Is it needed??
503 bottle
.response
.headers
['Access-Control-Allow-Origin'] = '*'
509 @bottle.route(url_base
+ '/hosts', method
='GET')
510 def http_get_hosts():
511 return format_out(get_hosts())
515 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_host
,
516 ('id', 'name', 'description', 'status', 'admin_state_up', 'ip_name'))
518 myself
= config_dic
['http_threads'][ threading
.current_thread().name
]
519 result
, content
= myself
.db
.get_table(FROM
='hosts', SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
521 print "http_get_hosts Error", content
522 bottle
.abort(-result
, content
)
524 convert_boolean(content
, ('admin_state_up',) )
525 change_keys_http2db(content
, http2db_host
, reverse
=True)
527 row
['links'] = ( {'href': myself
.url_preffix
+ '/hosts/' + str(row
['id']), 'rel': 'bookmark'}, )
528 data
={'hosts' : content
}
531 @bottle.route(url_base
+ '/hosts/<host_id>', method
='GET')
532 def http_get_host_id(host_id
):
533 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
534 return my
.gethost(host_id
)
536 @bottle.route(url_base
+ '/hosts', method
='POST')
537 def http_post_hosts():
538 '''insert a host into the database. All resources are got and inserted'''
539 global RADclass_module
540 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
543 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
546 http_content
= format_in( host_new_schema
)
547 r
= remove_extra_items(http_content
, host_new_schema
)
548 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
549 change_keys_http2db(http_content
['host'], http2db_host
)
551 if 'host' in http_content
:
552 host
= http_content
['host']
553 if 'host-data' in http_content
:
554 host
.update(http_content
['host-data'])
556 host
= http_content
['host-data']
558 ip_name
= host
['ip_name']
560 password
= host
.get('password')
561 if host
.get('autodiscover'):
562 if not RADclass_module
:
564 RADclass_module
= imp
.find_module("RADclass")
565 except (IOError, ImportError) as e
:
566 raise ImportError("Cannot import RADclass.py Openvim not properly installed" +str(e
))
569 rad
= RADclass_module
.RADclass()
570 (return_status
, code
) = rad
.obtain_RAD(user
, password
, ip_name
)
573 if not return_status
:
574 print 'http_post_hosts ERROR obtaining RAD', code
575 bottle
.abort(HTTP_Bad_Request
, code
)
578 rad_structure
= yaml
.load(rad
.to_text())
579 print 'rad_structure\n---------------------'
580 print json
.dumps(rad_structure
, indent
=4)
581 print '---------------------'
583 WHERE_
={"family":rad_structure
['processor']['family'], 'manufacturer':rad_structure
['processor']['manufacturer'], 'version':rad_structure
['processor']['version']}
584 result
, content
= my
.db
.get_table(FROM
='host_ranking',
588 host
['ranking'] = content
[0]['ranking']
590 #error_text= "Host " + str(WHERE_)+ " not found in ranking table. Not valid for VIM management"
591 #bottle.abort(HTTP_Bad_Request, error_text)
593 warning_text
+= "Host " + str(WHERE_
)+ " not found in ranking table. Assuming lowest value 100\n"
594 host
['ranking'] = 100 #TODO: as not used in this version, set the lowest value
596 features
= rad_structure
['processor'].get('features', ())
597 host
['features'] = ",".join(features
)
600 for node
in (rad_structure
['resource topology']['nodes'] or {}).itervalues():
605 for core
in node
['cpu']['eligible_cores']:
606 eligible_cores
.extend(core
)
607 for core
in node
['cpu']['cores']:
608 for thread_id
in core
:
609 c
={'core_id': count
, 'thread_id': thread_id
}
610 if thread_id
not in eligible_cores
: c
['status'] = 'noteligible'
615 for port_k
, port_v
in node
['nics']['nic 0']['ports'].iteritems():
616 if port_v
['virtual']:
620 for port_k2
, port_v2
in node
['nics']['nic 0']['ports'].iteritems():
621 if port_v2
['virtual'] and port_v2
['PF_pci_id']==port_k
:
622 sriovs
.append({'pci':port_k2
, 'mac':port_v2
['mac'], 'source_name':port_v2
['source_name']})
624 #sort sriov according to pci and rename them to the vf number
625 new_sriovs
= sorted(sriovs
, key
=lambda k
: k
['pci'])
627 for sriov
in new_sriovs
:
628 sriov
['source_name'] = index
630 interfaces
.append ({'pci':str(port_k
), 'Mbps': port_v
['speed']/1000000, 'sriovs': new_sriovs
, 'mac':port_v
['mac'], 'source_name':port_v
['source_name']})
631 memory
=node
['memory']['node_size'] / (1024*1024*1024)
632 #memory=get_next_2pow(node['memory']['hugepage_nr'])
633 host
['numas'].append( {'numa_socket': node
['id'], 'hugepages': node
['memory']['hugepage_nr'], 'memory':memory
, 'interfaces': interfaces
, 'cores': cores
} )
634 # print json.dumps(host, indent=4)
635 # insert in data base
636 if "created_at" in host
:
637 del host
["created_at"]
638 for numa
in host
.get("numas", ()):
639 if "hugepages_consumed" in numa
:
640 del numa
["hugepages_consumed"]
641 for core
in numa
.get("cores", ()):
642 if "instance_id" in core
:
643 del core
["instance_id"]
644 if "v_thread_id" in core
:
645 del core
["v_thread_id"]
646 result
, content
= my
.db
.new_host(host
)
648 if content
['admin_state_up']:
650 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
651 host_develop_mode
= True if config_dic
['mode']=='development' else False
652 host_develop_bridge_iface
= config_dic
.get('development_bridge', None)
653 thread
= ht
.host_thread(name
=host
.get('name',ip_name
), user
=user
, host
=ip_name
,
654 password
=host
.get('password'),
655 keyfile
=host
.get('keyfile', config_dic
["host_ssh_keyfile"]),
656 db
=config_dic
['db'], db_lock
=config_dic
['db_lock'],
657 test
=host_test_mode
, image_path
=config_dic
['host_image_path'],
658 version
=config_dic
['version'], host_id
=content
['uuid'],
659 develop_mode
=host_develop_mode
, develop_bridge_iface
=host_develop_bridge_iface
)
662 config_dic
['host_threads'][content
['uuid']] = thread
664 if config_dic
['network_type'] == 'ovs':
666 create_dhcp_ovs_bridge()
667 config_dic
['host_threads'][content
['uuid']].insert_task("new-ovsbridge")
668 # create vlxan bwt OVS controller and computes
669 create_vxlan_mesh(content
['uuid'], my
.logger
)
672 change_keys_http2db(content
, http2db_host
, reverse
=True)
673 if len(warning_text
)>0:
674 content
["warning"]= warning_text
675 data
={'host' : content
}
676 return format_out(data
)
678 bottle
.abort(HTTP_Bad_Request
, content
)
682 def delete_dhcp_ovs_bridge(vlan
, net_uuid
):
684 Delete bridges and port created during dhcp launching at openvim controller
685 :param vlan: net vlan id
686 :param net_uuid: network identifier
689 dhcp_path
= config_dic
['ovs_controller_file_path']
691 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
692 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
694 dhcp_controller
.delete_dhcp_server(vlan
, net_uuid
, dhcp_path
)
695 dhcp_controller
.delete_dhcp_port(vlan
, net_uuid
, dhcp_path
)
698 def create_dhcp_ovs_bridge():
700 Initialize bridge to allocate the dhcp server at openvim controller
703 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
704 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
706 dhcp_controller
.create_ovs_bridge()
709 def set_mac_dhcp(vm_ip
, vlan
, first_ip
, last_ip
, cidr
, mac
):
711 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
712 :param vm_ip: IP address asigned to a VM
713 :param vlan: Segmentation id
714 :param first_ip: First dhcp range ip
715 :param last_ip: Last dhcp range ip
716 :param cidr: net cidr
717 :param mac: VM vnic mac to be macthed with the IP received
721 ip_tools
= IPNetwork(cidr
)
722 cidr_len
= ip_tools
.prefixlen
723 dhcp_netmask
= str(ip_tools
.netmask
)
724 dhcp_path
= config_dic
['ovs_controller_file_path']
726 new_cidr
= [first_ip
+ '/' + str(cidr_len
)]
727 if not len(all_matching_cidrs(vm_ip
, new_cidr
)):
730 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
731 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
733 dhcp_controller
.set_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_netmask
, first_ip
, dhcp_path
)
736 def delete_mac_dhcp(vm_ip
, vlan
, mac
):
738 Delete into dhcp conf file the ip assigned to a specific MAC address
739 :param vm_ip: IP address asigned to a VM
740 :param vlan: Segmentation id
741 :param mac: VM vnic mac to be macthed with the IP received
745 dhcp_path
= config_dic
['ovs_controller_file_path']
747 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
748 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
750 dhcp_controller
.delete_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_path
)
753 def create_vxlan_mesh(host_id
, logger
=None):
755 Create vxlan mesh across all openvimc controller and computes.
756 :param host_id: Added compute node id. Anyway vlan is created by all compute nodes
757 :param logger: To log errors
760 dhcp_compute_name
= get_vxlan_interface("dhcp")
761 existing_hosts
= get_hosts()
762 if len(existing_hosts
['hosts']) > 0:
763 # vlxan mesh creation between openvim controller and computes
764 computes_available
= existing_hosts
['hosts']
766 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
767 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
769 for compute
in computes_available
:
771 if compute
['ip_name'] != 'localhost':
772 remote_ip
= socket
.gethostbyname(compute
['ip_name'])
774 remote_ip
= 'localhost'
775 except socket
.error
as e
:
777 logger
.error("Cannot get compute node remote ip from '{}'. Skipping: {}".format(
778 compute
['ip_name'], e
))
780 # vxlan ovs_controller <=> compute node
781 vxlan_interface_name
= get_vxlan_interface(compute
['id'][:8])
782 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan", dhcp_compute_name
, dhcp_controller
.host
)
783 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, remote_ip
)
784 # vxlan from others compute node to cthis ompute node
785 for compute_src
in computes_available
:
786 if compute_src
['id'] == compute
['id']:
788 config_dic
['host_threads'][compute_src
['id']].insert_task("new-vxlan",
789 vxlan_interface_name
,
792 def delete_vxlan_mesh(host_id
):
794 Create a task for remove a specific compute of the vlxan mesh
795 :param host_id: host id to be deleted.
797 existing_hosts
= get_hosts()
798 computes_available
= existing_hosts
['hosts']
800 vxlan_interface_name
= get_vxlan_interface(host_id
[:8])
802 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
803 dhcp_host
= http_controller
.ovim
.get_dhcp_controller()
805 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
806 # remove bridge from openvim controller if no more computes exist
807 if len(existing_hosts
):
808 dhcp_host
.delete_ovs_bridge()
810 for compute
in computes_available
:
811 if host_id
== compute
['id']:
814 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
815 config_dic
['host_threads'][compute
['id']].insert_task("del-vxlan", vxlan_interface_name
)
818 def get_vxlan_interface(local_uuid
):
820 Genearte a vxlan interface name
821 :param local_uuid: host id
822 :return: vlxan-8digits
824 return 'vxlan-' + local_uuid
[:8]
827 @bottle.route(url_base
+ '/hosts/<host_id>', method
='PUT')
828 def http_put_host_id(host_id
):
829 '''modify a host into the database. All resources are got and inserted'''
830 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
833 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
836 http_content
= format_in( host_edit_schema
)
837 r
= remove_extra_items(http_content
, host_edit_schema
)
838 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
839 change_keys_http2db(http_content
['host'], http2db_host
)
842 result
, content
= my
.db
.edit_host(host_id
, http_content
['host'])
844 convert_boolean(content
, ('admin_state_up',) )
845 change_keys_http2db(content
, http2db_host
, reverse
=True)
846 data
={'host' : content
}
848 if config_dic
['network_type'] == 'ovs':
849 delete_vxlan_mesh(host_id
)
850 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
853 config_dic
['host_threads'][host_id
].name
= content
.get('name',content
['ip_name'])
854 config_dic
['host_threads'][host_id
].user
= content
['user']
855 config_dic
['host_threads'][host_id
].host
= content
['ip_name']
856 config_dic
['host_threads'][host_id
].insert_task("reload")
858 if config_dic
['network_type'] == 'ovs':
859 # create mesh with new host data
860 config_dic
['host_threads'][host_id
].insert_task("new-ovsbridge")
861 create_vxlan_mesh(host_id
, my
.logger
)
864 return format_out(data
)
866 bottle
.abort(HTTP_Bad_Request
, content
)
871 @bottle.route(url_base
+ '/hosts/<host_id>', method
='DELETE')
872 def http_delete_host_id(host_id
):
873 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
876 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
877 result
, content
= my
.db
.delete_row('hosts', host_id
)
879 bottle
.abort(HTTP_Not_Found
, content
)
881 if config_dic
['network_type'] == 'ovs':
882 delete_vxlan_mesh(host_id
)
884 if host_id
in config_dic
['host_threads']:
885 if config_dic
['network_type'] == 'ovs':
886 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
887 config_dic
['host_threads'][host_id
].insert_task("exit")
889 data
={'result' : content
}
890 return format_out(data
)
892 print "http_delete_host_id error",result
, content
893 bottle
.abort(-result
, content
)
900 @bottle.route(url_base
+ '/tenants', method
='GET')
901 def http_get_tenants():
903 Retreive tenant list from DB
906 my
= config_dic
['http_threads'][threading
.current_thread().name
]
909 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_tenant
,
910 ('id', 'name', 'description', 'enabled'))
911 tenants
= my
.ovim
.get_tenants(select_
, where_
)
912 delete_nulls(tenants
)
913 change_keys_http2db(tenants
, http2db_tenant
, reverse
=True)
914 data
= {'tenants': tenants
}
915 return format_out(data
)
916 except ovim
.ovimException
as e
:
917 my
.logger
.error(str(e
), exc_info
=True)
918 bottle
.abort(e
.http_code
, str(e
))
919 except Exception as e
:
920 my
.logger
.error(str(e
), exc_info
=True)
921 bottle
.abort(HTTP_Bad_Request
, str(e
))
924 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
925 def http_get_tenant_id(tenant_id
):
927 Get tenant from DB by id
928 :param tenant_id: tenant id
931 my
= config_dic
['http_threads'][threading
.current_thread().name
]
934 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
936 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
937 data
= {'tenant': tenant
}
938 return format_out(data
)
939 except ovim
.ovimException
as e
:
940 my
.logger
.error(str(e
), exc_info
=True)
941 bottle
.abort(e
.http_code
, str(e
))
942 except Exception as e
:
943 my
.logger
.error(str(e
), exc_info
=True)
944 bottle
.abort(HTTP_Bad_Request
, str(e
))
947 @bottle.route(url_base
+ '/tenants', method
='POST')
948 def http_post_tenants():
950 Insert a tenant into the database.
953 my
= config_dic
['http_threads'][threading
.current_thread().name
]
956 http_content
= format_in(tenant_new_schema
)
957 r
= remove_extra_items(http_content
, tenant_new_schema
)
959 my
.logger
.error("http_post_tenants: Warning: remove extra items " + str(r
), exc_info
=True)
960 # insert in data base
961 tenant_id
= my
.ovim
.new_tentant(http_content
['tenant'])
962 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
963 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
='PUT')
976 def http_put_tenant_id(tenant_id
):
978 Update a tenantinto DB.
979 :param tenant_id: tentant id
983 my
= config_dic
['http_threads'][threading
.current_thread().name
]
986 http_content
= format_in(tenant_edit_schema
)
987 r
= remove_extra_items(http_content
, tenant_edit_schema
)
989 print "http_put_tenant_id: Warning: remove extra items ", r
990 change_keys_http2db(http_content
['tenant'], http2db_tenant
)
991 # insert in data base
992 my
.ovim
.edit_tenant(tenant_id
, http_content
['tenant'])
993 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
994 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
995 data
= {'tenant': tenant
}
996 return format_out(data
)
997 except ovim
.ovimException
as e
:
998 my
.logger
.error(str(e
), exc_info
=True)
999 bottle
.abort(e
.http_code
, str(e
))
1000 except Exception as e
:
1001 my
.logger
.error(str(e
), exc_info
=True)
1002 bottle
.abort(HTTP_Bad_Request
, str(e
))
1005 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
1006 def http_delete_tenant_id(tenant_id
):
1008 Delete a tenant from the database.
1009 :param tenant_id: tenant id
1012 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1015 content
= my
.ovim
.delete_tentant(tenant_id
)
1016 data
= {'result': content
}
1017 return format_out(data
)
1018 except ovim
.ovimException
as e
:
1019 my
.logger
.error(str(e
), exc_info
=True)
1020 bottle
.abort(e
.http_code
, str(e
))
1021 except Exception as e
:
1022 my
.logger
.error(str(e
), exc_info
=True)
1023 bottle
.abort(HTTP_Bad_Request
, str(e
))
1029 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='GET')
1030 def http_get_flavors(tenant_id
):
1031 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1032 #check valid tenant_id
1033 result
,content
= check_valid_tenant(my
, tenant_id
)
1035 bottle
.abort(result
, content
)
1037 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1038 ('id','name','description','public') )
1039 if tenant_id
=='any':
1042 from_
='tenants_flavors inner join flavors on tenants_flavors.flavor_id=flavors.uuid'
1043 where_
['tenant_id'] = tenant_id
1044 result
, content
= my
.db
.get_table(FROM
=from_
, SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1046 print "http_get_flavors Error", content
1047 bottle
.abort(-result
, content
)
1049 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1051 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(row
['id']) ) ), 'rel':'bookmark' } ]
1052 data
={'flavors' : content
}
1053 return format_out(data
)
1055 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='GET')
1056 def http_get_flavor_id(tenant_id
, flavor_id
):
1057 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1058 #check valid tenant_id
1059 result
,content
= check_valid_tenant(my
, tenant_id
)
1061 bottle
.abort(result
, content
)
1063 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1064 ('id','name','description','ram', 'vcpus', 'extended', 'disk', 'public') )
1065 if tenant_id
=='any':
1068 from_
='tenants_flavors as tf inner join flavors as f on tf.flavor_id=f.uuid'
1069 where_
['tenant_id'] = tenant_id
1070 where_
['uuid'] = flavor_id
1071 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
=from_
, WHERE
=where_
, LIMIT
=limit_
)
1074 print "http_get_flavor_id error %d %s" % (result
, content
)
1075 bottle
.abort(-result
, content
)
1077 print "http_get_flavors_id flavor '%s' not found" % str(flavor_id
)
1078 bottle
.abort(HTTP_Not_Found
, 'flavor %s not found' % flavor_id
)
1080 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1081 if 'extended' in content
[0] and content
[0]['extended'] is not None:
1082 extended
= json
.loads(content
[0]['extended'])
1083 if 'devices' in extended
:
1084 change_keys_http2db(extended
['devices'], http2db_flavor
, reverse
=True)
1085 content
[0]['extended']=extended
1086 convert_bandwidth(content
[0], reverse
=True)
1087 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1088 data
={'flavor' : content
[0]}
1089 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1090 return format_out(data
)
1093 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='POST')
1094 def http_post_flavors(tenant_id
):
1095 '''insert a flavor into the database, and attach to tenant.'''
1096 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1097 #check valid tenant_id
1098 result
,content
= check_valid_tenant(my
, tenant_id
)
1100 bottle
.abort(result
, content
)
1101 http_content
= format_in( flavor_new_schema
)
1102 r
= remove_extra_items(http_content
, flavor_new_schema
)
1103 if r
is not None: print "http_post_flavors: Warning: remove extra items ", r
1104 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1105 extended_dict
= http_content
['flavor'].pop('extended', None)
1106 if extended_dict
is not None:
1107 result
, content
= check_extended(extended_dict
)
1109 print "http_post_flavors wrong input extended error %d %s" % (result
, content
)
1110 bottle
.abort(-result
, content
)
1112 convert_bandwidth(extended_dict
)
1113 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1114 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1115 #insert in data base
1116 result
, content
= my
.db
.new_flavor(http_content
['flavor'], tenant_id
)
1118 return http_get_flavor_id(tenant_id
, content
)
1120 print "http_psot_flavors error %d %s" % (result
, content
)
1121 bottle
.abort(-result
, content
)
1124 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='DELETE')
1125 def http_delete_flavor_id(tenant_id
, flavor_id
):
1126 '''Deletes the flavor_id of a tenant. IT removes from tenants_flavors table.'''
1127 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1128 #check valid tenant_id
1129 result
,content
= check_valid_tenant(my
, tenant_id
)
1131 bottle
.abort(result
, content
)
1133 result
, content
= my
.db
.delete_image_flavor('flavor', flavor_id
, tenant_id
)
1135 bottle
.abort(HTTP_Not_Found
, content
)
1137 data
={'result' : content
}
1138 return format_out(data
)
1140 print "http_delete_flavor_id error",result
, content
1141 bottle
.abort(-result
, content
)
1144 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>/<action>', method
='POST')
1145 def http_attach_detach_flavors(tenant_id
, flavor_id
, action
):
1146 '''attach/detach an existing flavor in this tenant. That is insert/remove at tenants_flavors table.'''
1147 #TODO alf: not tested at all!!!
1148 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1149 #check valid tenant_id
1150 result
,content
= check_valid_tenant(my
, tenant_id
)
1152 bottle
.abort(result
, content
)
1153 if tenant_id
=='any':
1154 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1156 if action
!='attach' and action
!= 'detach':
1157 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1160 #Ensure that flavor exist
1161 from_
='tenants_flavors as tf right join flavors as f on tf.flavor_id=f.uuid'
1162 where_
={'uuid': flavor_id
}
1163 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1165 if action
=='attach':
1166 text_error
="Flavor '%s' not found" % flavor_id
1168 text_error
="Flavor '%s' not found for tenant '%s'" % (flavor_id
, tenant_id
)
1169 bottle
.abort(HTTP_Not_Found
, text_error
)
1173 if action
=='attach':
1174 if flavor
['tenant_id']!=None:
1175 bottle
.abort(HTTP_Conflict
, "Flavor '%s' already attached to tenant '%s'" % (flavor_id
, tenant_id
))
1176 if flavor
['public']=='no' and not my
.admin
:
1177 #allow only attaching public flavors
1178 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private flavor")
1180 #insert in data base
1181 result
, content
= my
.db
.new_row('tenants_flavors', {'flavor_id':flavor_id
, 'tenant_id': tenant_id
})
1183 return http_get_flavor_id(tenant_id
, flavor_id
)
1185 if flavor
['tenant_id']==None:
1186 bottle
.abort(HTTP_Not_Found
, "Flavor '%s' not attached to tenant '%s'" % (flavor_id
, tenant_id
))
1187 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_flavors', WHERE
={'flavor_id':flavor_id
, 'tenant_id':tenant_id
})
1189 if flavor
['public']=='no':
1190 #try to delete the flavor completely to avoid orphan flavors, IGNORE error
1191 my
.db
.delete_row_by_dict(FROM
='flavors', WHERE
={'uuid':flavor_id
})
1192 data
={'result' : "flavor detached"}
1193 return format_out(data
)
1195 #if get here is because an error
1196 print "http_attach_detach_flavors error %d %s" % (result
, content
)
1197 bottle
.abort(-result
, content
)
1200 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='PUT')
1201 def http_put_flavor_id(tenant_id
, flavor_id
):
1202 '''update a flavor_id into the database.'''
1203 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1204 #check valid tenant_id
1205 result
,content
= check_valid_tenant(my
, tenant_id
)
1207 bottle
.abort(result
, content
)
1209 http_content
= format_in( flavor_update_schema
)
1210 r
= remove_extra_items(http_content
, flavor_update_schema
)
1211 if r
is not None: print "http_put_flavor_id: Warning: remove extra items ", r
1212 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1213 extended_dict
= http_content
['flavor'].pop('extended', None)
1214 if extended_dict
is not None:
1215 result
, content
= check_extended(extended_dict
)
1217 print "http_put_flavor_id wrong input extended error %d %s" % (result
, content
)
1218 bottle
.abort(-result
, content
)
1220 convert_bandwidth(extended_dict
)
1221 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1222 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1223 #Ensure that flavor exist
1224 where_
={'uuid': flavor_id
}
1225 if tenant_id
=='any':
1228 from_
='tenants_flavors as ti inner join flavors as i on ti.flavor_id=i.uuid'
1229 where_
['tenant_id'] = tenant_id
1230 result
, content
= my
.db
.get_table(SELECT
=('public',), FROM
=from_
, WHERE
=where_
)
1232 text_error
="Flavor '%s' not found" % flavor_id
1233 if tenant_id
!='any':
1234 text_error
+=" for tenant '%s'" % flavor_id
1235 bottle
.abort(HTTP_Not_Found
, text_error
)
1238 if content
[0]['public']=='yes' and not my
.admin
:
1239 #allow only modifications over private flavors
1240 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public flavor")
1242 #insert in data base
1243 result
, content
= my
.db
.update_rows('flavors', http_content
['flavor'], {'uuid': flavor_id
})
1246 print "http_put_flavor_id error %d %s" % (result
, content
)
1247 bottle
.abort(-result
, content
)
1250 return http_get_flavor_id(tenant_id
, flavor_id
)
1258 @bottle.route(url_base
+ '/<tenant_id>/images', method
='GET')
1259 def http_get_images(tenant_id
):
1260 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1261 #check valid tenant_id
1262 result
,content
= check_valid_tenant(my
, tenant_id
)
1264 bottle
.abort(result
, content
)
1266 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1267 ('id','name','checksum','description','path','public') )
1268 if tenant_id
=='any':
1272 from_
='tenants_images right join images on tenants_images.image_id=images.uuid'
1273 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1274 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1276 print "http_get_images Error", content
1277 bottle
.abort(-result
, content
)
1279 change_keys_http2db(content
, http2db_image
, reverse
=True)
1280 #for row in content: row['links']=[ {'href': "/".join( (my.url_preffix, tenant_id, 'images', str(row['id']) ) ), 'rel':'bookmark' } ]
1281 data
={'images' : content
}
1282 return format_out(data
)
1284 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='GET')
1285 def http_get_image_id(tenant_id
, image_id
):
1286 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1287 #check valid tenant_id
1288 result
,content
= check_valid_tenant(my
, tenant_id
)
1290 bottle
.abort(result
, content
)
1292 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1293 ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
1294 if tenant_id
=='any':
1298 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1299 where_or_
= {'tenant_id': tenant_id
, 'public': "yes"}
1300 where_
['uuid'] = image_id
1301 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1304 print "http_get_images error %d %s" % (result
, content
)
1305 bottle
.abort(-result
, content
)
1307 print "http_get_images image '%s' not found" % str(image_id
)
1308 bottle
.abort(HTTP_Not_Found
, 'image %s not found' % image_id
)
1310 convert_datetime2str(content
)
1311 change_keys_http2db(content
, http2db_image
, reverse
=True)
1312 if 'metadata' in content
[0] and content
[0]['metadata'] is not None:
1313 metadata
= json
.loads(content
[0]['metadata'])
1314 content
[0]['metadata']=metadata
1315 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'images', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1316 data
={'image' : content
[0]}
1317 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1318 return format_out(data
)
1320 @bottle.route(url_base
+ '/<tenant_id>/images', method
='POST')
1321 def http_post_images(tenant_id
):
1322 '''insert a image into the database, and attach to tenant.'''
1323 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1324 #check valid tenant_id
1325 result
,content
= check_valid_tenant(my
, tenant_id
)
1327 bottle
.abort(result
, content
)
1328 http_content
= format_in(image_new_schema
)
1329 r
= remove_extra_items(http_content
, image_new_schema
)
1330 if r
is not None: print "http_post_images: Warning: remove extra items ", r
1331 change_keys_http2db(http_content
['image'], http2db_image
)
1332 metadata_dict
= http_content
['image'].pop('metadata', None)
1333 if metadata_dict
is not None:
1334 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1337 image_file
= http_content
['image'].get('path',None)
1338 parsed_url
= urlparse
.urlparse(image_file
)
1339 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "":
1340 # The path is a local file
1341 if os
.path
.exists(image_file
):
1342 http_content
['image']['checksum'] = md5(image_file
)
1344 # The path is a URL. Code should be added to download the image and calculate the checksum
1345 #http_content['image']['checksum'] = md5(downloaded_image)
1347 # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
1348 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
1350 if 'checksum' not in http_content
['image']:
1351 http_content
['image']['checksum'] = md5_string(image_file
)
1353 # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
1354 # If it is a URL, no error is sent. Checksum will be an empty string
1355 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "" and 'checksum' not in http_content
['image']:
1356 content
= "Image file not found"
1357 print "http_post_images error: %d %s" % (HTTP_Bad_Request
, content
)
1358 bottle
.abort(HTTP_Bad_Request
, content
)
1359 except Exception as e
:
1360 print "ERROR. Unexpected exception: %s" % (str(e
))
1361 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1362 #insert in data base
1363 result
, content
= my
.db
.new_image(http_content
['image'], tenant_id
)
1365 return http_get_image_id(tenant_id
, content
)
1367 print "http_post_images error %d %s" % (result
, content
)
1368 bottle
.abort(-result
, content
)
1371 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='DELETE')
1372 def http_delete_image_id(tenant_id
, image_id
):
1373 '''Deletes the image_id of a tenant. IT removes from tenants_images table.'''
1374 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1375 #check valid tenant_id
1376 result
,content
= check_valid_tenant(my
, tenant_id
)
1378 bottle
.abort(result
, content
)
1379 result
, content
= my
.db
.delete_image_flavor('image', image_id
, tenant_id
)
1381 bottle
.abort(HTTP_Not_Found
, content
)
1383 data
={'result' : content
}
1384 return format_out(data
)
1386 print "http_delete_image_id error",result
, content
1387 bottle
.abort(-result
, content
)
1390 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>/<action>', method
='POST')
1391 def http_attach_detach_images(tenant_id
, image_id
, action
):
1392 '''attach/detach an existing image in this tenant. That is insert/remove at tenants_images table.'''
1393 #TODO alf: not tested at all!!!
1394 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1395 #check valid tenant_id
1396 result
,content
= check_valid_tenant(my
, tenant_id
)
1398 bottle
.abort(result
, content
)
1399 if tenant_id
=='any':
1400 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1402 if action
!='attach' and action
!= 'detach':
1403 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1406 #Ensure that image exist
1407 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1408 where_
={'uuid': image_id
}
1409 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1411 if action
=='attach':
1412 text_error
="Image '%s' not found" % image_id
1414 text_error
="Image '%s' not found for tenant '%s'" % (image_id
, tenant_id
)
1415 bottle
.abort(HTTP_Not_Found
, text_error
)
1419 if action
=='attach':
1420 if image
['tenant_id']!=None:
1421 bottle
.abort(HTTP_Conflict
, "Image '%s' already attached to tenant '%s'" % (image_id
, tenant_id
))
1422 if image
['public']=='no' and not my
.admin
:
1423 #allow only attaching public images
1424 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private image")
1426 #insert in data base
1427 result
, content
= my
.db
.new_row('tenants_images', {'image_id':image_id
, 'tenant_id': tenant_id
})
1429 return http_get_image_id(tenant_id
, image_id
)
1431 if image
['tenant_id']==None:
1432 bottle
.abort(HTTP_Not_Found
, "Image '%s' not attached to tenant '%s'" % (image_id
, tenant_id
))
1433 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_images', WHERE
={'image_id':image_id
, 'tenant_id':tenant_id
})
1435 if image
['public']=='no':
1436 #try to delete the image completely to avoid orphan images, IGNORE error
1437 my
.db
.delete_row_by_dict(FROM
='images', WHERE
={'uuid':image_id
})
1438 data
={'result' : "image detached"}
1439 return format_out(data
)
1441 #if get here is because an error
1442 print "http_attach_detach_images error %d %s" % (result
, content
)
1443 bottle
.abort(-result
, content
)
1446 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='PUT')
1447 def http_put_image_id(tenant_id
, image_id
):
1448 '''update a image_id into the database.'''
1449 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1450 #check valid tenant_id
1451 result
,content
= check_valid_tenant(my
, tenant_id
)
1453 bottle
.abort(result
, content
)
1455 http_content
= format_in( image_update_schema
)
1456 r
= remove_extra_items(http_content
, image_update_schema
)
1457 if r
is not None: print "http_put_image_id: Warning: remove extra items ", r
1458 change_keys_http2db(http_content
['image'], http2db_image
)
1459 metadata_dict
= http_content
['image'].pop('metadata', None)
1460 if metadata_dict
is not None:
1461 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1462 #Ensure that image exist
1463 where_
={'uuid': image_id
}
1464 if tenant_id
=='any':
1468 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1469 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1470 result
, content
= my
.db
.get_table(SELECT
=('public',), DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND")
1472 text_error
="Image '%s' not found" % image_id
1473 if tenant_id
!='any':
1474 text_error
+=" for tenant '%s'" % image_id
1475 bottle
.abort(HTTP_Not_Found
, text_error
)
1478 if content
[0]['public']=='yes' and not my
.admin
:
1479 #allow only modifications over private images
1480 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public image")
1482 #insert in data base
1483 result
, content
= my
.db
.update_rows('images', http_content
['image'], {'uuid': image_id
})
1486 print "http_put_image_id error %d %s" % (result
, content
)
1487 bottle
.abort(-result
, content
)
1490 return http_get_image_id(tenant_id
, image_id
)
1497 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='GET')
1498 def http_get_servers(tenant_id
):
1499 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1500 result
,content
= check_valid_tenant(my
, tenant_id
)
1502 bottle
.abort(result
, content
)
1505 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_server
,
1506 ('id','name','description','hostId','imageRef','flavorRef','status', 'tenant_id') )
1507 if tenant_id
!='any':
1508 where_
['tenant_id'] = tenant_id
1509 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
='instances', WHERE
=where_
, LIMIT
=limit_
)
1511 print "http_get_servers Error", content
1512 bottle
.abort(-result
, content
)
1514 change_keys_http2db(content
, http2db_server
, reverse
=True)
1516 tenant_id
= row
.pop('tenant_id')
1517 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'servers', str(row
['id']) ) ), 'rel':'bookmark' } ]
1518 data
={'servers' : content
}
1519 return format_out(data
)
1521 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='GET')
1522 def http_get_server_id(tenant_id
, server_id
):
1523 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1524 #check valid tenant_id
1525 result
,content
= check_valid_tenant(my
, tenant_id
)
1527 bottle
.abort(result
, content
)
1530 result
, content
= my
.db
.get_instance(server_id
)
1532 bottle
.abort(HTTP_Not_Found
, content
)
1534 #change image/flavor-id to id and link
1535 convert_bandwidth(content
, reverse
=True)
1536 convert_datetime2str(content
)
1537 if content
["ram"]==0 : del content
["ram"]
1538 if content
["vcpus"]==0 : del content
["vcpus"]
1539 if 'flavor_id' in content
:
1540 if content
['flavor_id'] is not None:
1541 content
['flavor'] = {'id':content
['flavor_id'],
1542 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'flavors', str(content
['flavor_id']) ) ), 'rel':'bookmark'}]
1544 del content
['flavor_id']
1545 if 'image_id' in content
:
1546 if content
['image_id'] is not None:
1547 content
['image'] = {'id':content
['image_id'],
1548 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'images', str(content
['image_id']) ) ), 'rel':'bookmark'}]
1550 del content
['image_id']
1551 change_keys_http2db(content
, http2db_server
, reverse
=True)
1552 if 'extended' in content
:
1553 if 'devices' in content
['extended']: change_keys_http2db(content
['extended']['devices'], http2db_server
, reverse
=True)
1555 data
={'server' : content
}
1556 return format_out(data
)
1558 bottle
.abort(-result
, content
)
1561 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='POST')
1562 def http_post_server_id(tenant_id
):
1563 '''deploys a new server'''
1564 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1565 #check valid tenant_id
1566 result
,content
= check_valid_tenant(my
, tenant_id
)
1568 bottle
.abort(result
, content
)
1570 if tenant_id
=='any':
1571 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1573 http_content
= format_in( server_new_schema
)
1574 r
= remove_extra_items(http_content
, server_new_schema
)
1575 if r
is not None: print "http_post_serves: Warning: remove extra items ", r
1576 change_keys_http2db(http_content
['server'], http2db_server
)
1577 extended_dict
= http_content
['server'].get('extended', None)
1578 if extended_dict
is not None:
1579 result
, content
= check_extended(extended_dict
, True)
1581 print "http_post_servers wrong input extended error %d %s" % (result
, content
)
1582 bottle
.abort(-result
, content
)
1584 convert_bandwidth(extended_dict
)
1585 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_server
)
1587 server
= http_content
['server']
1588 server_start
= server
.get('start', 'yes')
1589 server
['tenant_id'] = tenant_id
1590 #check flavor valid and take info
1591 result
, content
= my
.db
.get_table(FROM
='tenants_flavors as tf join flavors as f on tf.flavor_id=f.uuid',
1592 SELECT
=('ram','vcpus','extended'), WHERE
={'uuid':server
['flavor_id'], 'tenant_id':tenant_id
})
1594 bottle
.abort(HTTP_Not_Found
, 'flavor_id %s not found' % server
['flavor_id'])
1596 server
['flavor']=content
[0]
1597 #check image valid and take info
1598 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1599 SELECT
=('path', 'metadata', 'image_id'),
1600 WHERE
={'uuid':server
['image_id'], "status":"ACTIVE"},
1601 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'},
1605 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % server
['image_id'])
1607 for image_dict
in content
:
1608 if image_dict
.get("image_id"):
1611 # insert in data base tenants_images
1612 r2
, c2
= my
.db
.new_row('tenants_images', {'image_id': server
['image_id'], 'tenant_id': tenant_id
})
1614 bottle
.abort(HTTP_Not_Found
, 'image_id %s cannot be used. Error %s' % (server
['image_id'], c2
))
1616 server
['image']={"path": content
[0]["path"], "metadata": content
[0]["metadata"]}
1617 if "hosts_id" in server
:
1618 result
, content
= my
.db
.get_table(FROM
='hosts', SELECT
=('uuid',), WHERE
={'uuid': server
['host_id']})
1620 bottle
.abort(HTTP_Not_Found
, 'hostId %s not found' % server
['host_id'])
1622 #print json.dumps(server, indent=4)
1624 result
, content
= ht
.create_server(server
, config_dic
['db'], config_dic
['db_lock'], config_dic
['mode']=='normal')
1627 #Insert instance to database
1630 print "inserting at DB"
1632 if server_start
== 'no':
1633 content
['status'] = 'INACTIVE'
1635 for net
in http_content
['server']['networks']:
1636 if net
['type'] == 'instance:ovs':
1637 dhcp_nets_id
.append(get_network_id(net
['net_id']))
1640 new_instance_result
, new_instance
= my
.db
.new_instance(content
, nets
, ports_to_free
)
1641 if new_instance_result
< 0:
1642 print "Error http_post_servers() :", new_instance_result
, new_instance
1643 bottle
.abort(-new_instance_result
, new_instance
)
1646 print "inserted at DB"
1650 for port
in ports_to_free
:
1651 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1653 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1657 my
.ovim
.net_update_ofc_thread(net_id
)
1658 except ovim
.ovimException
as e
:
1659 my
.logger
.error("http_post_servers, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1661 # look for dhcp ip address
1662 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "ip_address", "net_id"], WHERE
={"instance_id": new_instance
})
1665 if config_dic
.get("dhcp_server") and iface
["net_id"] in config_dic
["dhcp_nets"]:
1666 #print "dhcp insert add task"
1667 r
,c
= config_dic
['dhcp_thread'].insert_task("add", iface
["mac"])
1669 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1671 #ensure compute contain the bridge for ovs networks:
1672 if iface
.get("net_id"):
1673 server_net
= get_network_id(iface
['net_id'])
1674 if server_net
["network"].get('provider:physical', "")[:3] == 'OVS':
1675 vlan
= str(server_net
['network']['provider:vlan'])
1676 dhcp_enable
= bool(server_net
['network']['enable_dhcp'])
1677 vm_dhcp_ip
= c2
[0]["ip_address"]
1678 config_dic
['host_threads'][server
['host_id']].insert_task("create-ovs-bridge-port", vlan
)
1679 dns
= server_net
['network'].get("dns")
1681 dns
= yaml
.safe_load(server_net
['network'].get("dns"))
1682 routes
= server_net
['network'].get("routes")
1684 routes
= yaml
.safe_load(server_net
['network'].get("routes"))
1685 links
= server_net
['network'].get("links")
1687 links
= yaml
.safe_load(server_net
['network'].get("links"))
1689 dhcp_firt_ip
= str(server_net
['network']['dhcp_first_ip'])
1690 dhcp_last_ip
= str(server_net
['network']['dhcp_last_ip'])
1691 dhcp_cidr
= str(server_net
['network']['cidr'])
1692 gateway
= str(server_net
['network']['gateway_ip'])
1694 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
1695 http_controller
.ovim
.launch_dhcp_server(vlan
, dhcp_firt_ip
, dhcp_last_ip
,
1696 dhcp_cidr
, gateway
, dns
, routes
)
1697 set_mac_dhcp(vm_dhcp_ip
, vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, c2
[0]['mac'])
1700 http_controller
.ovim
.launch_link_bridge_to_ovs(vlan
, gateway
, dhcp_cidr
, links
, routes
)
1704 server
['uuid'] = new_instance
1705 server_start
= server
.get('start', 'yes')
1707 if server_start
!= 'no':
1708 server
['paused'] = True if server_start
== 'paused' else False
1709 server
['action'] = {"start":None}
1710 server
['status'] = "CREATING"
1712 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1714 my
.db
.update_rows('instances', {'status':"ERROR"}, {'uuid':server
['uuid'], 'last_error':c
}, log
=True)
1716 return http_get_server_id(tenant_id
, new_instance
)
1718 bottle
.abort(HTTP_Bad_Request
, content
)
1722 def http_server_action(server_id
, tenant_id
, action
):
1723 '''Perform actions over a server as resume, reboot, terminate, ...'''
1724 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1725 server
={"uuid": server_id
, "action":action
}
1726 where
={'uuid': server_id
}
1727 if tenant_id
!='any':
1728 where
['tenant_id']= tenant_id
1729 result
, content
= my
.db
.get_table(FROM
='instances', WHERE
=where
)
1731 bottle
.abort(HTTP_Not_Found
, "server %s not found" % server_id
)
1734 print "http_post_server_action error getting data %d %s" % (result
, content
)
1735 bottle
.abort(HTTP_Internal_Server_Error
, content
)
1737 server
.update(content
[0])
1738 tenant_id
= server
["tenant_id"]
1740 #TODO check a right content
1742 if 'terminate' in action
:
1743 new_status
='DELETING'
1744 elif server
['status'] == 'ERROR': #or server['status'] == 'CREATING':
1745 if 'terminate' not in action
and 'rebuild' not in action
:
1746 bottle
.abort(HTTP_Method_Not_Allowed
, "Server is in ERROR status, must be rebuit or deleted ")
1748 # elif server['status'] == 'INACTIVE':
1749 # if 'start' not in action and 'createImage' not in action:
1750 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'INACTIVE' status is 'start'")
1752 # if 'start' in action:
1753 # new_status='CREATING'
1754 # server['paused']='no'
1755 # elif server['status'] == 'PAUSED':
1756 # if 'resume' not in action:
1757 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'PAUSED' status is 'resume'")
1759 # elif server['status'] == 'ACTIVE':
1760 # if 'pause' not in action and 'reboot'not in action and 'shutoff'not in action:
1761 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'ACTIVE' status is 'pause','reboot' or 'shutoff'")
1764 if 'start' in action
or 'createImage' in action
or 'rebuild' in action
:
1765 #check image valid and take info
1766 image_id
= server
['image_id']
1767 if 'createImage' in action
:
1768 if 'imageRef' in action
['createImage']:
1769 image_id
= action
['createImage']['imageRef']
1770 elif 'disk' in action
['createImage']:
1771 result
, content
= my
.db
.get_table(FROM
='instance_devices',
1772 SELECT
=('image_id','dev'), WHERE
={'instance_id':server
['uuid'],"type":"disk"})
1774 bottle
.abort(HTTP_Not_Found
, 'disk not found for server')
1778 if action
['createImage']['imageRef']['disk'] != None:
1779 for disk
in content
:
1780 if disk
['dev'] == action
['createImage']['imageRef']['disk']:
1781 disk_id
= disk
['image_id']
1784 bottle
.abort(HTTP_Not_Found
, 'disk %s not found for server' % action
['createImage']['imageRef']['disk'])
1787 bottle
.abort(HTTP_Not_Found
, 'more than one disk found for server' )
1791 image_id
= content
[0]['image_id']
1793 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1794 SELECT
=('path','metadata'), WHERE
={'uuid':image_id
, "status":"ACTIVE"},
1795 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'}, WHERE_AND_OR
="AND", DISTINCT
=True)
1797 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % image_id
)
1799 if content
[0]['metadata'] is not None:
1801 metadata
= json
.loads(content
[0]['metadata'])
1803 return -HTTP_Internal_Server_Error
, "Can not decode image metadata"
1804 content
[0]['metadata']=metadata
1806 content
[0]['metadata'] = {}
1807 server
['image']=content
[0]
1808 if 'createImage' in action
:
1809 action
['createImage']['source'] = {'image_id': image_id
, 'path': content
[0]['path']}
1810 if 'createImage' in action
:
1811 #Create an entry in Database for the new image
1812 new_image
={'status':'BUILD', 'progress': 0 }
1813 new_image_metadata
=content
[0]
1814 if 'metadata' in server
['image'] and server
['image']['metadata'] != None:
1815 new_image_metadata
.update(server
['image']['metadata'])
1816 new_image_metadata
= {"use_incremental":"no"}
1817 if 'metadata' in action
['createImage']:
1818 new_image_metadata
.update(action
['createImage']['metadata'])
1819 new_image
['metadata'] = json
.dumps(new_image_metadata
)
1820 new_image
['name'] = action
['createImage'].get('name', None)
1821 new_image
['description'] = action
['createImage'].get('description', None)
1822 new_image
['uuid']=my
.db
.new_uuid()
1823 if 'path' in action
['createImage']:
1824 new_image
['path'] = action
['createImage']['path']
1826 new_image
['path']="/provisional/path/" + new_image
['uuid']
1827 result
, image_uuid
= my
.db
.new_image(new_image
, tenant_id
)
1829 bottle
.abort(HTTP_Bad_Request
, 'Error: ' + image_uuid
)
1831 server
['new_image'] = new_image
1835 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1837 print "Task queue full at host ", server
['host_id']
1838 bottle
.abort(HTTP_Request_Timeout
, c
)
1839 if 'createImage' in action
and result
>= 0:
1840 return http_get_image_id(tenant_id
, image_uuid
)
1842 #Update DB only for CREATING or DELETING status
1843 data
={'result' : 'deleting in process'}
1845 if new_status
!= None and new_status
== 'DELETING':
1850 #look for dhcp ip address
1851 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "net_id"], WHERE
={"instance_id": server_id
})
1852 r
, c
= my
.db
.delete_instance(server_id
, tenant_id
, nets
, ports_to_free
, net_ovs_list
, "requested by http")
1853 for port
in ports_to_free
:
1854 r1
,c1
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1856 my
.logger
.error("http_post_server_action server deletion ERROR at resore-iface!!!! " + c1
)
1857 warn_text
+= "; Error iface '{}' cannot be restored '{}'".format(str(port
), str(e
))
1860 my
.ovim
.net_update_ofc_thread(net_id
)
1861 except ovim
.ovimException
as e
:
1862 my
.logger
.error("http_server_action, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1863 warn_text
+= "; Error openflow rules of network '{}' cannot be restore '{}'".format(net_id
, str (e
))
1865 # look for dhcp ip address
1866 if r2
>0 and config_dic
.get("dhcp_server"):
1868 if iface
["net_id"] in config_dic
["dhcp_nets"]:
1869 r
,c
= config_dic
['dhcp_thread'].insert_task("del", iface
["mac"])
1870 #print "dhcp insert del task"
1872 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1873 # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan, vm_ip, mac)
1875 for net
in net_ovs_list
:
1881 delete_dhcp_ovs_bridge(vlan
, net_id
)
1882 delete_mac_dhcp(vm_ip
, vlan
, mac
)
1884 net_data
= my
.ovim
.show_network(net_id
)
1885 if net_data
.get('links'):
1886 links
= yaml
.load(net_data
.get('links'))
1887 my
.ovim
.delete_link_bridge_to_ovs(vlan
, links
)
1889 config_dic
['host_threads'][server
['host_id']].insert_task('del-ovs-port', vlan
, net_id
)
1891 data
["result"] += warn_text
1892 return format_out(data
)
1896 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='DELETE')
1897 def http_delete_server_id(tenant_id
, server_id
):
1898 '''delete a server'''
1899 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1900 #check valid tenant_id
1901 result
,content
= check_valid_tenant(my
, tenant_id
)
1903 bottle
.abort(result
, content
)
1906 return http_server_action(server_id
, tenant_id
, {"terminate":None} )
1909 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>/action', method
='POST')
1910 def http_post_server_action(tenant_id
, server_id
):
1911 '''take an action over a server'''
1912 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1913 #check valid tenant_id
1914 result
,content
= check_valid_tenant(my
, tenant_id
)
1916 bottle
.abort(result
, content
)
1918 http_content
= format_in( server_action_schema
)
1919 #r = remove_extra_items(http_content, server_action_schema)
1920 #if r is not None: print "http_post_server_action: Warning: remove extra items ", r
1922 return http_server_action(server_id
, tenant_id
, http_content
)
1929 @bottle.route(url_base
+ '/networks', method
='GET')
1930 def http_get_networks():
1932 Get all networks available
1935 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1939 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_network
,
1940 ('id', 'name', 'tenant_id', 'type',
1941 'shared', 'provider:vlan', 'status', 'last_error',
1942 'admin_state_up', 'provider:physical'))
1943 if "tenant_id" in where_
:
1944 del where_
["tenant_id"]
1946 content
= my
.ovim
.get_networks(select_
, where_
, limit_
)
1948 delete_nulls(content
)
1949 change_keys_http2db(content
, http2db_network
, reverse
=True)
1950 data
= {'networks': content
}
1951 return format_out(data
)
1953 except ovim
.ovimException
as e
:
1954 my
.logger
.error(str(e
), exc_info
=True)
1955 bottle
.abort(e
.http_code
, str(e
))
1956 except Exception as e
:
1957 my
.logger
.error(str(e
), exc_info
=True)
1958 bottle
.abort(HTTP_Bad_Request
, str(e
))
1961 @bottle.route(url_base
+ '/networks/<network_id>', method
='GET')
1962 def http_get_network_id(network_id
):
1964 Get a network data by id
1968 data
= get_network_id(network_id
)
1969 return format_out(data
)
1972 def get_network_id(network_id
):
1974 Get network from DB by id
1975 :param network_id: network Id
1978 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1982 where_
= bottle
.request
.query
1983 content
= my
.ovim
.show_network(network_id
, where_
)
1985 change_keys_http2db(content
, http2db_network
, reverse
=True)
1986 delete_nulls(content
)
1987 data
= {'network': content
}
1989 except ovim
.ovimException
as e
:
1990 my
.logger
.error(str(e
), exc_info
=True)
1991 bottle
.abort(e
.http_code
, str(e
))
1992 except Exception as e
:
1993 my
.logger
.error(str(e
), exc_info
=True)
1994 bottle
.abort(HTTP_Bad_Request
, str(e
))
1997 @bottle.route(url_base
+ '/networks', method
='POST')
1998 def http_post_networks():
2000 Insert a network into the database.
2003 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2007 http_content
= format_in(network_new_schema
)
2008 r
= remove_extra_items(http_content
, network_new_schema
)
2010 print "http_post_networks: Warning: remove extra items ", r
2011 change_keys_http2db(http_content
['network'], http2db_network
)
2012 network
= http_content
['network']
2013 content
= my
.ovim
.new_network(network
)
2014 return format_out(get_network_id(content
))
2015 except ovim
.ovimException
as e
:
2016 my
.logger
.error(str(e
), exc_info
=True)
2017 bottle
.abort(e
.http_code
, str(e
))
2018 except Exception as e
:
2019 my
.logger
.error(str(e
), exc_info
=True)
2020 bottle
.abort(HTTP_Bad_Request
, str(e
))
2023 @bottle.route(url_base
+ '/networks/<network_id>', method
='PUT')
2024 def http_put_network_id(network_id
):
2026 Update a network_id into DB.
2027 :param network_id: network id
2030 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2034 http_content
= format_in(network_update_schema
)
2035 change_keys_http2db(http_content
['network'], http2db_network
)
2036 network
= http_content
['network']
2037 return format_out(my
.ovim
.edit_network(network_id
, network
))
2039 except ovim
.ovimException
as e
:
2040 my
.logger
.error(str(e
), exc_info
=True)
2041 bottle
.abort(e
.http_code
, str(e
))
2042 except Exception as e
:
2043 my
.logger
.error(str(e
), exc_info
=True)
2044 bottle
.abort(HTTP_Bad_Request
, str(e
))
2047 @bottle.route(url_base
+ '/networks/<network_id>', method
='DELETE')
2048 def http_delete_network_id(network_id
):
2050 Delete a network_id from the database.
2051 :param network_id: Network id
2054 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2057 # delete from the data base
2058 content
= my
.ovim
.delete_network(network_id
)
2059 data
= {'result': content
}
2060 return format_out(data
)
2062 except ovim
.ovimException
as e
:
2063 my
.logger
.error(str(e
), exc_info
=True)
2064 bottle
.abort(e
.http_code
, str(e
))
2065 except Exception as e
:
2066 my
.logger
.error(str(e
), exc_info
=True)
2067 bottle
.abort(HTTP_Bad_Request
, str(e
))
2074 @bottle.route(url_base
+ '/openflow/controller', method
='GET')
2075 def http_get_openflow_controller():
2077 Retrieve a openflow controllers list from DB.
2080 # TODO check if show a proper list
2081 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2084 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_ofc
,
2085 ('id', 'name', 'dpid', 'ip', 'port', 'type',
2086 'version', 'user', 'password'))
2088 content
= my
.ovim
.get_of_controllers(select_
, where_
)
2089 delete_nulls(content
)
2090 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2091 data
= {'ofcs': content
}
2092 return format_out(data
)
2093 except ovim
.ovimException
as e
:
2094 my
.logger
.error(str(e
), exc_info
=True)
2095 bottle
.abort(e
.http_code
, str(e
))
2096 except Exception as e
:
2097 my
.logger
.error(str(e
), exc_info
=True)
2098 bottle
.abort(HTTP_Bad_Request
, str(e
))
2101 @bottle.route(url_base
+ '/openflow/controller/<uuid>', method
='GET')
2102 def http_get_openflow_controller_id(uuid
):
2104 Get an openflow controller by dpid from DB.get_of_controllers
2106 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2110 content
= my
.ovim
.show_of_controller(uuid
)
2111 delete_nulls(content
)
2112 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2113 data
= {'ofc': content
}
2114 return format_out(data
)
2115 except ovim
.ovimException
as e
:
2116 my
.logger
.error(str(e
), exc_info
=True)
2117 bottle
.abort(e
.http_code
, str(e
))
2118 except Exception as e
:
2119 my
.logger
.error(str(e
), exc_info
=True)
2120 bottle
.abort(HTTP_Bad_Request
, str(e
))
2123 @bottle.route(url_base
+ '/openflow/controller/', method
='POST')
2124 def http_post_openflow_controller():
2126 Create a new openflow controller into DB
2129 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2132 http_content
= format_in(openflow_controller_schema
)
2133 of_c
= http_content
['ofc']
2134 uuid
= my
.ovim
.new_of_controller(of_c
)
2135 content
= my
.ovim
.show_of_controller(uuid
)
2136 delete_nulls(content
)
2137 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2138 data
= {'ofc': content
}
2139 return format_out(data
)
2140 except ovim
.ovimException
as e
:
2141 my
.logger
.error(str(e
), exc_info
=True)
2142 bottle
.abort(e
.http_code
, str(e
))
2143 except Exception as e
:
2144 my
.logger
.error(str(e
), exc_info
=True)
2145 bottle
.abort(HTTP_Bad_Request
, str(e
))
2148 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='PUT')
2149 def http_put_openflow_controller_by_id(of_controller_id
):
2151 Create an openflow controller into DB
2152 :param of_controller_id: openflow controller dpid
2155 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2158 http_content
= format_in(openflow_controller_schema
)
2159 of_c
= http_content
['ofc']
2161 content
= my
.ovim
.edit_of_controller(of_controller_id
, of_c
)
2162 delete_nulls(content
)
2163 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2164 data
= {'ofc': content
}
2165 return format_out(data
)
2166 except ovim
.ovimException
as e
:
2167 my
.logger
.error(str(e
), exc_info
=True)
2168 bottle
.abort(e
.http_code
, str(e
))
2169 except Exception as e
:
2170 my
.logger
.error(str(e
), exc_info
=True)
2171 bottle
.abort(HTTP_Bad_Request
, str(e
))
2174 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='DELETE')
2175 def http_delete_openflow_controller(of_controller_id
):
2177 Delete an openflow controller from DB.
2178 :param of_controller_id: openflow controller dpid
2181 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2184 content
= my
.ovim
.delete_of_controller(of_controller_id
)
2185 data
= {'result': content
}
2186 return format_out(data
)
2187 except ovim
.ovimException
as e
:
2188 my
.logger
.error(str(e
), exc_info
=True)
2189 bottle
.abort(e
.http_code
, str(e
))
2190 except Exception as e
:
2191 my
.logger
.error(str(e
), exc_info
=True)
2192 bottle
.abort(HTTP_Bad_Request
, str(e
))
2195 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='GET')
2196 def http_get_openflow_id(network_id
):
2198 To obtain the list of openflow rules of a network
2199 :param network_id: network id
2202 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2205 if network_id
== 'all':
2208 content
= my
.ovim
.get_openflow_rules(network_id
)
2209 data
= {'openflow-rules': content
}
2210 except ovim
.ovimException
as e
:
2211 my
.logger
.error(str(e
), exc_info
=True)
2212 bottle
.abort(e
.http_code
, str(e
))
2213 except Exception as e
:
2214 my
.logger
.error(str(e
), exc_info
=True)
2215 bottle
.abort(HTTP_Bad_Request
, str(e
))
2217 return format_out(data
)
2220 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='PUT')
2221 def http_put_openflow_id(network_id
):
2223 To make actions over the net. The action is to reinstall the openflow rules
2224 network_id can be 'all'
2225 :param network_id: network id
2228 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2231 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2233 if network_id
== 'all':
2237 result
= my
.ovim
.edit_openflow_rules(network_id
)
2238 except ovim
.ovimException
as e
:
2239 my
.logger
.error(str(e
), exc_info
=True)
2240 bottle
.abort(e
.http_code
, str(e
))
2241 except Exception as e
:
2242 my
.logger
.error(str(e
), exc_info
=True)
2243 bottle
.abort(HTTP_Bad_Request
, str(e
))
2245 data
= {'result': str(result
) + " nets updates"}
2246 return format_out(data
)
2248 @bottle.route(url_base
+ '/networks/clear/openflow/<ofc_id>', method
='DELETE')
2249 @bottle.route(url_base
+ '/networks/clear/openflow', method
='DELETE')
2250 def http_clear_openflow_rules(ofc_id
=None):
2252 To make actions over the net. The action is to delete ALL openflow rules
2255 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2258 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2260 my
.ovim
.delete_openflow_rules(ofc_id
)
2261 except ovim
.ovimException
as e
:
2262 my
.logger
.error(str(e
), exc_info
=True)
2263 bottle
.abort(e
.http_code
, str(e
))
2264 except Exception as e
:
2265 my
.logger
.error(str(e
), exc_info
=True)
2266 bottle
.abort(HTTP_Bad_Request
, str(e
))
2268 data
= {'result': " Clearing openflow rules in process"}
2269 return format_out(data
)
2271 @bottle.route(url_base
+ '/networks/openflow/ports/<ofc_id>', method
='GET')
2272 @bottle.route(url_base
+ '/networks/openflow/ports', method
='GET')
2273 def http_get_openflow_ports(ofc_id
=None):
2275 Obtain switch ports names of openflow controller
2278 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2281 ports
= my
.ovim
.get_openflow_ports(ofc_id
)
2282 data
= {'ports': ports
}
2283 except ovim
.ovimException
as e
:
2284 my
.logger
.error(str(e
), exc_info
=True)
2285 bottle
.abort(e
.http_code
, str(e
))
2286 except Exception as e
:
2287 my
.logger
.error(str(e
), exc_info
=True)
2288 bottle
.abort(HTTP_Bad_Request
, str(e
))
2290 return format_out(data
)
2296 @bottle.route(url_base
+ '/ports', method
='GET')
2297 def http_get_ports():
2299 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2300 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_port
,
2301 ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
2302 'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
2304 ports
= my
.ovim
.get_ports(columns
=select_
, filter=where_
, limit
=limit_
)
2306 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2307 data
={'ports' : ports
}
2308 return format_out(data
)
2309 except ovim
.ovimException
as e
:
2310 my
.logger
.error(str(e
), exc_info
=True)
2311 bottle
.abort(e
.http_code
, str(e
))
2312 except Exception as e
:
2313 my
.logger
.error(str(e
), exc_info
=True)
2314 bottle
.abort(HTTP_Bad_Request
, str(e
))
2316 @bottle.route(url_base
+ '/ports/<port_id>', method
='GET')
2317 def http_get_port_id(port_id
):
2318 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2320 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2322 bottle
.abort(HTTP_Not_Found
, 'port %s not found' % port_id
)
2325 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2326 data
= {'port': ports
[0]}
2327 return format_out(data
)
2328 except ovim
.ovimException
as e
:
2329 my
.logger
.error(str(e
), exc_info
=True)
2330 bottle
.abort(e
.http_code
, str(e
))
2331 except Exception as e
:
2332 my
.logger
.error(str(e
), exc_info
=True)
2333 bottle
.abort(HTTP_Bad_Request
, str(e
))
2335 @bottle.route(url_base
+ '/ports', method
='POST')
2336 def http_post_ports():
2337 '''insert an external port into the database.'''
2338 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2340 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2342 http_content
= format_in( port_new_schema
)
2343 r
= remove_extra_items(http_content
, port_new_schema
)
2344 if r
is not None: print "http_post_ports: Warning: remove extra items ", r
2345 change_keys_http2db(http_content
['port'], http2db_port
)
2346 port
=http_content
['port']
2348 port_id
= my
.ovim
.new_port(port
)
2349 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2351 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' inserted but not found at database".format(port_id
))
2354 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2355 data
= {'port': ports
[0]}
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
))
2364 @bottle.route(url_base
+ '/ports/<port_id>', method
='PUT')
2365 def http_put_port_id(port_id
):
2366 '''update a port_id into the database.'''
2367 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2369 http_content
= format_in( port_update_schema
)
2370 change_keys_http2db(http_content
['port'], http2db_port
)
2371 port_dict
=http_content
['port']
2373 for k
in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
2374 if k
in port_dict
and not my
.admin
:
2375 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges for changing " + k
)
2378 port_id
= my
.ovim
.edit_port(port_id
, port_dict
, my
.admin
)
2379 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2381 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' edited but not found at database".format(port_id
))
2384 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2385 data
= {'port': ports
[0]}
2386 return format_out(data
)
2387 except ovim
.ovimException
as e
:
2388 my
.logger
.error(str(e
), exc_info
=True)
2389 bottle
.abort(e
.http_code
, str(e
))
2390 except Exception as e
:
2391 my
.logger
.error(str(e
), exc_info
=True)
2392 bottle
.abort(HTTP_Bad_Request
, str(e
))
2395 @bottle.route(url_base
+ '/ports/<port_id>', method
='DELETE')
2396 def http_delete_port_id(port_id
):
2397 '''delete a port_id from the database.'''
2398 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2400 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2403 result
= my
.ovim
.delete_port(port_id
)
2404 data
= {'result': result
}
2405 return format_out(data
)
2406 except ovim
.ovimException
as e
:
2407 my
.logger
.error(str(e
), exc_info
=True)
2408 bottle
.abort(e
.http_code
, str(e
))
2409 except Exception as e
:
2410 my
.logger
.error(str(e
), exc_info
=True)
2411 bottle
.abort(HTTP_Bad_Request
, str(e
))
2414 @bottle.route(url_base
+ '/openflow/mapping', method
='POST')
2415 def http_of_port_mapping():
2417 Create new compute port mapping entry
2420 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2423 http_content
= format_in(of_port_map_new_schema
)
2424 r
= remove_extra_items(http_content
, of_port_map_new_schema
)
2426 my
.logger
.error("http_of_port_mapping: Warning: remove extra items " + str(r
), exc_info
=True)
2428 # insert in data base
2429 port_mapping
= my
.ovim
.set_of_port_mapping(http_content
['of_port_mapings'])
2430 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2431 delete_nulls(port_mapping
)
2432 data
= {'of_port_mappings': port_mapping
}
2433 return format_out(data
)
2434 except ovim
.ovimException
as e
:
2435 my
.logger
.error(str(e
), exc_info
=True)
2436 bottle
.abort(e
.http_code
, str(e
))
2437 except Exception as e
:
2438 my
.logger
.error(str(e
), exc_info
=True)
2439 bottle
.abort(HTTP_Bad_Request
, str(e
))
2442 @bottle.route(url_base
+ '/openflow/mapping', method
='GET')
2443 def get_of_port_mapping():
2445 Get compute port mapping
2448 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2451 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_id
,
2452 ('id', 'ofc_id', 'region', 'compute_node', 'pci',
2453 'switch_dpid', 'switch_port', 'switch_mac'))
2454 # insert in data base
2455 port_mapping
= my
.ovim
.get_of_port_mappings(select_
, where_
)
2456 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2457 delete_nulls(port_mapping
)
2458 data
= {'of_port_mappings': port_mapping
}
2459 return format_out(data
)
2460 except ovim
.ovimException
as e
:
2461 my
.logger
.error(str(e
), exc_info
=True)
2462 bottle
.abort(e
.http_code
, str(e
))
2463 except Exception as e
:
2464 my
.logger
.error(str(e
), exc_info
=True)
2465 bottle
.abort(HTTP_Bad_Request
, str(e
))
2468 @bottle.route(url_base
+ '/openflow/mapping/<region>', method
='DELETE')
2469 def delete_of_port_mapping(region
):
2471 Insert a tenant into the database.
2474 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2477 # insert in data base
2478 db_filter
= {'region': region
}
2479 result
= my
.ovim
.clear_of_port_mapping(db_filter
)
2480 data
= {'result': result
}
2481 return format_out(data
)
2482 except ovim
.ovimException
as e
:
2483 my
.logger
.error(str(e
), exc_info
=True)
2484 bottle
.abort(e
.http_code
, str(e
))
2485 except Exception as e
:
2486 my
.logger
.error(str(e
), exc_info
=True)
2487 bottle
.abort(HTTP_Bad_Request
, str(e
))