OpenStack Dashboard (Horizon)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

neutron.py 73KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050
  1. # Copyright 2012 United States Government as represented by the
  2. # Administrator of the National Aeronautics and Space Administration.
  3. # All Rights Reserved.
  4. #
  5. # Copyright 2012 Cisco Systems, Inc.
  6. # Copyright 2012 NEC Corporation
  7. #
  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
  11. #
  12. # http://www.apache.org/licenses/LICENSE-2.0
  13. #
  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
  18. # under the License.
  19. from __future__ import absolute_import
  20. import collections
  21. import copy
  22. import logging
  23. import netaddr
  24. from django.conf import settings
  25. from django.utils.translation import ugettext_lazy as _
  26. from neutronclient.common import exceptions as neutron_exc
  27. from neutronclient.v2_0 import client as neutron_client
  28. from novaclient import exceptions as nova_exc
  29. import six
  30. from horizon import exceptions
  31. from horizon import messages
  32. from horizon.utils.memoized import memoized
  33. from openstack_dashboard.api import base
  34. from openstack_dashboard.api import nova
  35. from openstack_dashboard.contrib.developer.profiler import api as profiler
  36. from openstack_dashboard import policy
  37. LOG = logging.getLogger(__name__)
  38. IP_VERSION_DICT = {4: 'IPv4', 6: 'IPv6'}
  39. OFF_STATE = 'OFF'
  40. ON_STATE = 'ON'
  41. ROUTER_INTERFACE_OWNERS = (
  42. 'network:router_interface',
  43. 'network:router_interface_distributed',
  44. 'network:ha_router_replicated_interface'
  45. )
  46. VNIC_TYPES = [
  47. ('normal', _('Normal')),
  48. ('direct', _('Direct')),
  49. ('direct-physical', _('Direct Physical')),
  50. ('macvtap', _('MacVTap')),
  51. ('baremetal', _('Bare Metal')),
  52. ('virtio-forwarder', _('Virtio Forwarder')),
  53. ]
  54. class NeutronAPIDictWrapper(base.APIDictWrapper):
  55. def __init__(self, apidict):
  56. if 'admin_state_up' in apidict:
  57. if apidict['admin_state_up']:
  58. apidict['admin_state'] = 'UP'
  59. else:
  60. apidict['admin_state'] = 'DOWN'
  61. # Django cannot handle a key name with ':', so use '__'.
  62. apidict.update({
  63. key.replace(':', '__'): value
  64. for key, value in apidict.items()
  65. if ':' in key
  66. })
  67. super(NeutronAPIDictWrapper, self).__init__(apidict)
  68. def set_id_as_name_if_empty(self, length=8):
  69. try:
  70. if not self._apidict['name'].strip():
  71. id = self._apidict['id']
  72. if length:
  73. id = id[:length]
  74. self._apidict['name'] = '(%s)' % id
  75. except KeyError:
  76. pass
  77. def items(self):
  78. return self._apidict.items()
  79. @property
  80. def name_or_id(self):
  81. return (self._apidict.get('name').strip() or
  82. '(%s)' % self._apidict['id'][:13])
  83. class Agent(NeutronAPIDictWrapper):
  84. """Wrapper for neutron agents."""
  85. class Network(NeutronAPIDictWrapper):
  86. """Wrapper for neutron Networks."""
  87. class Subnet(NeutronAPIDictWrapper):
  88. """Wrapper for neutron subnets."""
  89. def __init__(self, apidict):
  90. apidict['ipver_str'] = get_ipver_str(apidict['ip_version'])
  91. super(Subnet, self).__init__(apidict)
  92. AUTO_ALLOCATE_ID = '__auto_allocate__'
  93. class PreAutoAllocateNetwork(Network):
  94. def __init__(self, request):
  95. tenant_id = request.user.tenant_id
  96. auto_allocated_subnet = Subnet({
  97. 'name': 'auto_allocated_subnet',
  98. 'id': AUTO_ALLOCATE_ID,
  99. 'network_id': 'auto',
  100. 'tenant_id': tenant_id,
  101. # The following two fields are fake so that Subnet class
  102. # and the network topology view work without errors.
  103. 'ip_version': 4,
  104. 'cidr': '0.0.0.0/0',
  105. })
  106. auto_allocated_network = {
  107. 'name': 'auto_allocated_network',
  108. 'description': 'Network to be allocated automatically',
  109. 'id': AUTO_ALLOCATE_ID,
  110. 'status': 'ACTIVE',
  111. 'admin_state_up': True,
  112. 'shared': False,
  113. 'router:external': False,
  114. 'subnets': [auto_allocated_subnet],
  115. 'tenant_id': tenant_id,
  116. }
  117. super(PreAutoAllocateNetwork, self).__init__(auto_allocated_network)
  118. class Trunk(NeutronAPIDictWrapper):
  119. """Wrapper for neutron trunks."""
  120. @property
  121. def subport_count(self):
  122. return len(self._apidict.get('sub_ports', []))
  123. def to_dict(self):
  124. trunk_dict = super(Trunk, self).to_dict()
  125. trunk_dict['name_or_id'] = self.name_or_id
  126. trunk_dict['subport_count'] = self.subport_count
  127. return trunk_dict
  128. class SubnetPool(NeutronAPIDictWrapper):
  129. """Wrapper for neutron subnetpools."""
  130. class Port(NeutronAPIDictWrapper):
  131. """Wrapper for neutron ports."""
  132. def __init__(self, apidict):
  133. if 'mac_learning_enabled' in apidict:
  134. apidict['mac_state'] = \
  135. ON_STATE if apidict['mac_learning_enabled'] else OFF_STATE
  136. pairs = apidict.get('allowed_address_pairs')
  137. if pairs:
  138. apidict = copy.deepcopy(apidict)
  139. wrapped_pairs = [PortAllowedAddressPair(pair) for pair in pairs]
  140. apidict['allowed_address_pairs'] = wrapped_pairs
  141. super(Port, self).__init__(apidict)
  142. class PortTrunkParent(Port):
  143. """Neutron ports that are trunk parents.
  144. There's no need to add extra attributes for a trunk parent, because it
  145. already has 'trunk_details'. See also class PortTrunkSubport.
  146. """
  147. class PortTrunkSubport(Port):
  148. """Neutron ports that are trunk subports.
  149. The Neutron API expresses port subtyping information in a surprisingly
  150. complex way. When you see a port with attribute 'trunk_details' you know
  151. it's a trunk parent. But when you see a port without the 'trunk_details'
  152. attribute you can't tell if it's a trunk subport or a regular one without
  153. looking beyond the port's attributes. You must go and check if trunks
  154. (and/or trunk_details of trunk parent ports) refer to this port.
  155. Since this behavior is awfully complex we hide this from the rest of
  156. horizon by introducing types PortTrunkParent and PortTrunkSubport.
  157. """
  158. def __init__(self, apidict, trunk_subport_info):
  159. for field in ['trunk_id', 'segmentation_type', 'segmentation_id']:
  160. apidict[field] = trunk_subport_info[field]
  161. super(PortTrunkSubport, self).__init__(apidict)
  162. class PortAllowedAddressPair(NeutronAPIDictWrapper):
  163. """Wrapper for neutron port allowed address pairs."""
  164. def __init__(self, addr_pair):
  165. super(PortAllowedAddressPair, self).__init__(addr_pair)
  166. # Horizon references id property for table operations
  167. self.id = addr_pair['ip_address']
  168. class Router(NeutronAPIDictWrapper):
  169. """Wrapper for neutron routers."""
  170. class RouterStaticRoute(NeutronAPIDictWrapper):
  171. """Wrapper for neutron routes extra route."""
  172. def __init__(self, route):
  173. super(RouterStaticRoute, self).__init__(route)
  174. # Horizon references id property for table operations
  175. self.id = route['nexthop'] + ":" + route['destination']
  176. class SecurityGroup(NeutronAPIDictWrapper):
  177. # Required attributes: id, name, description, tenant_id, rules
  178. def __init__(self, sg, sg_dict=None):
  179. if sg_dict is None:
  180. sg_dict = {sg['id']: sg['name']}
  181. sg['rules'] = [SecurityGroupRule(rule, sg_dict)
  182. for rule in sg['security_group_rules']]
  183. super(SecurityGroup, self).__init__(sg)
  184. def to_dict(self):
  185. return {k: self._apidict[k] for k in self._apidict if k != 'rules'}
  186. @six.python_2_unicode_compatible
  187. class SecurityGroupRule(NeutronAPIDictWrapper):
  188. # Required attributes:
  189. # id, parent_group_id
  190. # ip_protocol, from_port, to_port, ip_range, group
  191. # ethertype, direction (Neutron specific)
  192. def _get_secgroup_name(self, sg_id, sg_dict):
  193. if sg_id:
  194. if sg_dict is None:
  195. sg_dict = {}
  196. # If sg name not found in sg_dict,
  197. # first two parts of UUID is used as sg name.
  198. return sg_dict.get(sg_id, sg_id[:13])
  199. else:
  200. return u''
  201. def __init__(self, sgr, sg_dict=None):
  202. # In Neutron, if both remote_ip_prefix and remote_group_id are None,
  203. # it means all remote IP range is allowed, i.e., 0.0.0.0/0 or ::/0.
  204. if not sgr['remote_ip_prefix'] and not sgr['remote_group_id']:
  205. if sgr['ethertype'] == 'IPv6':
  206. sgr['remote_ip_prefix'] = '::/0'
  207. else:
  208. sgr['remote_ip_prefix'] = '0.0.0.0/0'
  209. rule = {
  210. 'id': sgr['id'],
  211. 'parent_group_id': sgr['security_group_id'],
  212. 'direction': sgr['direction'],
  213. 'ethertype': sgr['ethertype'],
  214. 'ip_protocol': sgr['protocol'],
  215. 'from_port': sgr['port_range_min'],
  216. 'to_port': sgr['port_range_max'],
  217. 'description': sgr.get('description', '')
  218. }
  219. cidr = sgr['remote_ip_prefix']
  220. rule['ip_range'] = {'cidr': cidr} if cidr else {}
  221. group = self._get_secgroup_name(sgr['remote_group_id'], sg_dict)
  222. rule['group'] = {'name': group} if group else {}
  223. super(SecurityGroupRule, self).__init__(rule)
  224. def __str__(self):
  225. if 'name' in self.group:
  226. remote = self.group['name']
  227. elif 'cidr' in self.ip_range:
  228. remote = self.ip_range['cidr']
  229. else:
  230. remote = 'ANY'
  231. direction = 'to' if self.direction == 'egress' else 'from'
  232. if self.from_port:
  233. if self.from_port == self.to_port:
  234. proto_port = ("%s/%s" %
  235. (self.from_port, self.ip_protocol.lower()))
  236. else:
  237. proto_port = ("%s-%s/%s" %
  238. (self.from_port, self.to_port,
  239. self.ip_protocol.lower()))
  240. elif self.ip_protocol:
  241. try:
  242. ip_proto = int(self.ip_protocol)
  243. proto_port = "ip_proto=%d" % ip_proto
  244. except Exception:
  245. # well-defined IP protocol name like TCP, UDP, ICMP.
  246. proto_port = self.ip_protocol
  247. else:
  248. proto_port = ''
  249. return (_('ALLOW %(ethertype)s %(proto_port)s '
  250. '%(direction)s %(remote)s') %
  251. {'ethertype': self.ethertype,
  252. 'proto_port': proto_port,
  253. 'remote': remote,
  254. 'direction': direction})
  255. class SecurityGroupManager(object):
  256. """Manager class to implement Security Group methods
  257. SecurityGroup object returned from methods in this class
  258. must contains the following attributes:
  259. * id: ID of Security Group (int for Nova, uuid for Neutron)
  260. * name
  261. * description
  262. * tenant_id
  263. * rules: A list of SecurityGroupRule objects
  264. SecurityGroupRule object should have the following attributes
  265. (The attribute names and their formats are borrowed from nova
  266. security group implementation):
  267. * id
  268. * direction
  269. * ethertype
  270. * parent_group_id: security group the rule belongs to
  271. * ip_protocol
  272. * from_port: lower limit of allowed port range (inclusive)
  273. * to_port: upper limit of allowed port range (inclusive)
  274. * ip_range: remote IP CIDR (source for ingress, dest for egress).
  275. The value should be a format of "{'cidr': <cidr>}"
  276. * group: remote security group. The value should be a format of
  277. "{'name': <secgroup_name>}"
  278. """
  279. backend = 'neutron'
  280. def __init__(self, request):
  281. self.request = request
  282. self.client = neutronclient(request)
  283. def _list(self, **filters):
  284. secgroups = self.client.list_security_groups(**filters)
  285. return [SecurityGroup(sg) for sg in secgroups.get('security_groups')]
  286. @profiler.trace
  287. def list(self, **params):
  288. """Fetches a list all security groups.
  289. :returns: List of SecurityGroup objects
  290. """
  291. # This is to ensure tenant_id key is not populated
  292. # if tenant_id=None is specified.
  293. tenant_id = params.pop('tenant_id', self.request.user.tenant_id)
  294. if tenant_id:
  295. params['tenant_id'] = tenant_id
  296. return self._list(**params)
  297. def _sg_name_dict(self, sg_id, rules):
  298. """Create a mapping dict from secgroup id to its name."""
  299. related_ids = set([sg_id])
  300. related_ids |= set(filter(None, [r['remote_group_id'] for r in rules]))
  301. related_sgs = self.client.list_security_groups(id=related_ids,
  302. fields=['id', 'name'])
  303. related_sgs = related_sgs.get('security_groups')
  304. return dict((sg['id'], sg['name']) for sg in related_sgs)
  305. @profiler.trace
  306. def get(self, sg_id):
  307. """Fetches the security group.
  308. :returns: SecurityGroup object corresponding to sg_id
  309. """
  310. secgroup = self.client.show_security_group(sg_id).get('security_group')
  311. sg_dict = self._sg_name_dict(sg_id, secgroup['security_group_rules'])
  312. return SecurityGroup(secgroup, sg_dict)
  313. @profiler.trace
  314. def create(self, name, desc):
  315. """Create a new security group.
  316. :returns: SecurityGroup object created
  317. """
  318. body = {'security_group': {'name': name,
  319. 'description': desc,
  320. 'tenant_id': self.request.user.project_id}}
  321. secgroup = self.client.create_security_group(body)
  322. return SecurityGroup(secgroup.get('security_group'))
  323. @profiler.trace
  324. def update(self, sg_id, name, desc):
  325. body = {'security_group': {'name': name,
  326. 'description': desc}}
  327. secgroup = self.client.update_security_group(sg_id, body)
  328. return SecurityGroup(secgroup.get('security_group'))
  329. @profiler.trace
  330. def delete(self, sg_id):
  331. """Delete the specified security group."""
  332. self.client.delete_security_group(sg_id)
  333. @profiler.trace
  334. def rule_create(self, parent_group_id,
  335. direction=None, ethertype=None,
  336. ip_protocol=None, from_port=None, to_port=None,
  337. cidr=None, group_id=None, description=None):
  338. """Create a new security group rule.
  339. :param parent_group_id: security group id a rule is created to
  340. :param direction: ``ingress`` or ``egress``
  341. :param ethertype: ``IPv4`` or ``IPv6``
  342. :param ip_protocol: tcp, udp, icmp
  343. :param from_port: L4 port range min
  344. :param to_port: L4 port range max
  345. :param cidr: Remote IP CIDR
  346. :param group_id: ID of Source Security Group
  347. :returns: SecurityGroupRule object
  348. """
  349. if not cidr:
  350. cidr = None
  351. if isinstance(from_port, int) and from_port < 0:
  352. from_port = None
  353. if isinstance(to_port, int) and to_port < 0:
  354. to_port = None
  355. if isinstance(ip_protocol, int) and ip_protocol < 0:
  356. ip_protocol = None
  357. params = {'security_group_id': parent_group_id,
  358. 'direction': direction,
  359. 'ethertype': ethertype,
  360. 'protocol': ip_protocol,
  361. 'port_range_min': from_port,
  362. 'port_range_max': to_port,
  363. 'remote_ip_prefix': cidr,
  364. 'remote_group_id': group_id}
  365. if description is not None:
  366. params['description'] = description
  367. body = {'security_group_rule': params}
  368. try:
  369. rule = self.client.create_security_group_rule(body)
  370. except neutron_exc.OverQuotaClient:
  371. raise exceptions.Conflict(
  372. _('Security group rule quota exceeded.'))
  373. except neutron_exc.Conflict:
  374. raise exceptions.Conflict(
  375. _('Security group rule already exists.'))
  376. rule = rule.get('security_group_rule')
  377. sg_dict = self._sg_name_dict(parent_group_id, [rule])
  378. return SecurityGroupRule(rule, sg_dict)
  379. @profiler.trace
  380. def rule_delete(self, sgr_id):
  381. """Delete the specified security group rule."""
  382. self.client.delete_security_group_rule(sgr_id)
  383. @profiler.trace
  384. def list_by_instance(self, instance_id):
  385. """Gets security groups of an instance.
  386. :returns: List of SecurityGroup objects associated with the instance
  387. """
  388. ports = port_list(self.request, device_id=instance_id)
  389. sg_ids = []
  390. for p in ports:
  391. sg_ids += p.security_groups
  392. return self._list(id=set(sg_ids)) if sg_ids else []
  393. @profiler.trace
  394. def update_instance_security_group(self, instance_id,
  395. new_security_group_ids):
  396. """Update security groups of a specified instance."""
  397. ports = port_list(self.request, device_id=instance_id)
  398. for p in ports:
  399. params = {'security_groups': new_security_group_ids}
  400. port_update(self.request, p.id, **params)
  401. class FloatingIp(base.APIDictWrapper):
  402. _attrs = ['id', 'ip', 'fixed_ip', 'port_id', 'instance_id',
  403. 'instance_type', 'pool', 'dns_domain', 'dns_name']
  404. def __init__(self, fip):
  405. fip['ip'] = fip['floating_ip_address']
  406. fip['fixed_ip'] = fip['fixed_ip_address']
  407. fip['pool'] = fip['floating_network_id']
  408. super(FloatingIp, self).__init__(fip)
  409. class FloatingIpPool(base.APIDictWrapper):
  410. pass
  411. class FloatingIpTarget(base.APIDictWrapper):
  412. """Representation of floating IP association target.
  413. The following parameter needs to be passed when instantiating the class:
  414. :param port: ``Port`` object which represents a neutron port.
  415. :param ip_address: IP address of the ``port``. It must be one of
  416. IP address of a given port.
  417. :param label: String displayed in the floating IP association form.
  418. IP address will be appended to a specified label.
  419. """
  420. def __init__(self, port, ip_address, label):
  421. name = '%s: %s' % (label, ip_address) if label else ip_address
  422. target = {'name': name,
  423. 'id': '%s_%s' % (port.id, ip_address),
  424. 'port_id': port.id,
  425. 'instance_id': port.device_id}
  426. super(FloatingIpTarget, self).__init__(target)
  427. class FloatingIpManager(object):
  428. """Manager class to implement Floating IP methods
  429. The FloatingIP object returned from methods in this class
  430. must contains the following attributes:
  431. * id: ID of Floating IP
  432. * ip: Floating IP address
  433. * pool: ID of Floating IP pool from which the address is allocated
  434. * fixed_ip: Fixed IP address of a VIF associated with the address
  435. * port_id: ID of a VIF associated with the address
  436. (instance_id when Nova floating IP is used)
  437. * instance_id: Instance ID of an associated with the Floating IP
  438. """
  439. device_owner_map = {
  440. 'compute:': 'compute',
  441. 'neutron:LOADBALANCER': 'loadbalancer',
  442. }
  443. def __init__(self, request):
  444. self.request = request
  445. self.client = neutronclient(request)
  446. @profiler.trace
  447. def list_pools(self):
  448. """Fetches a list of all floating IP pools.
  449. :returns: List of FloatingIpPool objects
  450. """
  451. search_opts = {'router:external': True}
  452. return [FloatingIpPool(pool) for pool
  453. in self.client.list_networks(**search_opts).get('networks')]
  454. def _get_instance_type_from_device_owner(self, device_owner):
  455. for key, value in self.device_owner_map.items():
  456. if device_owner.startswith(key):
  457. return value
  458. return device_owner
  459. def _set_instance_info(self, fip, port=None):
  460. if fip['port_id']:
  461. if not port:
  462. port = port_get(self.request, fip['port_id'])
  463. fip['instance_id'] = port.device_id
  464. fip['instance_type'] = self._get_instance_type_from_device_owner(
  465. port.device_owner)
  466. else:
  467. fip['instance_id'] = None
  468. fip['instance_type'] = None
  469. @profiler.trace
  470. def list(self, all_tenants=False, **search_opts):
  471. """Fetches a list of all floating IPs.
  472. :returns: List of FloatingIp object
  473. """
  474. if not all_tenants:
  475. tenant_id = self.request.user.tenant_id
  476. # In Neutron, list_floatingips returns Floating IPs from
  477. # all tenants when the API is called with admin role, so
  478. # we need to filter them with tenant_id.
  479. search_opts['tenant_id'] = tenant_id
  480. port_search_opts = {'tenant_id': tenant_id}
  481. else:
  482. port_search_opts = {}
  483. fips = self.client.list_floatingips(**search_opts)
  484. fips = fips.get('floatingips')
  485. # Get port list to add instance_id to floating IP list
  486. # instance_id is stored in device_id attribute
  487. ports = port_list(self.request, **port_search_opts)
  488. port_dict = collections.OrderedDict([(p['id'], p) for p in ports])
  489. for fip in fips:
  490. self._set_instance_info(fip, port_dict.get(fip['port_id']))
  491. return [FloatingIp(fip) for fip in fips]
  492. @profiler.trace
  493. def get(self, floating_ip_id):
  494. """Fetches the floating IP.
  495. :returns: FloatingIp object corresponding to floating_ip_id
  496. """
  497. fip = self.client.show_floatingip(floating_ip_id).get('floatingip')
  498. self._set_instance_info(fip)
  499. return FloatingIp(fip)
  500. @profiler.trace
  501. def allocate(self, pool, tenant_id=None, **params):
  502. """Allocates a floating IP to the tenant.
  503. You must provide a pool name or id for which you would like to
  504. allocate a floating IP.
  505. :returns: FloatingIp object corresponding to an allocated floating IP
  506. """
  507. if not tenant_id:
  508. tenant_id = self.request.user.project_id
  509. create_dict = {'floating_network_id': pool,
  510. 'tenant_id': tenant_id}
  511. if 'subnet_id' in params:
  512. create_dict['subnet_id'] = params['subnet_id']
  513. if 'floating_ip_address' in params:
  514. create_dict['floating_ip_address'] = params['floating_ip_address']
  515. if 'description' in params:
  516. create_dict['description'] = params['description']
  517. if 'dns_domain' in params:
  518. create_dict['dns_domain'] = params['dns_domain']
  519. if 'dns_name' in params:
  520. create_dict['dns_name'] = params['dns_name']
  521. fip = self.client.create_floatingip(
  522. {'floatingip': create_dict}).get('floatingip')
  523. self._set_instance_info(fip)
  524. return FloatingIp(fip)
  525. @profiler.trace
  526. def release(self, floating_ip_id):
  527. """Releases a floating IP specified."""
  528. self.client.delete_floatingip(floating_ip_id)
  529. @profiler.trace
  530. def associate(self, floating_ip_id, port_id):
  531. """Associates the floating IP to the port.
  532. ``port_id`` represents a VNIC of an instance.
  533. ``port_id`` argument is different from a normal neutron port ID.
  534. A value passed as ``port_id`` must be one of target_id returned by
  535. ``list_targets``, ``get_target_by_instance`` or
  536. ``list_targets_by_instance`` method.
  537. """
  538. # NOTE: In Neutron Horizon floating IP support, port_id is
  539. # "<port_id>_<ip_address>" format to identify multiple ports.
  540. pid, ip_address = port_id.split('_', 1)
  541. update_dict = {'port_id': pid,
  542. 'fixed_ip_address': ip_address}
  543. self.client.update_floatingip(floating_ip_id,
  544. {'floatingip': update_dict})
  545. @profiler.trace
  546. def disassociate(self, floating_ip_id):
  547. """Disassociates the floating IP specified."""
  548. update_dict = {'port_id': None}
  549. self.client.update_floatingip(floating_ip_id,
  550. {'floatingip': update_dict})
  551. def _get_reachable_subnets(self, ports, fetch_router_ports=False):
  552. if not is_enabled_by_config('enable_fip_topology_check', True):
  553. # All subnets are reachable from external network
  554. return set(
  555. p.fixed_ips[0]['subnet_id'] for p in ports if p.fixed_ips
  556. )
  557. # Retrieve subnet list reachable from external network
  558. ext_net_ids = [ext_net.id for ext_net in self.list_pools()]
  559. gw_routers = [r.id for r in router_list(self.request)
  560. if (r.external_gateway_info and
  561. r.external_gateway_info.get('network_id')
  562. in ext_net_ids)]
  563. if fetch_router_ports:
  564. router_ports = port_list(self.request,
  565. device_owner=ROUTER_INTERFACE_OWNERS)
  566. else:
  567. router_ports = [p for p in ports
  568. if p.device_owner in ROUTER_INTERFACE_OWNERS]
  569. reachable_subnets = set([p.fixed_ips[0]['subnet_id']
  570. for p in router_ports
  571. if p.device_id in gw_routers])
  572. # we have to include any shared subnets as well because we may not
  573. # have permission to see the router interface to infer connectivity
  574. shared = set([s.id for n in network_list(self.request, shared=True)
  575. for s in n.subnets])
  576. return reachable_subnets | shared
  577. @profiler.trace
  578. def list_targets(self):
  579. """Returns a list of association targets of instance VIFs.
  580. Each association target is represented as FloatingIpTarget object.
  581. FloatingIpTarget is a APIResourceWrapper/APIDictWrapper and
  582. 'id' and 'name' attributes must be defined in each object.
  583. FloatingIpTarget.id can be passed as port_id in associate().
  584. FloatingIpTarget.name is displayed in Floating Ip Association Form.
  585. """
  586. tenant_id = self.request.user.tenant_id
  587. ports = port_list(self.request, tenant_id=tenant_id)
  588. servers, has_more = nova.server_list(self.request, detailed=False)
  589. server_dict = collections.OrderedDict(
  590. [(s.id, s.name) for s in servers])
  591. reachable_subnets = self._get_reachable_subnets(ports)
  592. targets = []
  593. for p in ports:
  594. # Remove network ports from Floating IP targets
  595. if p.device_owner.startswith('network:'):
  596. continue
  597. server_name = server_dict.get(p.device_id)
  598. for ip in p.fixed_ips:
  599. if ip['subnet_id'] not in reachable_subnets:
  600. continue
  601. # Floating IPs can only target IPv4 addresses.
  602. if netaddr.IPAddress(ip['ip_address']).version != 4:
  603. continue
  604. targets.append(FloatingIpTarget(p, ip['ip_address'],
  605. server_name))
  606. return targets
  607. def _target_ports_by_instance(self, instance_id):
  608. if not instance_id:
  609. return None
  610. search_opts = {'device_id': instance_id}
  611. return port_list(self.request, **search_opts)
  612. @profiler.trace
  613. def list_targets_by_instance(self, instance_id, target_list=None):
  614. """Returns a list of FloatingIpTarget objects of FIP association.
  615. :param instance_id: ID of target VM instance
  616. :param target_list: (optional) a list returned by list_targets().
  617. If specified, looking up is done against the specified list
  618. to save extra API calls to a back-end. Otherwise target list
  619. is retrieved from a back-end inside the method.
  620. """
  621. if target_list is not None:
  622. # We assume that target_list was returned by list_targets()
  623. # so we can assume checks for subnet reachability and IP version
  624. # have been done already. We skip all checks here.
  625. return [target for target in target_list
  626. if target['instance_id'] == instance_id]
  627. else:
  628. ports = self._target_ports_by_instance(instance_id)
  629. reachable_subnets = self._get_reachable_subnets(
  630. ports, fetch_router_ports=True)
  631. name = self._get_server_name(instance_id)
  632. targets = []
  633. for p in ports:
  634. for ip in p.fixed_ips:
  635. if ip['subnet_id'] not in reachable_subnets:
  636. continue
  637. # Floating IPs can only target IPv4 addresses.
  638. if netaddr.IPAddress(ip['ip_address']).version != 4:
  639. continue
  640. targets.append(FloatingIpTarget(p, ip['ip_address'], name))
  641. return targets
  642. def _get_server_name(self, server_id):
  643. try:
  644. server = nova.server_get(self.request, server_id)
  645. return server.name
  646. except nova_exc.NotFound:
  647. return ''
  648. def is_simple_associate_supported(self):
  649. """Returns True if the default floating IP pool is enabled."""
  650. # NOTE: There are two reason that simple association support
  651. # needs more considerations. (1) Neutron does not support the
  652. # default floating IP pool at the moment. It can be avoided
  653. # in case where only one floating IP pool exists.
  654. # (2) Neutron floating IP is associated with each VIF and
  655. # we need to check whether such VIF is only one for an instance
  656. # to enable simple association support.
  657. return False
  658. def is_supported(self):
  659. """Returns True if floating IP feature is supported."""
  660. network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
  661. return network_config.get('enable_router', True)
  662. def get_ipver_str(ip_version):
  663. """Convert an ip version number to a human-friendly string."""
  664. return IP_VERSION_DICT.get(ip_version, '')
  665. def get_auth_params_from_request(request):
  666. return (
  667. request.user.token.id,
  668. base.url_for(request, 'network'),
  669. base.url_for(request, 'identity')
  670. )
  671. @memoized
  672. def neutronclient(request):
  673. token_id, neutron_url, auth_url = get_auth_params_from_request(request)
  674. insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False)
  675. cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None)
  676. c = neutron_client.Client(token=token_id,
  677. auth_url=auth_url,
  678. endpoint_url=neutron_url,
  679. insecure=insecure, ca_cert=cacert)
  680. return c
  681. @profiler.trace
  682. def list_resources_with_long_filters(list_method,
  683. filter_attr, filter_values, **params):
  684. """List neutron resources with handling RequestURITooLong exception.
  685. If filter parameters are long, list resources API request leads to
  686. 414 error (URL is too long). For such case, this method split
  687. list parameters specified by a list_field argument into chunks
  688. and call the specified list_method repeatedly.
  689. :param list_method: Method used to retrieve resource list.
  690. :param filter_attr: attribute name to be filtered. The value corresponding
  691. to this attribute is specified by "filter_values".
  692. If you want to specify more attributes for a filter condition,
  693. pass them as keyword arguments like "attr2=values2".
  694. :param filter_values: values of "filter_attr" to be filtered.
  695. If filter_values are too long and the total URI length exceed the
  696. maximum length supported by the neutron server, filter_values will
  697. be split into sub lists if filter_values is a list.
  698. :param params: parameters to pass a specified listing API call
  699. without any changes. You can specify more filter conditions
  700. in addition to a pair of filter_attr and filter_values.
  701. """
  702. try:
  703. params[filter_attr] = filter_values
  704. return list_method(**params)
  705. except neutron_exc.RequestURITooLong as uri_len_exc:
  706. # The URI is too long because of too many filter values.
  707. # Use the excess attribute of the exception to know how many
  708. # filter values can be inserted into a single request.
  709. # We consider only the filter condition from (filter_attr,
  710. # filter_values) and do not consider other filter conditions
  711. # which may be specified in **params.
  712. if not isinstance(filter_values, (list, tuple, set, frozenset)):
  713. filter_values = [filter_values]
  714. # Length of each query filter is:
  715. # <key>=<value>& (e.g., id=<uuid>)
  716. # The length will be key_len + value_maxlen + 2
  717. all_filter_len = sum(len(filter_attr) + len(val) + 2
  718. for val in filter_values)
  719. allowed_filter_len = all_filter_len - uri_len_exc.excess
  720. val_maxlen = max(len(val) for val in filter_values)
  721. filter_maxlen = len(filter_attr) + val_maxlen + 2
  722. chunk_size = allowed_filter_len // filter_maxlen
  723. resources = []
  724. for i in range(0, len(filter_values), chunk_size):
  725. params[filter_attr] = filter_values[i:i + chunk_size]
  726. resources.extend(list_method(**params))
  727. return resources
  728. @profiler.trace
  729. def trunk_show(request, trunk_id):
  730. LOG.debug("trunk_show(): trunk_id=%s", trunk_id)
  731. trunk = neutronclient(request).show_trunk(trunk_id).get('trunk')
  732. return Trunk(trunk)
  733. @profiler.trace
  734. def trunk_list(request, **params):
  735. LOG.debug("trunk_list(): params=%s", params)
  736. trunks = neutronclient(request).list_trunks(**params).get('trunks')
  737. return [Trunk(t) for t in trunks]
  738. @profiler.trace
  739. def trunk_create(request, **params):
  740. LOG.debug("trunk_create(): params=%s", params)
  741. if 'project_id' not in params:
  742. params['project_id'] = request.user.project_id
  743. body = {'trunk': params}
  744. trunk = neutronclient(request).create_trunk(body=body).get('trunk')
  745. return Trunk(trunk)
  746. @profiler.trace
  747. def trunk_delete(request, trunk_id):
  748. LOG.debug("trunk_delete(): trunk_id=%s", trunk_id)
  749. neutronclient(request).delete_trunk(trunk_id)
  750. def _prepare_body_update_trunk(prop_diff):
  751. """Prepare body for PUT /v2.0/trunks/TRUNK_ID."""
  752. return {'trunk': prop_diff}
  753. def _prepare_body_remove_subports(subports):
  754. """Prepare body for PUT /v2.0/trunks/TRUNK_ID/remove_subports."""
  755. return {'sub_ports': [{'port_id': sp['port_id']} for sp in subports]}
  756. def _prepare_body_add_subports(subports):
  757. """Prepare body for PUT /v2.0/trunks/TRUNK_ID/add_subports."""
  758. return {'sub_ports': subports}
  759. @profiler.trace
  760. def trunk_update(request, trunk_id, old_trunk, new_trunk):
  761. """Handle update to a trunk in (at most) three neutron calls.
  762. The JavaScript side should know only about the old and new state of a
  763. trunk. However it should not know anything about how the old and new are
  764. meant to be diffed and sent to neutron. We handle that here.
  765. This code was adapted from Heat, see: https://review.openstack.org/442496
  766. Call #1) Update all changed properties but 'sub_ports'.
  767. PUT /v2.0/trunks/TRUNK_ID
  768. openstack network trunk set
  769. Call #2) Delete subports not needed anymore.
  770. PUT /v2.0/trunks/TRUNK_ID/remove_subports
  771. openstack network trunk unset --subport
  772. Call #3) Create new subports.
  773. PUT /v2.0/trunks/TRUNK_ID/add_subports
  774. openstack network trunk set --subport
  775. A single neutron port cannot be two subports at the same time (ie.
  776. have two segmentation (type, ID)s on the same trunk or to belong to
  777. two trunks). Therefore we have to delete old subports before creating
  778. new ones to avoid conflicts.
  779. """
  780. LOG.debug("trunk_update(): trunk_id=%s", trunk_id)
  781. # NOTE(bence romsics): We want to do set operations on the subports,
  782. # however we receive subports represented as dicts. In Python
  783. # mutable objects like dicts are not hashable so they cannot be
  784. # inserted into sets. So we convert subport dicts to (immutable)
  785. # frozensets in order to do the set operations.
  786. def dict2frozenset(d):
  787. """Convert a dict to a frozenset.
  788. Create an immutable equivalent of a dict, so it's hashable
  789. therefore can be used as an element of a set or a key of another
  790. dictionary.
  791. """
  792. return frozenset(d.items())
  793. # cf. neutron_lib/api/definitions/trunk.py
  794. updatable_props = ('admin_state_up', 'description', 'name')
  795. prop_diff = {
  796. k: new_trunk[k]
  797. for k in updatable_props
  798. if old_trunk[k] != new_trunk[k]}
  799. subports_old = {dict2frozenset(d): d
  800. for d in old_trunk.get('sub_ports', [])}
  801. subports_new = {dict2frozenset(d): d
  802. for d in new_trunk.get('sub_ports', [])}
  803. old_set = set(subports_old.keys())
  804. new_set = set(subports_new.keys())
  805. delete = old_set - new_set
  806. create = new_set - old_set
  807. dicts_delete = [subports_old[fs] for fs in delete]
  808. dicts_create = [subports_new[fs] for fs in create]
  809. trunk = old_trunk
  810. if prop_diff:
  811. LOG.debug('trunk_update(): update properties of trunk %s: %s',
  812. trunk_id, prop_diff)
  813. body = _prepare_body_update_trunk(prop_diff)
  814. trunk = neutronclient(request).update_trunk(
  815. trunk_id, body=body).get('trunk')
  816. if dicts_delete:
  817. LOG.debug('trunk_update(): delete subports of trunk %s: %s',
  818. trunk_id, dicts_delete)
  819. body = _prepare_body_remove_subports(dicts_delete)
  820. trunk = neutronclient(request).trunk_remove_subports(
  821. trunk_id, body=body)
  822. if dicts_create:
  823. LOG.debug('trunk_update(): create subports of trunk %s: %s',
  824. trunk_id, dicts_create)
  825. body = _prepare_body_add_subports(dicts_create)
  826. trunk = neutronclient(request).trunk_add_subports(
  827. trunk_id, body=body)
  828. return Trunk(trunk)
  829. @profiler.trace
  830. def network_list(request, **params):
  831. LOG.debug("network_list(): params=%s", params)
  832. networks = neutronclient(request).list_networks(**params).get('networks')
  833. # Get subnet list to expand subnet info in network list.
  834. subnets = subnet_list(request)
  835. subnet_dict = dict([(s['id'], s) for s in subnets])
  836. # Expand subnet list from subnet_id to values.
  837. for n in networks:
  838. # Due to potential timing issues, we can't assume the subnet_dict data
  839. # is in sync with the network data.
  840. n['subnets'] = [subnet_dict[s] for s in n.get('subnets', []) if
  841. s in subnet_dict]
  842. return [Network(n) for n in networks]
  843. def _is_auto_allocated_network_supported(request):
  844. try:
  845. neutron_auto_supported = is_service_enabled(
  846. request, 'enable_auto_allocated_network',
  847. 'auto-allocated-topology', default=False)
  848. except Exception:
  849. exceptions.handle(request, _('Failed to check if neutron supports '
  850. '"auto_allocated_network".'))
  851. neutron_auto_supported = False
  852. if not neutron_auto_supported:
  853. return False
  854. try:
  855. # server_create needs to support both features,
  856. # so we need to pass both features here.
  857. nova_auto_supported = nova.is_feature_available(
  858. request, ("instance_description",
  859. "auto_allocated_network"))
  860. except Exception:
  861. exceptions.handle(request, _('Failed to check if nova supports '
  862. '"auto_allocated_network".'))
  863. nova_auto_supported = False
  864. return nova_auto_supported
  865. @profiler.trace
  866. def network_list_for_tenant(request, tenant_id, include_external=False,
  867. include_pre_auto_allocate=False,
  868. **params):
  869. """Return a network list available for the tenant.
  870. The list contains networks owned by the tenant and public networks.
  871. If requested_networks specified, it searches requested_networks only.
  872. """
  873. LOG.debug("network_list_for_tenant(): tenant_id=%(tenant_id)s, "
  874. "params=%(params)s", {'tenant_id': tenant_id, 'params': params})
  875. networks = []
  876. shared = params.get('shared')
  877. if shared is not None:
  878. del params['shared']
  879. if shared in (None, False):
  880. # If a user has admin role, network list returned by Neutron API
  881. # contains networks that do not belong to that tenant.
  882. # So we need to specify tenant_id when calling network_list().
  883. networks += network_list(request, tenant_id=tenant_id,
  884. shared=False, **params)
  885. if shared in (None, True):
  886. # In the current Neutron API, there is no way to retrieve
  887. # both owner networks and public networks in a single API call.
  888. networks += network_list(request, shared=True, **params)
  889. # Hack for auto allocated network
  890. if include_pre_auto_allocate and not networks:
  891. if _is_auto_allocated_network_supported(request):
  892. networks.append(PreAutoAllocateNetwork(request))
  893. params['router:external'] = params.get('router:external', True)
  894. if params['router:external'] and include_external:
  895. if shared is not None:
  896. params['shared'] = shared
  897. fetched_net_ids = [n.id for n in networks]
  898. # Retrieves external networks when router:external is not specified
  899. # in (filtering) params or router:external=True filter is specified.
  900. # When router:external=False is specified there is no need to query
  901. # networking API because apparently nothing will match the filter.
  902. ext_nets = network_list(request, **params)
  903. networks += [n for n in ext_nets if
  904. n.id not in fetched_net_ids]
  905. return networks
  906. @profiler.trace
  907. def network_get(request, network_id, expand_subnet=True, **params):
  908. LOG.debug("network_get(): netid=%(network_id)s, params=%(params)s",
  909. {'network_id': network_id, 'params': params})
  910. network = neutronclient(request).show_network(network_id,
  911. **params).get('network')
  912. if expand_subnet:
  913. # NOTE(amotoki): There are some cases where a user has no permission
  914. # to get subnet details, but the condition is complicated. We first
  915. # try to fetch subnet details. If successful, the subnet details are
  916. # set to network['subnets'] as a list of "Subent" object.
  917. # If NotFound exception is returned by neutron, network['subnets'] is
  918. # left untouched and a list of subnet IDs are stored.
  919. # Neutron returns NotFound exception if a request user has enough
  920. # permission to access a requested resource, so we catch only
  921. # NotFound exception here.
  922. try:
  923. # Since the number of subnets per network must be small,
  924. # call subnet_get() for each subnet instead of calling
  925. # subnet_list() once.
  926. network['subnets'] = [subnet_get(request, sid)
  927. for sid in network['subnets']]
  928. except neutron_exc.NotFound:
  929. pass
  930. return Network(network)
  931. @profiler.trace
  932. def network_create(request, **kwargs):
  933. """Create a network object.
  934. :param request: request context
  935. :param tenant_id: (optional) tenant id of the network created
  936. :param name: (optional) name of the network created
  937. :returns: Network object
  938. """
  939. LOG.debug("network_create(): kwargs = %s", kwargs)
  940. if 'tenant_id' not in kwargs:
  941. kwargs['tenant_id'] = request.user.project_id
  942. body = {'network': kwargs}
  943. network = neutronclient(request).create_network(body=body).get('network')
  944. return Network(network)
  945. @profiler.trace
  946. def network_update(request, network_id, **kwargs):
  947. LOG.debug("network_update(): netid=%(network_id)s, params=%(params)s",
  948. {'network_id': network_id, 'params': kwargs})
  949. body = {'network': kwargs}
  950. network = neutronclient(request).update_network(network_id,
  951. body=body).get('network')
  952. return Network(network)
  953. @profiler.trace
  954. def network_delete(request, network_id):
  955. LOG.debug("network_delete(): netid=%s", network_id)
  956. neutronclient(request).delete_network(network_id)
  957. @profiler.trace
  958. @memoized
  959. def subnet_list(request, **params):
  960. LOG.debug("subnet_list(): params=%s", params)
  961. subnets = neutronclient(request).list_subnets(**params).get('subnets')
  962. return [Subnet(s) for s in subnets]
  963. @profiler.trace
  964. def subnet_get(request, subnet_id, **params):
  965. LOG.debug("subnet_get(): subnetid=%(subnet_id)s, params=%(params)s",
  966. {'subnet_id': subnet_id, 'params': params})
  967. subnet = neutronclient(request).show_subnet(subnet_id,
  968. **params).get('subnet')
  969. return Subnet(subnet)
  970. @profiler.trace
  971. def subnet_create(request, network_id, **kwargs):
  972. """Create a subnet on a specified network.
  973. :param request: request context
  974. :param network_id: network id a subnet is created on
  975. :param cidr: (optional) subnet IP address range
  976. :param ip_version: (optional) IP version (4 or 6)
  977. :param gateway_ip: (optional) IP address of gateway
  978. :param tenant_id: (optional) tenant id of the subnet created
  979. :param name: (optional) name of the subnet created
  980. :param subnetpool_id: (optional) subnetpool to allocate prefix from
  981. :param prefixlen: (optional) length of prefix to allocate
  982. :returns: Subnet object
  983. Although both cidr+ip_version and subnetpool_id+preifxlen is listed as
  984. optional you MUST pass along one of the combinations to get a successful
  985. result.
  986. """
  987. LOG.debug("subnet_create(): netid=%(network_id)s, kwargs=%(kwargs)s",
  988. {'network_id': network_id, 'kwargs': kwargs})
  989. body = {'subnet': {'network_id': network_id}}
  990. if 'tenant_id' not in kwargs:
  991. kwargs['tenant_id'] = request.user.project_id
  992. body['subnet'].update(kwargs)
  993. subnet = neutronclient(request).create_subnet(body=body).get('subnet')
  994. return Subnet(subnet)
  995. @profiler.trace
  996. def subnet_update(request, subnet_id, **kwargs):
  997. LOG.debug("subnet_update(): subnetid=%(subnet_id)s, kwargs=%(kwargs)s",
  998. {'subnet_id': subnet_id, 'kwargs': kwargs})
  999. body = {'subnet': kwargs}
  1000. subnet = neutronclient(request).update_subnet(subnet_id,
  1001. body=body).get('subnet')
  1002. return Subnet(subnet)
  1003. @profiler.trace
  1004. def subnet_delete(request, subnet_id):
  1005. LOG.debug("subnet_delete(): subnetid=%s", subnet_id)
  1006. neutronclient(request).delete_subnet(subnet_id)
  1007. @profiler.trace
  1008. def subnetpool_list(request, **params):
  1009. LOG.debug("subnetpool_list(): params=%s", params)
  1010. subnetpools = \
  1011. neutronclient(request).list_subnetpools(**params).get('subnetpools')
  1012. return [SubnetPool(s) for s in subnetpools]
  1013. @profiler.trace
  1014. def subnetpool_get(request, subnetpool_id, **params):
  1015. LOG.debug("subnetpool_get(): subnetpoolid=%(subnetpool_id)s, "
  1016. "params=%(params)s", {'subnetpool_id': subnetpool_id,
  1017. 'params': params})
  1018. subnetpool = \
  1019. neutronclient(request).show_subnetpool(subnetpool_id,
  1020. **params).get('subnetpool')
  1021. return SubnetPool(subnetpool)
  1022. @profiler.trace
  1023. def subnetpool_create(request, name, prefixes, **kwargs):
  1024. """Create a subnetpool.
  1025. ip_version is auto-detected in back-end.
  1026. Parameters:
  1027. request -- Request context
  1028. name -- Name for subnetpool
  1029. prefixes -- List of prefixes for pool
  1030. Keyword Arguments (optional):
  1031. min_prefixlen -- Minimum prefix length for allocations from pool
  1032. max_prefixlen -- Maximum prefix length for allocations from pool
  1033. default_prefixlen -- Default prefix length for allocations from pool
  1034. default_quota -- Default quota for allocations from pool
  1035. shared -- Subnetpool should be shared (Admin-only)
  1036. tenant_id -- Owner of subnetpool
  1037. Returns:
  1038. SubnetPool object
  1039. """
  1040. LOG.debug("subnetpool_create(): name=%(name)s, prefixes=%(prefixes)s, "
  1041. "kwargs=%(kwargs)s", {'name': name, 'prefixes': prefixes,
  1042. 'kwargs': kwargs})
  1043. body = {'subnetpool':
  1044. {'name': name,
  1045. 'prefixes': prefixes,
  1046. }
  1047. }
  1048. if 'tenant_id' not in kwargs:
  1049. kwargs['tenant_id'] = request.user.project_id
  1050. body['subnetpool'].update(kwargs)
  1051. subnetpool = \
  1052. neutronclient(request).create_subnetpool(body=body).get('subnetpool')
  1053. return SubnetPool(subnetpool)
  1054. @profiler.trace
  1055. def subnetpool_update(request, subnetpool_id, **kwargs):
  1056. LOG.debug("subnetpool_update(): subnetpoolid=%(subnetpool_id)s, "
  1057. "kwargs=%(kwargs)s", {'subnetpool_id': subnetpool_id,
  1058. 'kwargs': kwargs})
  1059. body = {'subnetpool': kwargs}
  1060. subnetpool = \
  1061. neutronclient(request).update_subnetpool(subnetpool_id,
  1062. body=body).get('subnetpool')
  1063. return SubnetPool(subnetpool)
  1064. @profiler.trace
  1065. def subnetpool_delete(request, subnetpool_id):
  1066. LOG.debug("subnetpool_delete(): subnetpoolid=%s", subnetpool_id)
  1067. return neutronclient(request).delete_subnetpool(subnetpool_id)
  1068. @profiler.trace
  1069. @memoized
  1070. def port_list(request, **params):
  1071. LOG.debug("port_list(): params=%s", params)
  1072. ports = neutronclient(request).list_ports(**params).get('ports')
  1073. return [Port(p) for p in ports]
  1074. @profiler.trace
  1075. @memoized
  1076. def port_list_with_trunk_types(request, **params):
  1077. """List neutron Ports for this tenant with possible TrunkPort indicated
  1078. :param request: request context
  1079. NOTE Performing two API calls is not atomic, but this is not worse
  1080. than the original idea when we call port_list repeatedly for
  1081. each network to perform identification run-time. We should
  1082. handle the inconsistencies caused by non-atomic API requests
  1083. gracefully.
  1084. """
  1085. LOG.debug("port_list_with_trunk_types(): params=%s", params)
  1086. # When trunk feature is disabled in neutron, we have no need to fetch
  1087. # trunk information and port_list() is enough.
  1088. if not is_extension_supported(request, 'trunk'):
  1089. return port_list(request, **params)
  1090. ports = neutronclient(request).list_ports(**params)['ports']
  1091. trunk_filters = {}
  1092. if 'tenant_id' in params:
  1093. trunk_filters['tenant_id'] = params['tenant_id']
  1094. trunks = neutronclient(request).list_trunks(**trunk_filters)['trunks']
  1095. parent_ports = set([t['port_id'] for t in trunks])
  1096. # Create a dict map for child ports (port ID to trunk info)
  1097. child_ports = dict([(s['port_id'],
  1098. {'trunk_id': t['id'],
  1099. 'segmentation_type': s['segmentation_type'],
  1100. 'segmentation_id': s['segmentation_id']})
  1101. for t in trunks
  1102. for s in t['sub_ports']])
  1103. def _get_port_info(port):
  1104. if port['id'] in parent_ports:
  1105. return PortTrunkParent(port)
  1106. elif port['id'] in child_ports:
  1107. return PortTrunkSubport(port, child_ports[port['id']])
  1108. else:
  1109. return Port(port)
  1110. return [_get_port_info(p) for p in ports]
  1111. @profiler.trace
  1112. def port_get(request, port_id, **params):
  1113. LOG.debug("port_get(): portid=%(port_id)s, params=%(params)s",
  1114. {'port_id': port_id, 'params': params})
  1115. port = neutronclient(request).show_port(port_id, **params).get('port')
  1116. return Port(port)
  1117. def unescape_port_kwargs(**kwargs):
  1118. for key in kwargs:
  1119. if '__' in key:
  1120. kwargs[':'.join(key.split('__'))] = kwargs.pop(key)
  1121. return kwargs
  1122. @profiler.trace
  1123. def port_create(request, network_id, **kwargs):
  1124. """Create a port on a specified network.
  1125. :param request: request context
  1126. :param network_id: network id a subnet is created on
  1127. :param device_id: (optional) device id attached to the port
  1128. :param tenant_id: (optional) tenant id of the port created
  1129. :param name: (optional) name of the port created
  1130. :returns: Port object
  1131. """
  1132. LOG.debug("port_create(): netid=%(network_id)s, kwargs=%(kwargs)s",
  1133. {'network_id': network_id, 'kwargs': kwargs})
  1134. kwargs = unescape_port_kwargs(**kwargs)
  1135. body = {'port': {'network_id': network_id}}
  1136. if 'tenant_id' not in kwargs:
  1137. kwargs['tenant_id'] = request.user.project_id
  1138. body['port'].update(kwargs)
  1139. port = neutronclient(request).create_port(body=body).get('port')
  1140. return Port(port)
  1141. @profiler.trace
  1142. def port_delete(request, port_id):
  1143. LOG.debug("port_delete(): portid=%s", port_id)
  1144. neutronclient(request).delete_port(port_id)
  1145. @profiler.trace
  1146. def port_update(request, port_id, **kwargs):
  1147. LOG.debug("port_update(): portid=%(port_id)s, kwargs=%(kwargs)s",
  1148. {'port_id': port_id, 'kwargs': kwargs})
  1149. kwargs = unescape_port_kwargs(**kwargs)
  1150. body = {'port': kwargs}
  1151. port = neutronclient(request).update_port(port_id, body=body).get('port')
  1152. return Port(port)
  1153. @profiler.trace
  1154. def router_create(request, **kwargs):
  1155. LOG.debug("router_create():, kwargs=%s", kwargs)
  1156. body = {'router': {}}
  1157. if 'tenant_id' not in kwargs:
  1158. kwargs['tenant_id'] = request.user.project_id
  1159. body['router'].update(kwargs)
  1160. router = neutronclient(request).create_router(body=body).get('router')
  1161. return Router(router)
  1162. @profiler.trace
  1163. def router_update(request, r_id, **kwargs):
  1164. LOG.debug("router_update(): router_id=%(r_id)s, kwargs=%(kwargs)s",
  1165. {'r_id': r_id, 'kwargs': kwargs})
  1166. body = {'router': {}}
  1167. body['router'].update(kwargs)
  1168. router = neutronclient(request).update_router(r_id, body=body)
  1169. return Router(router['router'])
  1170. @profiler.trace
  1171. def router_get(request, router_id, **params):
  1172. router = neutronclient(request).show_router(router_id,
  1173. **params).get('router')
  1174. return Router(router)
  1175. @profiler.trace
  1176. def router_list(request, **params):
  1177. routers = neutronclient(request).list_routers(**params).get('routers')
  1178. return [Router(r) for r in routers]
  1179. @profiler.trace
  1180. def router_list_on_l3_agent(request, l3_agent_id, **params):
  1181. routers = neutronclient(request).\
  1182. list_routers_on_l3_agent(l3_agent_id,
  1183. **params).get('routers')
  1184. return [Router(r) for r in routers]
  1185. @profiler.trace
  1186. def router_delete(request, router_id):
  1187. neutronclient(request).delete_router(router_id)
  1188. @profiler.trace
  1189. def router_add_interface(request, router_id, subnet_id=None, port_id=None):
  1190. body = {}
  1191. if subnet_id:
  1192. body['subnet_id'] = subnet_id
  1193. if port_id:
  1194. body['port_id'] = port_id
  1195. client = neutronclient(request)
  1196. return client.add_interface_router(router_id, body)
  1197. @profiler.trace
  1198. def router_remove_interface(request, router_id, subnet_id=None, port_id=None):
  1199. body = {}
  1200. if subnet_id:
  1201. body['subnet_id'] = subnet_id
  1202. if port_id:
  1203. body['port_id'] = port_id
  1204. neutronclient(request).remove_interface_router(router_id, body)
  1205. @profiler.trace
  1206. def router_add_gateway(request, router_id, network_id, enable_snat=None):
  1207. body = {'network_id': network_id}
  1208. if enable_snat is not None:
  1209. body['enable_snat'] = enable_snat
  1210. neutronclient(request).add_gateway_router(router_id, body)
  1211. @profiler.trace
  1212. def router_remove_gateway(request, router_id):
  1213. neutronclient(request).remove_gateway_router(router_id)
  1214. @profiler.trace
  1215. def router_static_route_list(request, router_id=None):
  1216. router = router_get(request, router_id)
  1217. try:
  1218. routes = [RouterStaticRoute(r) for r in router.routes]
  1219. except AttributeError:
  1220. LOG.debug("router_static_route_list(): router_id=%(router_id)s, "
  1221. "router=%(router)s", {'router_id': router_id,
  1222. 'router': router})
  1223. return []
  1224. return routes
  1225. @profiler.trace
  1226. def router_static_route_remove(request, router_id, route_ids):
  1227. currentroutes = router_static_route_list(request, router_id=router_id)
  1228. newroutes = []
  1229. for oldroute in currentroutes:
  1230. if oldroute.id not in route_ids:
  1231. newroutes.append({'nexthop': oldroute.nexthop,
  1232. 'destination': oldroute.destination})
  1233. body = {'routes': newroutes}
  1234. new = router_update(request, router_id, **body)
  1235. return new
  1236. @profiler.trace
  1237. def router_static_route_add(request, router_id, newroute):
  1238. body = {}
  1239. currentroutes = router_static_route_list(request, router_id=router_id)
  1240. body['routes'] = [newroute] + [{'nexthop': r.nexthop,
  1241. 'destination': r.destination}
  1242. for r in currentroutes]
  1243. new = router_update(request, router_id, **body)
  1244. return new
  1245. @profiler.trace
  1246. def tenant_quota_get(request, tenant_id):
  1247. return base.QuotaSet(neutronclient(request).show_quota(tenant_id)['quota'])
  1248. @profiler.trace
  1249. def tenant_quota_update(request, tenant_id, **kwargs):
  1250. quotas = {'quota': kwargs}
  1251. return neutronclient(request).update_quota(tenant_id, quotas)
  1252. @profiler.trace
  1253. def tenant_quota_detail_get(request, tenant_id=None):
  1254. tenant_id = tenant_id or request.user.tenant_id
  1255. response = neutronclient(request).get('/quotas/%s/details' % tenant_id)
  1256. return response['quota']
  1257. @profiler.trace
  1258. def default_quota_get(request, tenant_id=None):
  1259. tenant_id = tenant_id or request.user.tenant_id
  1260. response = neutronclient(request).show_quota_default(tenant_id)
  1261. return base.QuotaSet(response['quota'])
  1262. @profiler.trace
  1263. def agent_list(request, **params):
  1264. agents = neutronclient(request).list_agents(**params)
  1265. return [Agent(a) for a in agents['agents']]
  1266. @profiler.trace
  1267. def list_dhcp_agent_hosting_networks(request, network, **params):
  1268. agents = neutronclient(request).list_dhcp_agent_hosting_networks(network,
  1269. **params)
  1270. return [Agent(a) for a in agents['agents']]
  1271. @profiler.trace
  1272. def list_l3_agent_hosting_router(request, router, **params):
  1273. agents = neutronclient(request).list_l3_agent_hosting_routers(router,
  1274. **params)
  1275. return [Agent(a) for a in agents['agents']]
  1276. @profiler.trace
  1277. def show_network_ip_availability(request, network_id):
  1278. ip_availability = neutronclient(request).show_network_ip_availability(
  1279. network_id)
  1280. return ip_availability
  1281. @profiler.trace
  1282. def add_network_to_dhcp_agent(request, dhcp_agent, network_id):
  1283. body = {'network_id': network_id}
  1284. return neutronclient(request).add_network_to_dhcp_agent(dhcp_agent, body)
  1285. @profiler.trace
  1286. def remove_network_from_dhcp_agent(request, dhcp_agent, network_id):
  1287. return neutronclient(request).remove_network_from_dhcp_agent(dhcp_agent,
  1288. network_id)
  1289. @profiler.trace
  1290. def provider_list(request):
  1291. providers = neutronclient(request).list_service_providers()
  1292. return providers['service_providers']
  1293. def floating_ip_pools_list(request):
  1294. return FloatingIpManager(request).list_pools()
  1295. @memoized
  1296. def tenant_floating_ip_list(request, all_tenants=False, **search_opts):
  1297. return FloatingIpManager(request).list(all_tenants=all_tenants,
  1298. **search_opts)
  1299. def tenant_floating_ip_get(request, floating_ip_id):
  1300. return FloatingIpManager(request).get(floating_ip_id)
  1301. def tenant_floating_ip_allocate(request, pool=None, tenant_id=None, **params):
  1302. return FloatingIpManager(request).allocate(pool, tenant_id, **params)
  1303. def tenant_floating_ip_release(request, floating_ip_id):
  1304. return FloatingIpManager(request).release(floating_ip_id)
  1305. def floating_ip_associate(request, floating_ip_id, port_id):
  1306. return FloatingIpManager(request).associate(floating_ip_id, port_id)
  1307. def floating_ip_disassociate(request, floating_ip_id):
  1308. return FloatingIpManager(request).disassociate(floating_ip_id)
  1309. def floating_ip_target_list(request):
  1310. return FloatingIpManager(request).list_targets()
  1311. def floating_ip_target_list_by_instance(request, instance_id, cache=None):
  1312. return FloatingIpManager(request).list_targets_by_instance(
  1313. instance_id, cache)
  1314. def floating_ip_simple_associate_supported(request):
  1315. return FloatingIpManager(request).is_simple_associate_supported()
  1316. def floating_ip_supported(request):
  1317. return FloatingIpManager(request).is_supported()
  1318. @memoized
  1319. def security_group_list(request, **params):
  1320. return SecurityGroupManager(request).list(**params)
  1321. def security_group_get(request, sg_id):
  1322. return SecurityGroupManager(request).get(sg_id)
  1323. def security_group_create(request, name, desc):
  1324. return SecurityGroupManager(request).create(name, desc)
  1325. def security_group_delete(request, sg_id):
  1326. return SecurityGroupManager(request).delete(sg_id)
  1327. def security_group_update(request, sg_id, name, desc):
  1328. return SecurityGroupManager(request).update(sg_id, name, desc)
  1329. def security_group_rule_create(request, parent_group_id,
  1330. direction, ethertype,
  1331. ip_protocol, from_port, to_port,
  1332. cidr, group_id, description=None):
  1333. return SecurityGroupManager(request).rule_create(
  1334. parent_group_id, direction, ethertype, ip_protocol,
  1335. from_port, to_port, cidr, group_id, description)
  1336. def security_group_rule_delete(request, sgr_id):
  1337. return SecurityGroupManager(request).rule_delete(sgr_id)
  1338. def server_security_groups(request, instance_id):
  1339. return SecurityGroupManager(request).list_by_instance(instance_id)
  1340. def server_update_security_groups(request, instance_id,
  1341. new_security_group_ids):
  1342. return SecurityGroupManager(request).update_instance_security_group(
  1343. instance_id, new_security_group_ids)
  1344. # TODO(pkarikh) need to uncomment when osprofiler will have no
  1345. # issues with unicode in:
  1346. # openstack_dashboard/test/test_data/nova_data.py#L470 data
  1347. # @profiler.trace
  1348. def servers_update_addresses(request, servers, all_tenants=False):
  1349. """Retrieve servers networking information from Neutron if enabled.
  1350. Should be used when up to date networking information is required,
  1351. and Nova's networking info caching mechanism is not fast enough.
  1352. """
  1353. # NOTE(e0ne): we don't need to call neutron if we have no instances
  1354. if not servers:
  1355. return
  1356. # Get all (filtered for relevant servers) information from Neutron
  1357. try:
  1358. # NOTE(e0ne): we need tuple here to work with @memoized decorator.
  1359. # @memoized works with hashable arguments only.
  1360. ports = list_resources_with_long_filters(
  1361. port_list, 'device_id',
  1362. tuple([instance.id for instance in servers]),
  1363. request=request)
  1364. fips = FloatingIpManager(request)
  1365. if fips.is_supported():
  1366. floating_ips = list_resources_with_long_filters(
  1367. fips.list, 'port_id', tuple([port.id for port in ports]),
  1368. all_tenants=all_tenants)
  1369. else:
  1370. floating_ips = []
  1371. # NOTE(e0ne): we need frozenset here to work with @memoized decorator.
  1372. # @memoized works with hashable arguments only
  1373. networks = list_resources_with_long_filters(
  1374. network_list, 'id', frozenset([port.network_id for port in ports]),
  1375. request=request)
  1376. except neutron_exc.NotFound as e:
  1377. LOG.error('Neutron resource does not exist. %s', e)
  1378. return
  1379. except Exception as e:
  1380. LOG.error('Unable to connect to Neutron: %s', e)
  1381. error_message = _('Unable to connect to Neutron.')
  1382. messages.error(request, error_message)
  1383. return
  1384. # Map instance to its ports
  1385. instances_ports = collections.defaultdict(list)
  1386. for port in ports:
  1387. instances_ports[port.device_id].append(port)
  1388. # Map port to its floating ips
  1389. ports_floating_ips = collections.defaultdict(list)
  1390. for fip in floating_ips:
  1391. ports_floating_ips[fip.port_id].append(fip)
  1392. # Map network id to its name
  1393. network_names = dict((network.id, network.name_or_id)
  1394. for network in networks)
  1395. for server in servers:
  1396. try:
  1397. addresses = _server_get_addresses(
  1398. request,
  1399. server,
  1400. instances_ports,
  1401. ports_floating_ips,
  1402. network_names)
  1403. except Exception as e:
  1404. LOG.error(six.text_type(e))
  1405. else:
  1406. server.addresses = addresses
  1407. def _server_get_addresses(request, server, ports, floating_ips, network_names):
  1408. def _format_address(mac, ip, type):
  1409. try:
  1410. version = netaddr.IPAddress(ip).version
  1411. except Exception as e:
  1412. LOG.error('Unable to parse IP address %(ip)s: %(exc)s',
  1413. {'ip': ip, 'exc': e})
  1414. error_message = _('Unable to parse IP address %s.') % ip
  1415. messages.error(request, error_message)
  1416. raise
  1417. return {u'OS-EXT-IPS-MAC:mac_addr': mac,
  1418. u'version': version,
  1419. u'addr': ip,
  1420. u'OS-EXT-IPS:type': type}
  1421. addresses = collections.defaultdict(list)
  1422. instance_ports = ports.get(server.id, [])
  1423. for port in instance_ports:
  1424. network_name = network_names.get(port.network_id)
  1425. if network_name is not None:
  1426. if port.fixed_ips:
  1427. for fixed_ip in port.fixed_ips:
  1428. addresses[network_name].append(
  1429. _format_address(port.mac_address,
  1430. fixed_ip['ip_address'],
  1431. u'fixed'))
  1432. else:
  1433. addresses[network_name] = []
  1434. port_fips = floating_ips.get(port.id, [])
  1435. for fip in port_fips:
  1436. addresses[network_name].append(
  1437. _format_address(port.mac_address,
  1438. fip.floating_ip_address,
  1439. u'floating'))
  1440. return dict(addresses)
  1441. @profiler.trace
  1442. @memoized
  1443. def list_extensions(request):
  1444. """List neutron extensions.
  1445. :param request: django request object
  1446. """
  1447. neutron_api = neutronclient(request)
  1448. try:
  1449. extensions_list = neutron_api.list_extensions()
  1450. except exceptions.ServiceCatalogException:
  1451. return {}
  1452. if 'extensions' in extensions_list:
  1453. return tuple(extensions_list['extensions'])
  1454. else:
  1455. return ()
  1456. @profiler.trace
  1457. def is_extension_supported(request, extension_alias):
  1458. """Check if a specified extension is supported.
  1459. :param request: django request object
  1460. :param extension_alias: neutron extension alias
  1461. """
  1462. extensions = list_extensions(request)
  1463. for extension in extensions:
  1464. if extension['alias'] == extension_alias:
  1465. return True
  1466. else:
  1467. return False
  1468. def is_enabled_by_config(name, default=True):
  1469. network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
  1470. return network_config.get(name, default)
  1471. @memoized
  1472. def is_service_enabled(request, config_name, ext_name, default=True):
  1473. return (is_enabled_by_config(config_name, default) and
  1474. is_extension_supported(request, ext_name))
  1475. @memoized
  1476. def is_quotas_extension_supported(request):
  1477. return (is_enabled_by_config('enable_quotas', False) and
  1478. is_extension_supported(request, 'quotas'))
  1479. @memoized
  1480. def is_router_enabled(request):
  1481. return (is_enabled_by_config('enable_router') and
  1482. is_extension_supported(request, 'router'))
  1483. # FEATURE_MAP is used to define:
  1484. # - related neutron extension name (key: "extension")
  1485. # - corresponding dashboard config (key: "config")
  1486. # - RBAC policies (key: "poclies")
  1487. # If a key is not contained, the corresponding permission check is skipped.
  1488. FEATURE_MAP = {
  1489. 'dvr': {
  1490. 'extension': 'dvr',
  1491. 'config': {
  1492. 'name': 'enable_distributed_router',
  1493. 'default': False,
  1494. },
  1495. 'policies': {
  1496. 'get': 'get_router:distributed',
  1497. 'create': 'create_router:distributed',
  1498. 'update': 'update_router:distributed',
  1499. }
  1500. },
  1501. 'l3-ha': {
  1502. 'extension': 'l3-ha',
  1503. 'config': {'name': 'enable_ha_router',
  1504. 'default': False},
  1505. 'policies': {
  1506. 'get': 'get_router:ha',
  1507. 'create': 'create_router:ha',
  1508. 'update': 'update_router:ha',
  1509. }
  1510. },
  1511. 'ext-gw-mode': {
  1512. 'extension': 'ext-gw-mode',
  1513. 'policies': {
  1514. 'create_router_enable_snat':
  1515. 'create_router:external_gateway_info:enable_snat',
  1516. 'update_router_enable_snat':
  1517. 'update_router:external_gateway_info:enable_snat',
  1518. }
  1519. },
  1520. }
  1521. def get_feature_permission(request, feature, operation=None):
  1522. """Check if a feature-specific field can be displayed.
  1523. This method check a permission for a feature-specific field.
  1524. Such field is usually provided through Neutron extension.
  1525. :param request: Request Object
  1526. :param feature: feature name defined in FEATURE_MAP
  1527. :param operation (optional): Operation type. The valid value should be
  1528. defined in FEATURE_MAP[feature]['policies']
  1529. It must be specified if FEATURE_MAP[feature] has 'policies'.
  1530. """
  1531. network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
  1532. feature_info = FEATURE_MAP.get(feature)
  1533. if not feature_info:
  1534. raise ValueError("The requested feature '%(feature)s' is unknown. "
  1535. "Please make sure to specify a feature defined "
  1536. "in FEATURE_MAP.")
  1537. # Check dashboard settings
  1538. feature_config = feature_info.get('config')
  1539. if feature_config:
  1540. if not network_config.get(feature_config['name'],
  1541. feature_config['default']):
  1542. return False
  1543. # Check policy
  1544. feature_policies = feature_info.get('policies')
  1545. if feature_policies:
  1546. policy_name = feature_policies.get(operation)
  1547. if not policy_name:
  1548. raise ValueError("The 'operation' parameter for "
  1549. "get_feature_permission '%(feature)s' "
  1550. "is invalid. It should be one of %(allowed)s"
  1551. % {'feature': feature,
  1552. 'allowed': ' '.join(feature_policies.keys())})
  1553. role = (('network', policy_name),)
  1554. if not policy.check(role, request):
  1555. return False
  1556. # Check if a required extension is enabled
  1557. feature_extension = feature_info.get('extension')
  1558. if feature_extension:
  1559. try:
  1560. return is_extension_supported(request, feature_extension)
  1561. except Exception:
  1562. LOG.info("Failed to check Neutron '%s' extension is not supported",
  1563. feature_extension)
  1564. return False
  1565. # If all checks are passed, now a given feature is allowed.
  1566. return True
  1567. class QoSPolicy(NeutronAPIDictWrapper):
  1568. """Wrapper for neutron QoS Policy."""
  1569. def to_dict(self):
  1570. return self._apidict
  1571. def policy_create(request, **kwargs):
  1572. """Create a QoS Policy.
  1573. :param request: request context
  1574. :param name: name of the policy
  1575. :param description: description of policy
  1576. :param shared: boolean (true or false)
  1577. :return: QoSPolicy object
  1578. """
  1579. body = {'policy': kwargs}
  1580. policy = neutronclient(request).create_qos_policy(body=body).get('policy')
  1581. return QoSPolicy(policy)
  1582. def policy_list(request, **kwargs):
  1583. """List of QoS Policies."""
  1584. policies = neutronclient(request).list_qos_policies(
  1585. **kwargs).get('policies')
  1586. return [QoSPolicy(p) for p in policies]
  1587. @profiler.trace
  1588. def policy_get(request, policy_id, **kwargs):
  1589. """Get QoS policy for a given policy id."""
  1590. policy = neutronclient(request).show_qos_policy(
  1591. policy_id, **kwargs).get('policy')
  1592. return QoSPolicy(policy)
  1593. @profiler.trace
  1594. def policy_delete(request, policy_id):
  1595. """Delete QoS policy for a given policy id."""
  1596. neutronclient(request).delete_qos_policy(policy_id)
  1597. @profiler.trace
  1598. def list_availability_zones(request, resource=None, state=None):
  1599. az_list = neutronclient(request).list_availability_zones().get(
  1600. 'availability_zones')
  1601. if resource:
  1602. az_list = [az for az in az_list if az['resource'] == resource]
  1603. if state:
  1604. az_list = [az for az in az_list if az['state'] == state]
  1605. return sorted(az_list, key=lambda zone: zone['name'])
  1606. class RBACPolicy(NeutronAPIDictWrapper):
  1607. """Wrapper for neutron RBAC Policy."""
  1608. def rbac_policy_create(request, **kwargs):
  1609. """Create a RBAC Policy.
  1610. :param request: request context
  1611. :param target_tenant: target tenant of the policy
  1612. :param tenant_id: owner tenant of the policy(Not recommended)
  1613. :param object_type: network or qos_policy
  1614. :param object_id: object id of policy
  1615. :param action: access_as_shared or access_as_external
  1616. :return: RBACPolicy object
  1617. """
  1618. body = {'rbac_policy': kwargs}
  1619. rbac_policy = neutronclient(request).create_rbac_policy(
  1620. body=body).get('rbac_policy')
  1621. return RBACPolicy(rbac_policy)
  1622. def rbac_policy_list(request, **kwargs):
  1623. """List of RBAC Policies."""
  1624. policies = neutronclient(request).list_rbac_policies(
  1625. **kwargs).get('rbac_policies')
  1626. return [RBACPolicy(p) for p in policies]
  1627. def rbac_policy_update(request, policy_id, **kwargs):
  1628. """Update a RBAC Policy.
  1629. :param request: request context
  1630. :param policy_id: target policy id
  1631. :param target_tenant: target tenant of the policy
  1632. :return: RBACPolicy object
  1633. """
  1634. body = {'rbac_policy': kwargs}
  1635. rbac_policy = neutronclient(request).update_rbac_policy(
  1636. policy_id, body=body).get('rbac_policy')
  1637. return RBACPolicy(rbac_policy)
  1638. @profiler.trace
  1639. def rbac_policy_get(request, policy_id, **kwargs):
  1640. """Get RBAC policy for a given policy id."""
  1641. policy = neutronclient(request).show_rbac_policy(
  1642. policy_id, **kwargs).get('rbac_policy')
  1643. return RBACPolicy(policy)
  1644. @profiler.trace
  1645. def rbac_policy_delete(request, policy_id):
  1646. """Delete RBAC policy for a given policy id."""
  1647. neutronclient(request).delete_rbac_policy(policy_id)