Guest User

Untitled

a guest
Nov 26th, 2012
55
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # vim: tabstop=4 shiftwidth=4 softtabstop=4
  2.  
  3. # Copyright 2012 Nicira Networks, Inc
  4. # All Rights Reserved.
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  7. # not use this file except in compliance with the License. You may obtain
  8. # a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. # License for the specific language governing permissions and limitations
  16. # under the License.
  17.  
  18. '''Implement Security Groups Hanlder that proxies calles to Quantum
  19.  
  20. The nova security_group_handler flag specifies which class is to be used
  21. to implement the security group calls.
  22.  
  23. The NullSecurityGroupHandler provides a "no-op" plugin that is loaded
  24. by default and has no impact on current system behavior. In the future,
  25. special purposes classes that inherit from SecurityGroupHandlerBase
  26. will provide enhanced functionality and will be loadable via the
  27. security_group_handler flag.
  28. '''
  29.  
  30. from nova import flags
  31. from nova import exception
  32. from nova.db import api as db
  33. from nova.db.sqlalchemy.session import get_session
  34. from nova.db.sqlalchemy import models
  35. from sqlalchemy.sql.expression import literal_column
  36.  
  37. from nova.openstack.common import log as logging
  38. from nova.network import sg
  39. from nova.network import quantumv2
  40. from nova import quota
  41. from quantumclient.common import exceptions as q_exc
  42.  
  43. FLAGS = flags.FLAGS
  44. QUOTAS = quota.QUOTAS
  45. LOG = logging.getLogger(__name__)
  46.  
  47.  
  48. class SecurityGroupHandlerQuantumProxy(sg.SecurityGroupHandlerBase):
  49.  
  50. def __init__(self):
  51. LOG.info('Loading SecurityGroupHandlerQuantumProxy')
  52.  
  53. def trigger_security_group_create_refresh(self, context, group):
  54. '''Called when a security group is created
  55.  
  56. :param context: the security context.
  57. :param group: the new group added. group is a dictionary that contains
  58. the following: user_id, project_id, name, description).'''
  59.  
  60. LOG.info('Creating security group: %s' % group)
  61. sg = db.security_group_get_by_name(context, group['project_id'],
  62. group['name'])
  63.  
  64. body = {'security_group': {'external_id': sg.id,
  65. 'name': group['name'],
  66. 'description': group['description'],
  67. 'tenant_id': group['project_id']}}
  68.  
  69. # This will cause us to get an admin token
  70. context.auth_token = None
  71. quantum = quantumv2.get_client(context)
  72. try:
  73. quantum.create_security_group(body)
  74. except q_exc.QuantumClientException as e:
  75. LOG.error("Quuantum Error creating %s (%s)" % (group, e))
  76.  
  77. # undo create
  78. if db.security_group_in_use(context, sg.id):
  79. msg = _("Security group is still in use")
  80. self.raise_invalid_group(msg)
  81.  
  82. # Get reservations
  83. try:
  84. reservations = QUOTAS.reserve(context, security_groups=-1)
  85. except Exception:
  86. reservations = None
  87. LOG.error("Failed to update usages deallocating "
  88. "security group")
  89.  
  90. LOG.warning(("Delete security group %s"), group['name'])
  91. db.security_group_destroy(context, sg.id)
  92.  
  93. # Commit the reservations
  94. if reservations:
  95. QUOTAS.commit(context, reservations)
  96.  
  97. # Raise exception here so user knows their operation failed.
  98. raise q_exc.ConnectionFailed(reason=e)
  99.  
  100. def trigger_security_group_destroy_refresh(self, context,
  101. security_group_id):
  102. '''Called when a security group is deleted
  103.  
  104. :param context: the security context.
  105. :param security_group_id: the security group identifier.'''
  106.  
  107. LOG.info('Deleting security group: %s' % security_group_id)
  108. # This will cause us to get an admin token
  109. context.auth_token = None
  110. quantum = quantumv2.get_client(context)
  111. try:
  112. quantum.delete_security_group(security_group_id)
  113. except q_exc.QuantumClientException as e:
  114. LOG.error("quantum exception deleting security group (%s)" % e)
  115. # Undo delete
  116. # Could there be a race condition here where someone creates
  117. # another security group and we run out of quota?
  118. try:
  119. reservations = QUOTAS.reserve(context, security_groups=1)
  120. except exception.OverQuota:
  121. msg = _("Quota exceeded, too many security groups.")
  122. self.raise_over_quota(msg)
  123.  
  124. LOG.warning(("Undeleting Security Group %s id"), security_group_id)
  125. session = get_session()
  126. with session.begin():
  127. session.query(models.SecurityGroup).\
  128. filter_by(id=security_group_id).\
  129. update({'deleted': False,
  130. 'deleted_at': None,
  131. 'updated_at': literal_column('updated_at')})
  132. session.query(models.SecurityGroupInstanceAssociation).\
  133. filter_by(security_group_id=security_group_id).\
  134. update({'deleted': False,
  135. 'deleted_at': None,
  136. 'updated_at': literal_column('updated_at')})
  137.  
  138. session.query(models.SecurityGroupIngressRule).\
  139. filter_by(group_id=security_group_id).\
  140. update({'deleted': False,
  141. 'deleted_at': None,
  142. 'updated_at': literal_column('updated_at')})
  143.  
  144. session.query(models.SecurityGroupIngressRule).\
  145. filter_by(parent_group_id=security_group_id).\
  146. update({'deleted': False,
  147. 'deleted_at': None,
  148. 'updated_at': literal_column('updated_at')})
  149.  
  150. # Commit the reservation
  151. QUOTAS.commit(context, reservations)
  152. # Raise exception here so user knows their operation failed.
  153. raise q_exc.ConnectionFailed(reason=e)
  154.  
  155. def trigger_security_group_rule_create_refresh(self, context,
  156. rule_ids):
  157. '''Called when a rule is added to a security_group.
  158.  
  159. :param context: the security context.
  160. :param rule_ids: a list of rule ids that have been affected.'''
  161. rules = []
  162. for rid in rule_ids:
  163. rules.append(db.security_group_rule_get(context, rid))
  164.  
  165. # This will cause us to get an admin token
  166. context.auth_token = None
  167. quantum = quantumv2.get_client(context)
  168.  
  169. try:
  170. quantum.create_security_group_rule(
  171. body=self.mk_security_group_rule_dict(rules))
  172. except q_exc.QuantumClientException as e:
  173. LOG.exception("quantum exception deleting security group (%s)" % e)
  174. # Undo the createrule
  175. for rule_id in rule_ids:
  176. db.security_group_rule_destroy(context, rule_id)
  177. raise q_exc.ConnectionFailed(reason=e)
  178.  
  179. def trigger_security_group_rule_destroy_refresh(self, context,
  180. rule_ids):
  181. '''Called when a rule is removed from a security_group.
  182.  
  183. :param context: the security context.
  184. :param rule_ids: a list of rule ids that have been affected.'''
  185.  
  186. rules = []
  187.  
  188. for rid in rule_ids:
  189. rules.append(db.security_group_rule_get(
  190. context.elevated(read_deleted='yes'), rid))
  191.  
  192. # This will cause us to get an admin token
  193. context.auth_token = None
  194. quantum = quantumv2.get_client(context)
  195. deleted_rids = set()
  196. try:
  197. for rid in rule_ids:
  198. quantum.delete_security_group_rule(rid)
  199. deleted_rids.add(rid)
  200. except q_exc.QuantumClientException as e:
  201. session = get_session()
  202. with session.begin():
  203. for rid in set(rule_ids) - deleted_rids:
  204. LOG.warning(("Undeleting Security Group Rule %s id"), rid)
  205. session.query(models.SecurityGroupIngressRule).\
  206. filter_by(id=rid).\
  207. update({'deleted': False,
  208. 'deleted_at': None,
  209. 'updated_at': literal_column('updated_at')})
  210.  
  211. LOG.exception("quantum exception deleting security group (%s)" % e)
  212. raise q_exc.ConnectionFailed(reason=e)
  213.  
  214. def trigger_instance_add_security_group_refresh(self, context, instance,
  215. group_name):
  216. '''Called when a security group gains a new member.
  217.  
  218. :param context: the security context.
  219. :param instance: the instance to be associated.
  220. :param group_name: the name of the security group to be associated.'''
  221.  
  222. project_id = context.project_id
  223. security_group = db.security_group_get_by_name(context,
  224. project_id, group_name)
  225.  
  226. # This will cause us to get an admin token
  227. context.auth_token = None
  228. quantum = quantumv2.get_client(context)
  229. params = {'device_id': instance['uuid']}
  230. ports = quantum.list_ports(**params)
  231. for port in ports['ports']:
  232. if ('port_security' not in port or
  233. port['port_security'] != 'mac_ip'):
  234. LOG.warn("Cannot add security group %s to %s since no"
  235. "port_security mac_ip" % (group_name,
  236. instance['uuid']))
  237. # Cannot make any chance since port doesn't have port_security
  238. # enabled.
  239. continue
  240. updated_port = {'port': {'name': port['name'],
  241. 'admin_state_up': port['admin_state_up'],
  242. 'fixed_ips': port['fixed_ips'],
  243. 'device_id': port['device_id'],
  244. 'device_owner': port['device_owner']}}
  245. if 'security_groups' not in port:
  246. port['security_groups'] = []
  247. port['security_groups'].append(security_group.id)
  248. updated_port['port']['security_groups'] = port['security_groups']
  249. try:
  250. LOG.info("Adding security group %s %s to port %s" %
  251. (group_name, security_group.id, port['id']))
  252. quantum.update_port(port['id'], updated_port)
  253. except q_exc.QuantumClientException as e:
  254. # undo
  255. db.instance_remove_security_group(context.elevated(),
  256. instance['uuid'],
  257. security_group.id)
  258.  
  259. raise q_exc.ConnectionFailed(reason=e)
  260.  
  261. def trigger_instance_remove_security_group_refresh(self, context, instance,
  262. group_name):
  263. '''Called when a security group loses a member.
  264.  
  265. :param context: the security context.
  266. :param instance: the instance to be associated.
  267. :param group_name: the name of the security group to be associated.'''
  268. session = get_session()
  269.  
  270. # get all the security groups associated with an instance
  271. with session.begin():
  272. sgs = session.query(models.SecurityGroupInstanceAssociation).\
  273. filter_by(instance_uuid=instance['uuid']).\
  274. filter_by(deleted=False).\
  275. all()
  276.  
  277. security_group_ids = []
  278. for sg in sgs:
  279. security_group_ids.append(sg['security_group_id'])
  280.  
  281. # This will cause us to get an admin token
  282. context.auth_token = None
  283. quantum = quantumv2.get_client(context)
  284. params = {'device_id': instance['uuid']}
  285. ports = quantum.list_ports(**params)
  286. for port in ports['ports']:
  287. if ('port_security' not in port or
  288. port['port_security'] != 'mac_ip'):
  289. # Cannot make any chance since port doesn't have port_security
  290. # enabled.
  291. LOG.warn("Cannot add security group %s to %s since no"
  292. "port_security mac_ip" % (group_name,
  293. instance['uuid']))
  294. continue
  295. updated_port = {'port': {'name': port['name'],
  296. 'admin_state_up': port['admin_state_up'],
  297. 'fixed_ips': port['fixed_ips'],
  298. 'device_id': port['device_id'],
  299. 'device_owner': port['device_owner'],
  300. 'security_groups': security_group_ids}}
  301. try:
  302. quantum.update_port(port['id'], updated_port)
  303. except q_exc.QuantumClientException as e:
  304. # undo
  305. security_group = db.security_group_get_by_name(
  306. context, context.project_id, group_name)
  307.  
  308. db.instance_add_security_group(context.elevated(),
  309. instance['uuid'],
  310. security_group.id)
  311.  
  312. raise q_exc.ConnectionFailed(reason=e)
  313.  
  314. def trigger_security_group_members_refresh(self, context, group_ids):
  315. '''Called when a security group gains or loses a member.
  316.  
  317. :param context: the security context.
  318. :param group_ids: a list of security group identifiers.'''
  319. pass
  320.  
  321. #-----------------------------------------------------------------------------
  322. # Helper functions
  323. #-----------------------------------------------------------------------------
  324. def mk_security_group_rule_dict(self, security_group_rules):
  325. new_rules = []
  326. for rule in security_group_rules:
  327. proxy_rule = {'source_group_id': rule.group_id,
  328. 'source_ip_prefix': rule.cidr,
  329. 'external_id': rule.id,
  330. 'security_group_id': rule.parent_group_id,
  331. 'protocol': rule.protocol,
  332. 'direction': 'ingress'}
  333. if rule.protocol != 'icmp':
  334. proxy_rule['port_range_max'] = rule.to_port
  335. proxy_rule['port_range_min'] = rule.from_port
  336. new_rules.append(proxy_rule)
  337. return {'security_group_rules': new_rules}
RAW Paste Data