1 # -*- coding: utf-8 -*-
4 # Copyright 2015 Telefonica Investigacion 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','osImageType':'os_image_type','imageRef':'image_id','created':'created_at'} #Unikernels extension
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', 'hypervisors')) #Unikernels extension
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"]),
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
,
660 hypervisors
=host
.get('hypervisors', None)) #Unikernels extension
663 config_dic
['host_threads'][content
['uuid']] = thread
665 if config_dic
['network_type'] == 'ovs':
667 create_dhcp_ovs_bridge()
668 config_dic
['host_threads'][content
['uuid']].insert_task("new-ovsbridge")
669 # create vlxan bwt OVS controller and computes
670 create_vxlan_mesh(content
['uuid'], my
.logger
)
673 change_keys_http2db(content
, http2db_host
, reverse
=True)
674 if len(warning_text
)>0:
675 content
["warning"]= warning_text
676 data
={'host' : content
}
677 return format_out(data
)
679 bottle
.abort(HTTP_Bad_Request
, content
)
683 def delete_dhcp_ovs_bridge(vlan
, net_uuid
):
685 Delete bridges and port created during dhcp launching at openvim controller
686 :param vlan: net vlan id
687 :param net_uuid: network identifier
690 dhcp_path
= config_dic
['ovs_controller_file_path']
692 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
693 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
695 dhcp_controller
.delete_dhcp_server(vlan
, net_uuid
, dhcp_path
)
696 dhcp_controller
.delete_dhcp_port(vlan
, net_uuid
, dhcp_path
)
699 def create_dhcp_ovs_bridge():
701 Initialize bridge to allocate the dhcp server at openvim controller
704 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
705 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
707 dhcp_controller
.create_ovs_bridge()
710 def set_mac_dhcp(vm_ip
, vlan
, first_ip
, last_ip
, cidr
, mac
):
712 Launch a dhcpserver base on dnsmasq attached to the net base on vlan id across the the openvim computes
713 :param vm_ip: IP address asigned to a VM
714 :param vlan: Segmentation id
715 :param first_ip: First dhcp range ip
716 :param last_ip: Last dhcp range ip
717 :param cidr: net cidr
718 :param mac: VM vnic mac to be macthed with the IP received
722 ip_tools
= IPNetwork(cidr
)
723 cidr_len
= ip_tools
.prefixlen
724 dhcp_netmask
= str(ip_tools
.netmask
)
725 dhcp_path
= config_dic
['ovs_controller_file_path']
727 new_cidr
= [first_ip
+ '/' + str(cidr_len
)]
728 if not len(all_matching_cidrs(vm_ip
, new_cidr
)):
731 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
732 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
734 dhcp_controller
.set_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_netmask
, first_ip
, dhcp_path
)
737 def delete_mac_dhcp(vm_ip
, vlan
, mac
):
739 Delete into dhcp conf file the ip assigned to a specific MAC address
740 :param vm_ip: IP address asigned to a VM
741 :param vlan: Segmentation id
742 :param mac: VM vnic mac to be macthed with the IP received
746 dhcp_path
= config_dic
['ovs_controller_file_path']
748 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
749 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
751 dhcp_controller
.delete_mac_dhcp_server(vm_ip
, mac
, vlan
, dhcp_path
)
754 def create_vxlan_mesh(host_id
, logger
=None):
756 Create vxlan mesh across all openvimc controller and computes.
757 :param host_id: Added compute node id. Anyway vlan is created by all compute nodes
758 :param logger: To log errors
761 dhcp_compute_name
= get_vxlan_interface("dhcp")
762 existing_hosts
= get_hosts()
763 if len(existing_hosts
['hosts']) > 0:
764 # vlxan mesh creation between openvim controller and computes
765 computes_available
= existing_hosts
['hosts']
767 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
768 dhcp_controller
= http_controller
.ovim
.get_dhcp_controller()
770 for compute
in computes_available
:
772 if compute
['ip_name'] != 'localhost':
773 remote_ip
= socket
.gethostbyname(compute
['ip_name'])
775 remote_ip
= 'localhost'
776 except socket
.error
as e
:
778 logger
.error("Cannot get compute node remote ip from '{}'. Skipping: {}".format(
779 compute
['ip_name'], e
))
781 # vxlan ovs_controller <=> compute node
782 vxlan_interface_name
= get_vxlan_interface(compute
['id'][:8])
783 config_dic
['host_threads'][compute
['id']].insert_task("new-vxlan", dhcp_compute_name
, dhcp_controller
.host
)
784 dhcp_controller
.create_ovs_vxlan_tunnel(vxlan_interface_name
, remote_ip
)
785 # vxlan from others compute node to cthis ompute node
786 for compute_src
in computes_available
:
787 if compute_src
['id'] == compute
['id']:
789 config_dic
['host_threads'][compute_src
['id']].insert_task("new-vxlan",
790 vxlan_interface_name
,
793 def delete_vxlan_mesh(host_id
):
795 Create a task for remove a specific compute of the vlxan mesh
796 :param host_id: host id to be deleted.
798 existing_hosts
= get_hosts()
799 computes_available
= existing_hosts
['hosts']
801 vxlan_interface_name
= get_vxlan_interface(host_id
[:8])
803 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
804 dhcp_host
= http_controller
.ovim
.get_dhcp_controller()
806 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
807 # remove bridge from openvim controller if no more computes exist
808 if len(existing_hosts
):
809 dhcp_host
.delete_ovs_bridge()
811 for compute
in computes_available
:
812 if host_id
== compute
['id']:
815 dhcp_host
.delete_ovs_vxlan_tunnel(vxlan_interface_name
)
816 config_dic
['host_threads'][compute
['id']].insert_task("del-vxlan", vxlan_interface_name
)
819 def get_vxlan_interface(local_uuid
):
821 Genearte a vxlan interface name
822 :param local_uuid: host id
823 :return: vlxan-8digits
825 return 'vxlan-' + local_uuid
[:8]
828 @bottle.route(url_base
+ '/hosts/<host_id>', method
='PUT')
829 def http_put_host_id(host_id
):
830 '''modify a host into the database. All resources are got and inserted'''
831 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
834 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
837 http_content
= format_in( host_edit_schema
)
838 r
= remove_extra_items(http_content
, host_edit_schema
)
839 if r
is not None: print "http_post_host_id: Warning: remove extra items ", r
840 change_keys_http2db(http_content
['host'], http2db_host
)
843 result
, content
= my
.db
.edit_host(host_id
, http_content
['host'])
845 convert_boolean(content
, ('admin_state_up',) )
846 change_keys_http2db(content
, http2db_host
, reverse
=True)
847 data
={'host' : content
}
849 if config_dic
['network_type'] == 'ovs':
850 delete_vxlan_mesh(host_id
)
851 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
854 config_dic
['host_threads'][host_id
].name
= content
.get('name',content
['ip_name'])
855 config_dic
['host_threads'][host_id
].user
= content
['user']
856 config_dic
['host_threads'][host_id
].host
= content
['ip_name']
857 config_dic
['host_threads'][host_id
].insert_task("reload")
859 if config_dic
['network_type'] == 'ovs':
860 # create mesh with new host data
861 config_dic
['host_threads'][host_id
].insert_task("new-ovsbridge")
862 create_vxlan_mesh(host_id
, my
.logger
)
865 return format_out(data
)
867 bottle
.abort(HTTP_Bad_Request
, content
)
872 @bottle.route(url_base
+ '/hosts/<host_id>', method
='DELETE')
873 def http_delete_host_id(host_id
):
874 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
877 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
878 result
, content
= my
.db
.delete_row('hosts', host_id
)
880 bottle
.abort(HTTP_Not_Found
, content
)
882 if config_dic
['network_type'] == 'ovs':
883 delete_vxlan_mesh(host_id
)
885 if host_id
in config_dic
['host_threads']:
886 if config_dic
['network_type'] == 'ovs':
887 config_dic
['host_threads'][host_id
].insert_task("del-ovsbridge")
888 config_dic
['host_threads'][host_id
].insert_task("exit")
890 data
={'result' : content
}
891 return format_out(data
)
893 print "http_delete_host_id error",result
, content
894 bottle
.abort(-result
, content
)
901 @bottle.route(url_base
+ '/tenants', method
='GET')
902 def http_get_tenants():
904 Retreive tenant list from DB
907 my
= config_dic
['http_threads'][threading
.current_thread().name
]
910 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_tenant
,
911 ('id', 'name', 'description', 'enabled'))
912 tenants
= my
.ovim
.get_tenants(select_
, where_
)
913 delete_nulls(tenants
)
914 change_keys_http2db(tenants
, http2db_tenant
, reverse
=True)
915 data
= {'tenants': tenants
}
916 return format_out(data
)
917 except ovim
.ovimException
as e
:
918 my
.logger
.error(str(e
), exc_info
=True)
919 bottle
.abort(e
.http_code
, str(e
))
920 except Exception as e
:
921 my
.logger
.error(str(e
), exc_info
=True)
922 bottle
.abort(HTTP_Bad_Request
, str(e
))
925 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='GET')
926 def http_get_tenant_id(tenant_id
):
928 Get tenant from DB by id
929 :param tenant_id: tenant id
932 my
= config_dic
['http_threads'][threading
.current_thread().name
]
935 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
937 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
938 data
= {'tenant': tenant
}
939 return format_out(data
)
940 except ovim
.ovimException
as e
:
941 my
.logger
.error(str(e
), exc_info
=True)
942 bottle
.abort(e
.http_code
, str(e
))
943 except Exception as e
:
944 my
.logger
.error(str(e
), exc_info
=True)
945 bottle
.abort(HTTP_Bad_Request
, str(e
))
948 @bottle.route(url_base
+ '/tenants', method
='POST')
949 def http_post_tenants():
951 Insert a tenant into the database.
954 my
= config_dic
['http_threads'][threading
.current_thread().name
]
957 http_content
= format_in(tenant_new_schema
)
958 r
= remove_extra_items(http_content
, tenant_new_schema
)
960 my
.logger
.error("http_post_tenants: Warning: remove extra items " + str(r
), exc_info
=True)
961 # insert in data base
962 tenant_id
= my
.ovim
.new_tentant(http_content
['tenant'])
963 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
964 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
966 data
= {'tenant': tenant
}
967 return format_out(data
)
968 except ovim
.ovimException
as e
:
969 my
.logger
.error(str(e
), exc_info
=True)
970 bottle
.abort(e
.http_code
, str(e
))
971 except Exception as e
:
972 my
.logger
.error(str(e
), exc_info
=True)
973 bottle
.abort(HTTP_Bad_Request
, str(e
))
976 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='PUT')
977 def http_put_tenant_id(tenant_id
):
979 Update a tenantinto DB.
980 :param tenant_id: tentant id
984 my
= config_dic
['http_threads'][threading
.current_thread().name
]
987 http_content
= format_in(tenant_edit_schema
)
988 r
= remove_extra_items(http_content
, tenant_edit_schema
)
990 print "http_put_tenant_id: Warning: remove extra items ", r
991 change_keys_http2db(http_content
['tenant'], http2db_tenant
)
992 # insert in data base
993 my
.ovim
.edit_tenant(tenant_id
, http_content
['tenant'])
994 tenant
= my
.ovim
.show_tenant_id(tenant_id
)
995 change_keys_http2db(tenant
, http2db_tenant
, reverse
=True)
996 data
= {'tenant': tenant
}
997 return format_out(data
)
998 except ovim
.ovimException
as e
:
999 my
.logger
.error(str(e
), exc_info
=True)
1000 bottle
.abort(e
.http_code
, str(e
))
1001 except Exception as e
:
1002 my
.logger
.error(str(e
), exc_info
=True)
1003 bottle
.abort(HTTP_Bad_Request
, str(e
))
1006 @bottle.route(url_base
+ '/tenants/<tenant_id>', method
='DELETE')
1007 def http_delete_tenant_id(tenant_id
):
1009 Delete a tenant from the database.
1010 :param tenant_id: tenant id
1013 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1016 content
= my
.ovim
.delete_tentant(tenant_id
)
1017 data
= {'result': content
}
1018 return format_out(data
)
1019 except ovim
.ovimException
as e
:
1020 my
.logger
.error(str(e
), exc_info
=True)
1021 bottle
.abort(e
.http_code
, str(e
))
1022 except Exception as e
:
1023 my
.logger
.error(str(e
), exc_info
=True)
1024 bottle
.abort(HTTP_Bad_Request
, str(e
))
1030 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='GET')
1031 def http_get_flavors(tenant_id
):
1032 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1033 #check valid tenant_id
1034 result
,content
= check_valid_tenant(my
, tenant_id
)
1036 bottle
.abort(result
, content
)
1038 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1039 ('id','name','description','public') )
1040 if tenant_id
=='any':
1043 from_
='tenants_flavors inner join flavors on tenants_flavors.flavor_id=flavors.uuid'
1044 where_
['tenant_id'] = tenant_id
1045 result
, content
= my
.db
.get_table(FROM
=from_
, SELECT
=select_
, WHERE
=where_
, LIMIT
=limit_
)
1047 print "http_get_flavors Error", content
1048 bottle
.abort(-result
, content
)
1050 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1052 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(row
['id']) ) ), 'rel':'bookmark' } ]
1053 data
={'flavors' : content
}
1054 return format_out(data
)
1056 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='GET')
1057 def http_get_flavor_id(tenant_id
, flavor_id
):
1058 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1059 #check valid tenant_id
1060 result
,content
= check_valid_tenant(my
, tenant_id
)
1062 bottle
.abort(result
, content
)
1064 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_flavor
,
1065 ('id','name','description','ram', 'vcpus', 'extended', 'disk', 'public') )
1066 if tenant_id
=='any':
1069 from_
='tenants_flavors as tf inner join flavors as f on tf.flavor_id=f.uuid'
1070 where_
['tenant_id'] = tenant_id
1071 where_
['uuid'] = flavor_id
1072 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
=from_
, WHERE
=where_
, LIMIT
=limit_
)
1075 print "http_get_flavor_id error %d %s" % (result
, content
)
1076 bottle
.abort(-result
, content
)
1078 print "http_get_flavors_id flavor '%s' not found" % str(flavor_id
)
1079 bottle
.abort(HTTP_Not_Found
, 'flavor %s not found' % flavor_id
)
1081 change_keys_http2db(content
, http2db_flavor
, reverse
=True)
1082 if 'extended' in content
[0] and content
[0]['extended'] is not None:
1083 extended
= json
.loads(content
[0]['extended'])
1084 if 'devices' in extended
:
1085 change_keys_http2db(extended
['devices'], http2db_flavor
, reverse
=True)
1086 content
[0]['extended']=extended
1087 convert_bandwidth(content
[0], reverse
=True)
1088 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'flavors', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1089 data
={'flavor' : content
[0]}
1090 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1091 return format_out(data
)
1094 @bottle.route(url_base
+ '/<tenant_id>/flavors', method
='POST')
1095 def http_post_flavors(tenant_id
):
1096 '''insert a flavor into the database, and attach to tenant.'''
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
)
1102 http_content
= format_in( flavor_new_schema
)
1103 r
= remove_extra_items(http_content
, flavor_new_schema
)
1104 if r
is not None: print "http_post_flavors: Warning: remove extra items ", r
1105 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1106 extended_dict
= http_content
['flavor'].pop('extended', None)
1107 if extended_dict
is not None:
1108 result
, content
= check_extended(extended_dict
)
1110 print "http_post_flavors wrong input extended error %d %s" % (result
, content
)
1111 bottle
.abort(-result
, content
)
1113 convert_bandwidth(extended_dict
)
1114 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1115 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1116 #insert in data base
1117 result
, content
= my
.db
.new_flavor(http_content
['flavor'], tenant_id
)
1119 return http_get_flavor_id(tenant_id
, content
)
1121 print "http_psot_flavors error %d %s" % (result
, content
)
1122 bottle
.abort(-result
, content
)
1125 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='DELETE')
1126 def http_delete_flavor_id(tenant_id
, flavor_id
):
1127 '''Deletes the flavor_id of a tenant. IT removes from tenants_flavors table.'''
1128 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1129 #check valid tenant_id
1130 result
,content
= check_valid_tenant(my
, tenant_id
)
1132 bottle
.abort(result
, content
)
1134 result
, content
= my
.db
.delete_image_flavor('flavor', flavor_id
, tenant_id
)
1136 bottle
.abort(HTTP_Not_Found
, content
)
1138 data
={'result' : content
}
1139 return format_out(data
)
1141 print "http_delete_flavor_id error",result
, content
1142 bottle
.abort(-result
, content
)
1145 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>/<action>', method
='POST')
1146 def http_attach_detach_flavors(tenant_id
, flavor_id
, action
):
1147 '''attach/detach an existing flavor in this tenant. That is insert/remove at tenants_flavors table.'''
1148 #TODO alf: not tested at all!!!
1149 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1150 #check valid tenant_id
1151 result
,content
= check_valid_tenant(my
, tenant_id
)
1153 bottle
.abort(result
, content
)
1154 if tenant_id
=='any':
1155 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1157 if action
!='attach' and action
!= 'detach':
1158 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1161 #Ensure that flavor exist
1162 from_
='tenants_flavors as tf right join flavors as f on tf.flavor_id=f.uuid'
1163 where_
={'uuid': flavor_id
}
1164 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1166 if action
=='attach':
1167 text_error
="Flavor '%s' not found" % flavor_id
1169 text_error
="Flavor '%s' not found for tenant '%s'" % (flavor_id
, tenant_id
)
1170 bottle
.abort(HTTP_Not_Found
, text_error
)
1174 if action
=='attach':
1175 if flavor
['tenant_id']!=None:
1176 bottle
.abort(HTTP_Conflict
, "Flavor '%s' already attached to tenant '%s'" % (flavor_id
, tenant_id
))
1177 if flavor
['public']=='no' and not my
.admin
:
1178 #allow only attaching public flavors
1179 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private flavor")
1181 #insert in data base
1182 result
, content
= my
.db
.new_row('tenants_flavors', {'flavor_id':flavor_id
, 'tenant_id': tenant_id
})
1184 return http_get_flavor_id(tenant_id
, flavor_id
)
1186 if flavor
['tenant_id']==None:
1187 bottle
.abort(HTTP_Not_Found
, "Flavor '%s' not attached to tenant '%s'" % (flavor_id
, tenant_id
))
1188 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_flavors', WHERE
={'flavor_id':flavor_id
, 'tenant_id':tenant_id
})
1190 if flavor
['public']=='no':
1191 #try to delete the flavor completely to avoid orphan flavors, IGNORE error
1192 my
.db
.delete_row_by_dict(FROM
='flavors', WHERE
={'uuid':flavor_id
})
1193 data
={'result' : "flavor detached"}
1194 return format_out(data
)
1196 #if get here is because an error
1197 print "http_attach_detach_flavors error %d %s" % (result
, content
)
1198 bottle
.abort(-result
, content
)
1201 @bottle.route(url_base
+ '/<tenant_id>/flavors/<flavor_id>', method
='PUT')
1202 def http_put_flavor_id(tenant_id
, flavor_id
):
1203 '''update a flavor_id into the database.'''
1204 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1205 #check valid tenant_id
1206 result
,content
= check_valid_tenant(my
, tenant_id
)
1208 bottle
.abort(result
, content
)
1210 http_content
= format_in( flavor_update_schema
)
1211 r
= remove_extra_items(http_content
, flavor_update_schema
)
1212 if r
is not None: print "http_put_flavor_id: Warning: remove extra items ", r
1213 change_keys_http2db(http_content
['flavor'], http2db_flavor
)
1214 extended_dict
= http_content
['flavor'].pop('extended', None)
1215 if extended_dict
is not None:
1216 result
, content
= check_extended(extended_dict
)
1218 print "http_put_flavor_id wrong input extended error %d %s" % (result
, content
)
1219 bottle
.abort(-result
, content
)
1221 convert_bandwidth(extended_dict
)
1222 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_flavor
)
1223 http_content
['flavor']['extended'] = json
.dumps(extended_dict
)
1224 #Ensure that flavor exist
1225 where_
={'uuid': flavor_id
}
1226 if tenant_id
=='any':
1229 from_
='tenants_flavors as ti inner join flavors as i on ti.flavor_id=i.uuid'
1230 where_
['tenant_id'] = tenant_id
1231 result
, content
= my
.db
.get_table(SELECT
=('public',), FROM
=from_
, WHERE
=where_
)
1233 text_error
="Flavor '%s' not found" % flavor_id
1234 if tenant_id
!='any':
1235 text_error
+=" for tenant '%s'" % flavor_id
1236 bottle
.abort(HTTP_Not_Found
, text_error
)
1239 if content
[0]['public']=='yes' and not my
.admin
:
1240 #allow only modifications over private flavors
1241 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public flavor")
1243 #insert in data base
1244 result
, content
= my
.db
.update_rows('flavors', http_content
['flavor'], {'uuid': flavor_id
})
1247 print "http_put_flavor_id error %d %s" % (result
, content
)
1248 bottle
.abort(-result
, content
)
1251 return http_get_flavor_id(tenant_id
, flavor_id
)
1259 @bottle.route(url_base
+ '/<tenant_id>/images', method
='GET')
1260 def http_get_images(tenant_id
):
1261 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1262 #check valid tenant_id
1263 result
,content
= check_valid_tenant(my
, tenant_id
)
1265 bottle
.abort(result
, content
)
1267 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1268 ('id','name','checksum','description','path','public') )
1269 if tenant_id
=='any':
1273 from_
='tenants_images right join images on tenants_images.image_id=images.uuid'
1274 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1275 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1277 print "http_get_images Error", content
1278 bottle
.abort(-result
, content
)
1280 change_keys_http2db(content
, http2db_image
, reverse
=True)
1281 #for row in content: row['links']=[ {'href': "/".join( (my.url_preffix, tenant_id, 'images', str(row['id']) ) ), 'rel':'bookmark' } ]
1282 data
={'images' : content
}
1283 return format_out(data
)
1285 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='GET')
1286 def http_get_image_id(tenant_id
, image_id
):
1287 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1288 #check valid tenant_id
1289 result
,content
= check_valid_tenant(my
, tenant_id
)
1291 bottle
.abort(result
, content
)
1293 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_image
,
1294 ('id','name','checksum','description','progress', 'status','path', 'created', 'updated','public') )
1295 if tenant_id
=='any':
1299 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1300 where_or_
= {'tenant_id': tenant_id
, 'public': "yes"}
1301 where_
['uuid'] = image_id
1302 result
, content
= my
.db
.get_table(SELECT
=select_
, DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND", LIMIT
=limit_
)
1305 print "http_get_images error %d %s" % (result
, content
)
1306 bottle
.abort(-result
, content
)
1308 print "http_get_images image '%s' not found" % str(image_id
)
1309 bottle
.abort(HTTP_Not_Found
, 'image %s not found' % image_id
)
1311 convert_datetime2str(content
)
1312 change_keys_http2db(content
, http2db_image
, reverse
=True)
1313 if 'metadata' in content
[0] and content
[0]['metadata'] is not None:
1314 metadata
= json
.loads(content
[0]['metadata'])
1315 content
[0]['metadata']=metadata
1316 content
[0]['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'images', str(content
[0]['id']) ) ), 'rel':'bookmark' } ]
1317 data
={'image' : content
[0]}
1318 #data['tenants_links'] = dict([('tenant', row['id']) for row in content])
1319 return format_out(data
)
1321 @bottle.route(url_base
+ '/<tenant_id>/images', method
='POST')
1322 def http_post_images(tenant_id
):
1323 '''insert a image into the database, and attach to tenant.'''
1324 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1325 #check valid tenant_id
1326 result
,content
= check_valid_tenant(my
, tenant_id
)
1328 bottle
.abort(result
, content
)
1329 http_content
= format_in(image_new_schema
)
1330 r
= remove_extra_items(http_content
, image_new_schema
)
1331 if r
is not None: print "http_post_images: Warning: remove extra items ", r
1332 change_keys_http2db(http_content
['image'], http2db_image
)
1333 metadata_dict
= http_content
['image'].pop('metadata', None)
1334 if metadata_dict
is not None:
1335 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1338 image_file
= http_content
['image'].get('path',None)
1339 parsed_url
= urlparse
.urlparse(image_file
)
1340 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "":
1341 # The path is a local file
1342 if os
.path
.exists(image_file
):
1343 http_content
['image']['checksum'] = md5(image_file
)
1345 # The path is a URL. Code should be added to download the image and calculate the checksum
1346 #http_content['image']['checksum'] = md5(downloaded_image)
1348 # Finally, only if we are in test mode and checksum has not been calculated, we calculate it from the path
1349 host_test_mode
= True if config_dic
['mode']=='test' or config_dic
['mode']=="OF only" else False
1351 if 'checksum' not in http_content
['image']:
1352 http_content
['image']['checksum'] = md5_string(image_file
)
1354 # At this point, if the path is a local file and no chechsum has been obtained yet, an error is sent back.
1355 # If it is a URL, no error is sent. Checksum will be an empty string
1356 if parsed_url
.scheme
== "" and parsed_url
.netloc
== "" and 'checksum' not in http_content
['image']:
1357 content
= "Image file not found"
1358 print "http_post_images error: %d %s" % (HTTP_Bad_Request
, content
)
1359 bottle
.abort(HTTP_Bad_Request
, content
)
1360 except Exception as e
:
1361 print "ERROR. Unexpected exception: %s" % (str(e
))
1362 bottle
.abort(HTTP_Internal_Server_Error
, type(e
).__name
__ + ": " + str(e
))
1363 #insert in data base
1364 result
, content
= my
.db
.new_image(http_content
['image'], tenant_id
)
1366 return http_get_image_id(tenant_id
, content
)
1368 print "http_post_images error %d %s" % (result
, content
)
1369 bottle
.abort(-result
, content
)
1372 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='DELETE')
1373 def http_delete_image_id(tenant_id
, image_id
):
1374 '''Deletes the image_id of a tenant. IT removes from tenants_images table.'''
1375 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1376 #check valid tenant_id
1377 result
,content
= check_valid_tenant(my
, tenant_id
)
1379 bottle
.abort(result
, content
)
1380 result
, content
= my
.db
.delete_image_flavor('image', image_id
, tenant_id
)
1382 bottle
.abort(HTTP_Not_Found
, content
)
1384 data
={'result' : content
}
1385 return format_out(data
)
1387 print "http_delete_image_id error",result
, content
1388 bottle
.abort(-result
, content
)
1391 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>/<action>', method
='POST')
1392 def http_attach_detach_images(tenant_id
, image_id
, action
):
1393 '''attach/detach an existing image in this tenant. That is insert/remove at tenants_images table.'''
1394 #TODO alf: not tested at all!!!
1395 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1396 #check valid tenant_id
1397 result
,content
= check_valid_tenant(my
, tenant_id
)
1399 bottle
.abort(result
, content
)
1400 if tenant_id
=='any':
1401 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1403 if action
!='attach' and action
!= 'detach':
1404 bottle
.abort(HTTP_Method_Not_Allowed
, "actions can be attach or detach")
1407 #Ensure that image exist
1408 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1409 where_
={'uuid': image_id
}
1410 result
, content
= my
.db
.get_table(SELECT
=('public','tenant_id'), FROM
=from_
, WHERE
=where_
)
1412 if action
=='attach':
1413 text_error
="Image '%s' not found" % image_id
1415 text_error
="Image '%s' not found for tenant '%s'" % (image_id
, tenant_id
)
1416 bottle
.abort(HTTP_Not_Found
, text_error
)
1420 if action
=='attach':
1421 if image
['tenant_id']!=None:
1422 bottle
.abort(HTTP_Conflict
, "Image '%s' already attached to tenant '%s'" % (image_id
, tenant_id
))
1423 if image
['public']=='no' and not my
.admin
:
1424 #allow only attaching public images
1425 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to attach a private image")
1427 #insert in data base
1428 result
, content
= my
.db
.new_row('tenants_images', {'image_id':image_id
, 'tenant_id': tenant_id
})
1430 return http_get_image_id(tenant_id
, image_id
)
1432 if image
['tenant_id']==None:
1433 bottle
.abort(HTTP_Not_Found
, "Image '%s' not attached to tenant '%s'" % (image_id
, tenant_id
))
1434 result
, content
= my
.db
.delete_row_by_dict(FROM
='tenants_images', WHERE
={'image_id':image_id
, 'tenant_id':tenant_id
})
1436 if image
['public']=='no':
1437 #try to delete the image completely to avoid orphan images, IGNORE error
1438 my
.db
.delete_row_by_dict(FROM
='images', WHERE
={'uuid':image_id
})
1439 data
={'result' : "image detached"}
1440 return format_out(data
)
1442 #if get here is because an error
1443 print "http_attach_detach_images error %d %s" % (result
, content
)
1444 bottle
.abort(-result
, content
)
1447 @bottle.route(url_base
+ '/<tenant_id>/images/<image_id>', method
='PUT')
1448 def http_put_image_id(tenant_id
, image_id
):
1449 '''update a image_id into the database.'''
1450 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1451 #check valid tenant_id
1452 result
,content
= check_valid_tenant(my
, tenant_id
)
1454 bottle
.abort(result
, content
)
1456 http_content
= format_in( image_update_schema
)
1457 r
= remove_extra_items(http_content
, image_update_schema
)
1458 if r
is not None: print "http_put_image_id: Warning: remove extra items ", r
1459 change_keys_http2db(http_content
['image'], http2db_image
)
1460 metadata_dict
= http_content
['image'].pop('metadata', None)
1461 if metadata_dict
is not None:
1462 http_content
['image']['metadata'] = json
.dumps(metadata_dict
)
1463 #Ensure that image exist
1464 where_
={'uuid': image_id
}
1465 if tenant_id
=='any':
1469 from_
='tenants_images as ti right join images as i on ti.image_id=i.uuid'
1470 where_or_
= {'tenant_id': tenant_id
, 'public': 'yes'}
1471 result
, content
= my
.db
.get_table(SELECT
=('public',), DISTINCT
=True, FROM
=from_
, WHERE
=where_
, WHERE_OR
=where_or_
, WHERE_AND_OR
="AND")
1473 text_error
="Image '%s' not found" % image_id
1474 if tenant_id
!='any':
1475 text_error
+=" for tenant '%s'" % image_id
1476 bottle
.abort(HTTP_Not_Found
, text_error
)
1479 if content
[0]['public']=='yes' and not my
.admin
:
1480 #allow only modifications over private images
1481 bottle
.abort(HTTP_Unauthorized
, "Needed admin rights to edit a public image")
1483 #insert in data base
1484 result
, content
= my
.db
.update_rows('images', http_content
['image'], {'uuid': image_id
})
1487 print "http_put_image_id error %d %s" % (result
, content
)
1488 bottle
.abort(-result
, content
)
1491 return http_get_image_id(tenant_id
, image_id
)
1498 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='GET')
1499 def http_get_servers(tenant_id
):
1500 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1501 result
,content
= check_valid_tenant(my
, tenant_id
)
1503 bottle
.abort(result
, content
)
1506 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_server
,
1507 ('id','name','description','hostId','imageRef','flavorRef','status', 'tenant_id') )
1508 if tenant_id
!='any':
1509 where_
['tenant_id'] = tenant_id
1510 result
, content
= my
.db
.get_table(SELECT
=select_
, FROM
='instances', WHERE
=where_
, LIMIT
=limit_
)
1512 print "http_get_servers Error", content
1513 bottle
.abort(-result
, content
)
1515 change_keys_http2db(content
, http2db_server
, reverse
=True)
1517 tenant_id
= row
.pop('tenant_id')
1518 row
['links']=[ {'href': "/".join( (my
.url_preffix
, tenant_id
, 'servers', str(row
['id']) ) ), 'rel':'bookmark' } ]
1519 data
={'servers' : content
}
1520 return format_out(data
)
1522 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='GET')
1523 def http_get_server_id(tenant_id
, server_id
):
1524 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1525 #check valid tenant_id
1526 result
,content
= check_valid_tenant(my
, tenant_id
)
1528 bottle
.abort(result
, content
)
1531 result
, content
= my
.db
.get_instance(server_id
)
1533 bottle
.abort(HTTP_Not_Found
, content
)
1535 #change image/flavor-id to id and link
1536 convert_bandwidth(content
, reverse
=True)
1537 convert_datetime2str(content
)
1538 if content
["ram"]==0 : del content
["ram"]
1539 if content
["vcpus"]==0 : del content
["vcpus"]
1540 if 'flavor_id' in content
:
1541 if content
['flavor_id'] is not None:
1542 content
['flavor'] = {'id':content
['flavor_id'],
1543 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'flavors', str(content
['flavor_id']) ) ), 'rel':'bookmark'}]
1545 del content
['flavor_id']
1546 if 'image_id' in content
:
1547 if content
['image_id'] is not None:
1548 content
['image'] = {'id':content
['image_id'],
1549 'links':[{'href': "/".join( (my
.url_preffix
, content
['tenant_id'], 'images', str(content
['image_id']) ) ), 'rel':'bookmark'}]
1551 del content
['image_id']
1552 change_keys_http2db(content
, http2db_server
, reverse
=True)
1553 if 'extended' in content
:
1554 if 'devices' in content
['extended']: change_keys_http2db(content
['extended']['devices'], http2db_server
, reverse
=True)
1556 data
={'server' : content
}
1557 return format_out(data
)
1559 bottle
.abort(-result
, content
)
1562 @bottle.route(url_base
+ '/<tenant_id>/servers', method
='POST')
1563 def http_post_server_id(tenant_id
):
1564 '''deploys a new server'''
1565 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1566 #check valid tenant_id
1567 result
,content
= check_valid_tenant(my
, tenant_id
)
1569 bottle
.abort(result
, content
)
1571 if tenant_id
=='any':
1572 bottle
.abort(HTTP_Bad_Request
, "Invalid tenant 'any' with this command")
1574 http_content
= format_in( server_new_schema
)
1575 r
= remove_extra_items(http_content
, server_new_schema
)
1576 if r
is not None: print "http_post_serves: Warning: remove extra items ", r
1577 change_keys_http2db(http_content
['server'], http2db_server
)
1578 extended_dict
= http_content
['server'].get('extended', None)
1579 if extended_dict
is not None:
1580 result
, content
= check_extended(extended_dict
, True)
1582 print "http_post_servers wrong input extended error %d %s" % (result
, content
)
1583 bottle
.abort(-result
, content
)
1585 convert_bandwidth(extended_dict
)
1586 if 'devices' in extended_dict
: change_keys_http2db(extended_dict
['devices'], http2db_server
)
1588 server
= http_content
['server']
1589 server_start
= server
.get('start', 'yes')
1590 server
['tenant_id'] = tenant_id
1591 #check flavor valid and take info
1592 result
, content
= my
.db
.get_table(FROM
='tenants_flavors as tf join flavors as f on tf.flavor_id=f.uuid',
1593 SELECT
=('ram','vcpus','extended'), WHERE
={'uuid':server
['flavor_id'], 'tenant_id':tenant_id
})
1595 bottle
.abort(HTTP_Not_Found
, 'flavor_id %s not found' % server
['flavor_id'])
1597 server
['flavor']=content
[0]
1598 #check image valid and take info
1599 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1600 SELECT
=('path', 'metadata', 'image_id'),
1601 WHERE
={'uuid':server
['image_id'], "status":"ACTIVE"},
1602 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'},
1606 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % server
['image_id'])
1608 for image_dict
in content
:
1609 if image_dict
.get("image_id"):
1612 # insert in data base tenants_images
1613 r2
, c2
= my
.db
.new_row('tenants_images', {'image_id': server
['image_id'], 'tenant_id': tenant_id
})
1615 bottle
.abort(HTTP_Not_Found
, 'image_id %s cannot be used. Error %s' % (server
['image_id'], c2
))
1617 server
['image']={"path": content
[0]["path"], "metadata": content
[0]["metadata"]}
1618 if "hosts_id" in server
:
1619 result
, content
= my
.db
.get_table(FROM
='hosts', SELECT
=('uuid',), WHERE
={'uuid': server
['host_id']})
1621 bottle
.abort(HTTP_Not_Found
, 'hostId %s not found' % server
['host_id'])
1623 #print json.dumps(server, indent=4)
1625 result
, content
= ht
.create_server(server
, config_dic
['db'], config_dic
['mode']=='normal')
1628 #Insert instance to database
1631 print "inserting at DB"
1633 if server_start
== 'no':
1634 content
['status'] = 'INACTIVE'
1636 for net
in http_content
['server']['networks']:
1637 if net
['type'] == 'instance:ovs':
1638 dhcp_nets_id
.append(get_network_id(net
['net_id']))
1641 new_instance_result
, new_instance
= my
.db
.new_instance(content
, nets
, ports_to_free
)
1642 if new_instance_result
< 0:
1643 print "Error http_post_servers() :", new_instance_result
, new_instance
1644 bottle
.abort(-new_instance_result
, new_instance
)
1647 print "inserted at DB"
1651 for port
in ports_to_free
:
1652 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1654 print ' http_post_servers ERROR RESTORE IFACE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1658 my
.ovim
.net_update_ofc_thread(net_id
)
1659 except ovim
.ovimException
as e
:
1660 my
.logger
.error("http_post_servers, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1662 # look for dhcp ip address
1663 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "ip_address", "net_id"], WHERE
={"instance_id": new_instance
})
1666 if config_dic
.get("dhcp_server") and iface
["net_id"] in config_dic
["dhcp_nets"]:
1667 #print "dhcp insert add task"
1668 r
,c
= config_dic
['dhcp_thread'].insert_task("add", iface
["mac"])
1670 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1672 #ensure compute contain the bridge for ovs networks:
1673 if iface
.get("net_id"):
1674 server_net
= get_network_id(iface
['net_id'])
1675 if server_net
["network"].get('provider:physical', "")[:3] == 'OVS':
1676 vlan
= str(server_net
['network']['provider:vlan'])
1677 dhcp_enable
= bool(server_net
['network']['enable_dhcp'])
1678 vm_dhcp_ip
= c2
[0]["ip_address"]
1679 config_dic
['host_threads'][server
['host_id']].insert_task("create-ovs-bridge-port", vlan
)
1680 dns
= server_net
['network'].get("dns")
1682 dns
= yaml
.safe_load(server_net
['network'].get("dns"))
1683 routes
= server_net
['network'].get("routes")
1685 routes
= yaml
.safe_load(server_net
['network'].get("routes"))
1686 links
= server_net
['network'].get("links")
1688 links
= yaml
.safe_load(server_net
['network'].get("links"))
1690 dhcp_firt_ip
= str(server_net
['network']['dhcp_first_ip'])
1691 dhcp_last_ip
= str(server_net
['network']['dhcp_last_ip'])
1692 dhcp_cidr
= str(server_net
['network']['cidr'])
1693 gateway
= str(server_net
['network']['gateway_ip'])
1695 http_controller
= config_dic
['http_threads'][threading
.current_thread().name
]
1696 http_controller
.ovim
.launch_dhcp_server(vlan
, dhcp_firt_ip
, dhcp_last_ip
,
1697 dhcp_cidr
, gateway
, dns
, routes
)
1698 set_mac_dhcp(vm_dhcp_ip
, vlan
, dhcp_firt_ip
, dhcp_last_ip
, dhcp_cidr
, c2
[0]['mac'])
1701 http_controller
.ovim
.launch_link_bridge_to_ovs(vlan
, gateway
, dhcp_cidr
, links
, routes
)
1705 server
['uuid'] = new_instance
1706 server_start
= server
.get('start', 'yes')
1708 if server_start
!= 'no':
1709 server
['paused'] = True if server_start
== 'paused' else False
1710 server
['action'] = {"start":None}
1711 server
['status'] = "CREATING"
1713 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1715 my
.db
.update_rows('instances', {'status':"ERROR"}, {'uuid':server
['uuid'], 'last_error':c
}, log
=True)
1717 return http_get_server_id(tenant_id
, new_instance
)
1719 bottle
.abort(HTTP_Bad_Request
, content
)
1723 def http_server_action(server_id
, tenant_id
, action
):
1724 '''Perform actions over a server as resume, reboot, terminate, ...'''
1725 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1726 server
={"uuid": server_id
, "action":action
}
1727 where
={'uuid': server_id
}
1728 if tenant_id
!='any':
1729 where
['tenant_id']= tenant_id
1730 result
, content
= my
.db
.get_table(FROM
='instances', WHERE
=where
)
1732 bottle
.abort(HTTP_Not_Found
, "server %s not found" % server_id
)
1735 print "http_post_server_action error getting data %d %s" % (result
, content
)
1736 bottle
.abort(HTTP_Internal_Server_Error
, content
)
1738 server
.update(content
[0])
1739 tenant_id
= server
["tenant_id"]
1741 #TODO check a right content
1743 if 'terminate' in action
:
1744 new_status
='DELETING'
1745 elif server
['status'] == 'ERROR': #or server['status'] == 'CREATING':
1746 if 'terminate' not in action
and 'rebuild' not in action
:
1747 bottle
.abort(HTTP_Method_Not_Allowed
, "Server is in ERROR status, must be rebuit or deleted ")
1749 # elif server['status'] == 'INACTIVE':
1750 # if 'start' not in action and 'createImage' not in action:
1751 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'INACTIVE' status is 'start'")
1753 # if 'start' in action:
1754 # new_status='CREATING'
1755 # server['paused']='no'
1756 # elif server['status'] == 'PAUSED':
1757 # if 'resume' not in action:
1758 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'PAUSED' status is 'resume'")
1760 # elif server['status'] == 'ACTIVE':
1761 # if 'pause' not in action and 'reboot'not in action and 'shutoff'not in action:
1762 # bottle.abort(HTTP_Method_Not_Allowed, "The only possible action over an instance in 'ACTIVE' status is 'pause','reboot' or 'shutoff'")
1765 if 'start' in action
or 'createImage' in action
or 'rebuild' in action
:
1766 #check image valid and take info
1767 image_id
= server
['image_id']
1768 if 'createImage' in action
:
1769 if 'imageRef' in action
['createImage']:
1770 image_id
= action
['createImage']['imageRef']
1771 elif 'disk' in action
['createImage']:
1772 result
, content
= my
.db
.get_table(FROM
='instance_devices',
1773 SELECT
=('image_id','dev'), WHERE
={'instance_id':server
['uuid'],"type":"disk"})
1775 bottle
.abort(HTTP_Not_Found
, 'disk not found for server')
1779 if action
['createImage']['imageRef']['disk'] != None:
1780 for disk
in content
:
1781 if disk
['dev'] == action
['createImage']['imageRef']['disk']:
1782 disk_id
= disk
['image_id']
1785 bottle
.abort(HTTP_Not_Found
, 'disk %s not found for server' % action
['createImage']['imageRef']['disk'])
1788 bottle
.abort(HTTP_Not_Found
, 'more than one disk found for server' )
1792 image_id
= content
[0]['image_id']
1794 result
, content
= my
.db
.get_table(FROM
='tenants_images as ti right join images as i on ti.image_id=i.uuid',
1795 SELECT
=('path','metadata'), WHERE
={'uuid':image_id
, "status":"ACTIVE"},
1796 WHERE_OR
={'tenant_id':tenant_id
, 'public': 'yes'}, WHERE_AND_OR
="AND", DISTINCT
=True)
1798 bottle
.abort(HTTP_Not_Found
, 'image_id %s not found or not ACTIVE' % image_id
)
1800 if content
[0]['metadata'] is not None:
1802 metadata
= json
.loads(content
[0]['metadata'])
1804 return -HTTP_Internal_Server_Error
, "Can not decode image metadata"
1805 content
[0]['metadata']=metadata
1807 content
[0]['metadata'] = {}
1808 server
['image']=content
[0]
1809 if 'createImage' in action
:
1810 action
['createImage']['source'] = {'image_id': image_id
, 'path': content
[0]['path']}
1811 if 'createImage' in action
:
1812 #Create an entry in Database for the new image
1813 new_image
={'status':'BUILD', 'progress': 0 }
1814 new_image_metadata
=content
[0]
1815 if 'metadata' in server
['image'] and server
['image']['metadata'] != None:
1816 new_image_metadata
.update(server
['image']['metadata'])
1817 new_image_metadata
= {"use_incremental":"no"}
1818 if 'metadata' in action
['createImage']:
1819 new_image_metadata
.update(action
['createImage']['metadata'])
1820 new_image
['metadata'] = json
.dumps(new_image_metadata
)
1821 new_image
['name'] = action
['createImage'].get('name', None)
1822 new_image
['description'] = action
['createImage'].get('description', None)
1823 new_image
['uuid']=my
.db
.new_uuid()
1824 if 'path' in action
['createImage']:
1825 new_image
['path'] = action
['createImage']['path']
1827 new_image
['path']="/provisional/path/" + new_image
['uuid']
1828 result
, image_uuid
= my
.db
.new_image(new_image
, tenant_id
)
1830 bottle
.abort(HTTP_Bad_Request
, 'Error: ' + image_uuid
)
1832 server
['new_image'] = new_image
1836 r
,c
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'instance',server
)
1838 print "Task queue full at host ", server
['host_id']
1839 bottle
.abort(HTTP_Request_Timeout
, c
)
1840 if 'createImage' in action
and result
>= 0:
1841 return http_get_image_id(tenant_id
, image_uuid
)
1843 #Update DB only for CREATING or DELETING status
1844 data
={'result' : 'deleting in process'}
1846 if new_status
!= None and new_status
== 'DELETING':
1851 #look for dhcp ip address
1852 r2
, c2
= my
.db
.get_table(FROM
="ports", SELECT
=["mac", "net_id"], WHERE
={"instance_id": server_id
})
1853 r
, c
= my
.db
.delete_instance(server_id
, tenant_id
, nets
, ports_to_free
, net_ovs_list
, "requested by http")
1854 for port
in ports_to_free
:
1855 r1
,c1
= config_dic
['host_threads'][ server
['host_id'] ].insert_task( 'restore-iface',*port
)
1857 my
.logger
.error("http_post_server_action server deletion ERROR at resore-iface!!!! " + c1
)
1858 warn_text
+= "; Error iface '{}' cannot be restored '{}'".format(str(port
), str(e
))
1861 my
.ovim
.net_update_ofc_thread(net_id
)
1862 except ovim
.ovimException
as e
:
1863 my
.logger
.error("http_server_action, Error updating network with id '{}', '{}'".format(net_id
, str(e
)))
1864 warn_text
+= "; Error openflow rules of network '{}' cannot be restore '{}'".format(net_id
, str (e
))
1866 # look for dhcp ip address
1867 if r2
>0 and config_dic
.get("dhcp_server"):
1869 if iface
["net_id"] in config_dic
["dhcp_nets"]:
1870 r
,c
= config_dic
['dhcp_thread'].insert_task("del", iface
["mac"])
1871 #print "dhcp insert del task"
1873 print ':http_post_servers ERROR UPDATING dhcp_server !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!' + c
1874 # delete ovs-port and linux bridge, contains a list of tuple (net_id,vlan, vm_ip, mac)
1876 for net
in net_ovs_list
:
1882 delete_dhcp_ovs_bridge(vlan
, net_id
)
1883 delete_mac_dhcp(vm_ip
, vlan
, mac
)
1885 net_data
= my
.ovim
.show_network(net_id
)
1886 if net_data
.get('links'):
1887 links
= yaml
.load(net_data
.get('links'))
1888 my
.ovim
.delete_link_bridge_to_ovs(vlan
, links
)
1890 config_dic
['host_threads'][server
['host_id']].insert_task('del-ovs-port', vlan
, net_id
)
1892 data
["result"] += warn_text
1893 return format_out(data
)
1897 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>', method
='DELETE')
1898 def http_delete_server_id(tenant_id
, server_id
):
1899 '''delete a server'''
1900 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1901 #check valid tenant_id
1902 result
,content
= check_valid_tenant(my
, tenant_id
)
1904 bottle
.abort(result
, content
)
1907 return http_server_action(server_id
, tenant_id
, {"terminate":None} )
1910 @bottle.route(url_base
+ '/<tenant_id>/servers/<server_id>/action', method
='POST')
1911 def http_post_server_action(tenant_id
, server_id
):
1912 '''take an action over a server'''
1913 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
1914 #check valid tenant_id
1915 result
,content
= check_valid_tenant(my
, tenant_id
)
1917 bottle
.abort(result
, content
)
1919 http_content
= format_in( server_action_schema
)
1920 #r = remove_extra_items(http_content, server_action_schema)
1921 #if r is not None: print "http_post_server_action: Warning: remove extra items ", r
1923 return http_server_action(server_id
, tenant_id
, http_content
)
1930 @bottle.route(url_base
+ '/networks', method
='GET')
1931 def http_get_networks():
1933 Get all networks available
1936 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1940 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_network
,
1941 ('id', 'name', 'tenant_id', 'type',
1942 'shared', 'provider:vlan', 'status', 'last_error',
1943 'admin_state_up', 'provider:physical'))
1944 if "tenant_id" in where_
:
1945 del where_
["tenant_id"]
1947 content
= my
.ovim
.get_networks(select_
, where_
, limit_
)
1949 delete_nulls(content
)
1950 change_keys_http2db(content
, http2db_network
, reverse
=True)
1951 data
= {'networks': content
}
1952 return format_out(data
)
1954 except ovim
.ovimException
as e
:
1955 my
.logger
.error(str(e
), exc_info
=True)
1956 bottle
.abort(e
.http_code
, str(e
))
1957 except Exception as e
:
1958 my
.logger
.error(str(e
), exc_info
=True)
1959 bottle
.abort(HTTP_Bad_Request
, str(e
))
1962 @bottle.route(url_base
+ '/networks/<network_id>', method
='GET')
1963 def http_get_network_id(network_id
):
1965 Get a network data by id
1969 data
= get_network_id(network_id
)
1970 return format_out(data
)
1973 def get_network_id(network_id
):
1975 Get network from DB by id
1976 :param network_id: network Id
1979 my
= config_dic
['http_threads'][threading
.current_thread().name
]
1983 where_
= bottle
.request
.query
1984 content
= my
.ovim
.show_network(network_id
, where_
)
1986 change_keys_http2db(content
, http2db_network
, reverse
=True)
1987 delete_nulls(content
)
1988 data
= {'network': content
}
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', method
='POST')
1999 def http_post_networks():
2001 Insert a network into the database.
2004 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2008 http_content
= format_in(network_new_schema
)
2009 r
= remove_extra_items(http_content
, network_new_schema
)
2011 print "http_post_networks: Warning: remove extra items ", r
2012 change_keys_http2db(http_content
['network'], http2db_network
)
2013 network
= http_content
['network']
2014 content
= my
.ovim
.new_network(network
)
2015 return format_out(get_network_id(content
))
2016 except ovim
.ovimException
as e
:
2017 my
.logger
.error(str(e
), exc_info
=True)
2018 bottle
.abort(e
.http_code
, str(e
))
2019 except Exception as e
:
2020 my
.logger
.error(str(e
), exc_info
=True)
2021 bottle
.abort(HTTP_Bad_Request
, str(e
))
2024 @bottle.route(url_base
+ '/networks/<network_id>', method
='PUT')
2025 def http_put_network_id(network_id
):
2027 Update a network_id into DB.
2028 :param network_id: network id
2031 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2035 http_content
= format_in(network_update_schema
)
2036 change_keys_http2db(http_content
['network'], http2db_network
)
2037 network
= http_content
['network']
2038 return format_out(my
.ovim
.edit_network(network_id
, network
))
2040 except ovim
.ovimException
as e
:
2041 my
.logger
.error(str(e
), exc_info
=True)
2042 bottle
.abort(e
.http_code
, str(e
))
2043 except Exception as e
:
2044 my
.logger
.error(str(e
), exc_info
=True)
2045 bottle
.abort(HTTP_Bad_Request
, str(e
))
2048 @bottle.route(url_base
+ '/networks/<network_id>', method
='DELETE')
2049 def http_delete_network_id(network_id
):
2051 Delete a network_id from the database.
2052 :param network_id: Network id
2055 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2058 # delete from the data base
2059 content
= my
.ovim
.delete_network(network_id
)
2060 data
= {'result': content
}
2061 return format_out(data
)
2063 except ovim
.ovimException
as e
:
2064 my
.logger
.error(str(e
), exc_info
=True)
2065 bottle
.abort(e
.http_code
, str(e
))
2066 except Exception as e
:
2067 my
.logger
.error(str(e
), exc_info
=True)
2068 bottle
.abort(HTTP_Bad_Request
, str(e
))
2075 @bottle.route(url_base
+ '/openflow/controller', method
='GET')
2076 def http_get_openflow_controller():
2078 Retrieve a openflow controllers list from DB.
2081 # TODO check if show a proper list
2082 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2085 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_ofc
,
2086 ('id', 'name', 'dpid', 'ip', 'port', 'type',
2087 'version', 'user', 'password'))
2089 content
= my
.ovim
.get_of_controllers(select_
, where_
)
2090 delete_nulls(content
)
2091 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2092 data
= {'ofcs': content
}
2093 return format_out(data
)
2094 except ovim
.ovimException
as e
:
2095 my
.logger
.error(str(e
), exc_info
=True)
2096 bottle
.abort(e
.http_code
, str(e
))
2097 except Exception as e
:
2098 my
.logger
.error(str(e
), exc_info
=True)
2099 bottle
.abort(HTTP_Bad_Request
, str(e
))
2102 @bottle.route(url_base
+ '/openflow/controller/<uuid>', method
='GET')
2103 def http_get_openflow_controller_id(uuid
):
2105 Get an openflow controller by dpid from DB.get_of_controllers
2107 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2111 content
= my
.ovim
.show_of_controller(uuid
)
2112 delete_nulls(content
)
2113 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2114 data
= {'ofc': content
}
2115 return format_out(data
)
2116 except ovim
.ovimException
as e
:
2117 my
.logger
.error(str(e
), exc_info
=True)
2118 bottle
.abort(e
.http_code
, str(e
))
2119 except Exception as e
:
2120 my
.logger
.error(str(e
), exc_info
=True)
2121 bottle
.abort(HTTP_Bad_Request
, str(e
))
2124 @bottle.route(url_base
+ '/openflow/controller/', method
='POST')
2125 def http_post_openflow_controller():
2127 Create a new openflow controller into DB
2130 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2133 http_content
= format_in(openflow_controller_schema
)
2134 of_c
= http_content
['ofc']
2135 uuid
= my
.ovim
.new_of_controller(of_c
)
2136 content
= my
.ovim
.show_of_controller(uuid
)
2137 delete_nulls(content
)
2138 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2139 data
= {'ofc': content
}
2140 return format_out(data
)
2141 except ovim
.ovimException
as e
:
2142 my
.logger
.error(str(e
), exc_info
=True)
2143 bottle
.abort(e
.http_code
, str(e
))
2144 except Exception as e
:
2145 my
.logger
.error(str(e
), exc_info
=True)
2146 bottle
.abort(HTTP_Bad_Request
, str(e
))
2149 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='PUT')
2150 def http_put_openflow_controller_by_id(of_controller_id
):
2152 Create an openflow controller into DB
2153 :param of_controller_id: openflow controller dpid
2156 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2159 http_content
= format_in(openflow_controller_schema
)
2160 of_c
= http_content
['ofc']
2162 content
= my
.ovim
.edit_of_controller(of_controller_id
, of_c
)
2163 delete_nulls(content
)
2164 change_keys_http2db(content
, http2db_ofc
, reverse
=True)
2165 data
= {'ofc': content
}
2166 return format_out(data
)
2167 except ovim
.ovimException
as e
:
2168 my
.logger
.error(str(e
), exc_info
=True)
2169 bottle
.abort(e
.http_code
, str(e
))
2170 except Exception as e
:
2171 my
.logger
.error(str(e
), exc_info
=True)
2172 bottle
.abort(HTTP_Bad_Request
, str(e
))
2175 @bottle.route(url_base
+ '/openflow/controller/<of_controller_id>', method
='DELETE')
2176 def http_delete_openflow_controller(of_controller_id
):
2178 Delete an openflow controller from DB.
2179 :param of_controller_id: openflow controller dpid
2182 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2185 content
= my
.ovim
.delete_of_controller(of_controller_id
)
2186 data
= {'result': content
}
2187 return format_out(data
)
2188 except ovim
.ovimException
as e
:
2189 my
.logger
.error(str(e
), exc_info
=True)
2190 bottle
.abort(e
.http_code
, str(e
))
2191 except Exception as e
:
2192 my
.logger
.error(str(e
), exc_info
=True)
2193 bottle
.abort(HTTP_Bad_Request
, str(e
))
2196 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='GET')
2197 def http_get_openflow_id(network_id
):
2199 To obtain the list of openflow rules of a network
2200 :param network_id: network id
2203 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2206 if network_id
== 'all':
2209 content
= my
.ovim
.get_openflow_rules(network_id
)
2210 data
= {'openflow-rules': content
}
2211 except ovim
.ovimException
as e
:
2212 my
.logger
.error(str(e
), exc_info
=True)
2213 bottle
.abort(e
.http_code
, str(e
))
2214 except Exception as e
:
2215 my
.logger
.error(str(e
), exc_info
=True)
2216 bottle
.abort(HTTP_Bad_Request
, str(e
))
2218 return format_out(data
)
2221 @bottle.route(url_base
+ '/networks/<network_id>/openflow', method
='PUT')
2222 def http_put_openflow_id(network_id
):
2224 To make actions over the net. The action is to reinstall the openflow rules
2225 network_id can be 'all'
2226 :param network_id: network id
2229 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2232 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2234 if network_id
== 'all':
2238 result
= my
.ovim
.edit_openflow_rules(network_id
)
2239 except ovim
.ovimException
as e
:
2240 my
.logger
.error(str(e
), exc_info
=True)
2241 bottle
.abort(e
.http_code
, str(e
))
2242 except Exception as e
:
2243 my
.logger
.error(str(e
), exc_info
=True)
2244 bottle
.abort(HTTP_Bad_Request
, str(e
))
2246 data
= {'result': str(result
) + " nets updates"}
2247 return format_out(data
)
2249 @bottle.route(url_base
+ '/networks/clear/openflow/<ofc_id>', method
='DELETE')
2250 @bottle.route(url_base
+ '/networks/clear/openflow', method
='DELETE')
2251 def http_clear_openflow_rules(ofc_id
=None):
2253 To make actions over the net. The action is to delete ALL openflow rules
2256 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2259 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2261 my
.ovim
.delete_openflow_rules(ofc_id
)
2262 except ovim
.ovimException
as e
:
2263 my
.logger
.error(str(e
), exc_info
=True)
2264 bottle
.abort(e
.http_code
, str(e
))
2265 except Exception as e
:
2266 my
.logger
.error(str(e
), exc_info
=True)
2267 bottle
.abort(HTTP_Bad_Request
, str(e
))
2269 data
= {'result': " Clearing openflow rules in process"}
2270 return format_out(data
)
2272 @bottle.route(url_base
+ '/networks/openflow/ports/<ofc_id>', method
='GET')
2273 @bottle.route(url_base
+ '/networks/openflow/ports', method
='GET')
2274 def http_get_openflow_ports(ofc_id
=None):
2276 Obtain switch ports names of openflow controller
2279 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2282 ports
= my
.ovim
.get_openflow_ports(ofc_id
)
2283 data
= {'ports': ports
}
2284 except ovim
.ovimException
as e
:
2285 my
.logger
.error(str(e
), exc_info
=True)
2286 bottle
.abort(e
.http_code
, str(e
))
2287 except Exception as e
:
2288 my
.logger
.error(str(e
), exc_info
=True)
2289 bottle
.abort(HTTP_Bad_Request
, str(e
))
2291 return format_out(data
)
2297 @bottle.route(url_base
+ '/ports', method
='GET')
2298 def http_get_ports():
2300 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2301 select_
,where_
,limit_
= filter_query_string(bottle
.request
.query
, http2db_port
,
2302 ('id','name','tenant_id','network_id','vpci','mac_address','device_owner','device_id',
2303 'binding:switch_port','binding:vlan','bandwidth','status','admin_state_up','ip_address') )
2305 ports
= my
.ovim
.get_ports(columns
=select_
, filter=where_
, limit
=limit_
)
2307 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2308 data
={'ports' : ports
}
2309 return format_out(data
)
2310 except ovim
.ovimException
as e
:
2311 my
.logger
.error(str(e
), exc_info
=True)
2312 bottle
.abort(e
.http_code
, str(e
))
2313 except Exception as e
:
2314 my
.logger
.error(str(e
), exc_info
=True)
2315 bottle
.abort(HTTP_Bad_Request
, str(e
))
2317 @bottle.route(url_base
+ '/ports/<port_id>', method
='GET')
2318 def http_get_port_id(port_id
):
2319 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2321 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2323 bottle
.abort(HTTP_Not_Found
, 'port %s not found' % port_id
)
2326 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2327 data
= {'port': ports
[0]}
2328 return format_out(data
)
2329 except ovim
.ovimException
as e
:
2330 my
.logger
.error(str(e
), exc_info
=True)
2331 bottle
.abort(e
.http_code
, str(e
))
2332 except Exception as e
:
2333 my
.logger
.error(str(e
), exc_info
=True)
2334 bottle
.abort(HTTP_Bad_Request
, str(e
))
2336 @bottle.route(url_base
+ '/ports', method
='POST')
2337 def http_post_ports():
2338 '''insert an external port into the database.'''
2339 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2341 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2343 http_content
= format_in( port_new_schema
)
2344 r
= remove_extra_items(http_content
, port_new_schema
)
2345 if r
is not None: print "http_post_ports: Warning: remove extra items ", r
2346 change_keys_http2db(http_content
['port'], http2db_port
)
2347 port
=http_content
['port']
2349 port_id
= my
.ovim
.new_port(port
)
2350 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2352 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' inserted but not found at database".format(port_id
))
2355 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2356 data
= {'port': ports
[0]}
2357 return format_out(data
)
2358 except ovim
.ovimException
as e
:
2359 my
.logger
.error(str(e
), exc_info
=True)
2360 bottle
.abort(e
.http_code
, str(e
))
2361 except Exception as e
:
2362 my
.logger
.error(str(e
), exc_info
=True)
2363 bottle
.abort(HTTP_Bad_Request
, str(e
))
2365 @bottle.route(url_base
+ '/ports/<port_id>', method
='PUT')
2366 def http_put_port_id(port_id
):
2367 '''update a port_id into the database.'''
2368 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2370 http_content
= format_in( port_update_schema
)
2371 change_keys_http2db(http_content
['port'], http2db_port
)
2372 port_dict
=http_content
['port']
2374 for k
in ('vlan', 'switch_port', 'mac_address', 'tenant_id'):
2375 if k
in port_dict
and not my
.admin
:
2376 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges for changing " + k
)
2379 port_id
= my
.ovim
.edit_port(port_id
, port_dict
, my
.admin
)
2380 ports
= my
.ovim
.get_ports(filter={"uuid": port_id
})
2382 bottle
.abort(HTTP_Internal_Server_Error
, "port '{}' edited but not found at database".format(port_id
))
2385 change_keys_http2db(ports
, http2db_port
, reverse
=True)
2386 data
= {'port': ports
[0]}
2387 return format_out(data
)
2388 except ovim
.ovimException
as e
:
2389 my
.logger
.error(str(e
), exc_info
=True)
2390 bottle
.abort(e
.http_code
, str(e
))
2391 except Exception as e
:
2392 my
.logger
.error(str(e
), exc_info
=True)
2393 bottle
.abort(HTTP_Bad_Request
, str(e
))
2396 @bottle.route(url_base
+ '/ports/<port_id>', method
='DELETE')
2397 def http_delete_port_id(port_id
):
2398 '''delete a port_id from the database.'''
2399 my
= config_dic
['http_threads'][ threading
.current_thread().name
]
2401 bottle
.abort(HTTP_Unauthorized
, "Needed admin privileges")
2404 result
= my
.ovim
.delete_port(port_id
)
2405 data
= {'result': result
}
2406 return format_out(data
)
2407 except ovim
.ovimException
as e
:
2408 my
.logger
.error(str(e
), exc_info
=True)
2409 bottle
.abort(e
.http_code
, str(e
))
2410 except Exception as e
:
2411 my
.logger
.error(str(e
), exc_info
=True)
2412 bottle
.abort(HTTP_Bad_Request
, str(e
))
2415 @bottle.route(url_base
+ '/openflow/mapping', method
='POST')
2416 def http_of_port_mapping():
2418 Create new compute port mapping entry
2421 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2424 http_content
= format_in(of_port_map_new_schema
)
2425 r
= remove_extra_items(http_content
, of_port_map_new_schema
)
2427 my
.logger
.error("http_of_port_mapping: Warning: remove extra items " + str(r
), exc_info
=True)
2429 # insert in data base
2430 port_mapping
= my
.ovim
.set_of_port_mapping(http_content
['of_port_mapings'])
2431 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2432 delete_nulls(port_mapping
)
2433 data
= {'of_port_mappings': port_mapping
}
2434 return format_out(data
)
2435 except ovim
.ovimException
as e
:
2436 my
.logger
.error(str(e
), exc_info
=True)
2437 bottle
.abort(e
.http_code
, str(e
))
2438 except Exception as e
:
2439 my
.logger
.error(str(e
), exc_info
=True)
2440 bottle
.abort(HTTP_Bad_Request
, str(e
))
2443 @bottle.route(url_base
+ '/openflow/mapping', method
='GET')
2444 def get_of_port_mapping():
2446 Get compute port mapping
2449 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2452 select_
, where_
, limit_
= filter_query_string(bottle
.request
.query
, http2db_id
,
2453 ('id', 'ofc_id', 'region', 'compute_node', 'pci',
2454 'switch_dpid', 'switch_port', 'switch_mac'))
2455 # insert in data base
2456 port_mapping
= my
.ovim
.get_of_port_mappings(select_
, where_
)
2457 change_keys_http2db(port_mapping
, http2db_id
, reverse
=True)
2458 delete_nulls(port_mapping
)
2459 data
= {'of_port_mappings': port_mapping
}
2460 return format_out(data
)
2461 except ovim
.ovimException
as e
:
2462 my
.logger
.error(str(e
), exc_info
=True)
2463 bottle
.abort(e
.http_code
, str(e
))
2464 except Exception as e
:
2465 my
.logger
.error(str(e
), exc_info
=True)
2466 bottle
.abort(HTTP_Bad_Request
, str(e
))
2469 @bottle.route(url_base
+ '/openflow/mapping/<region>', method
='DELETE')
2470 def delete_of_port_mapping(region
):
2472 Insert a tenant into the database.
2475 my
= config_dic
['http_threads'][threading
.current_thread().name
]
2478 # insert in data base
2479 db_filter
= {'region': region
}
2480 result
= my
.ovim
.clear_of_port_mapping(db_filter
)
2481 data
= {'result': result
}
2482 return format_out(data
)
2483 except ovim
.ovimException
as e
:
2484 my
.logger
.error(str(e
), exc_info
=True)
2485 bottle
.abort(e
.http_code
, str(e
))
2486 except Exception as e
:
2487 my
.logger
.error(str(e
), exc_info
=True)
2488 bottle
.abort(HTTP_Bad_Request
, str(e
))