| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- | 
|  | 2 | ''' | 
|  | 3 | Management of Neutron resources | 
|  | 4 | =============================== | 
|  | 5 | :depends:   - neutronclient Python module | 
|  | 6 | :configuration: See :py:mod:`salt.modules.neutron` for setup instructions. | 
|  | 7 | .. code-block:: yaml | 
|  | 8 | neutron network present: | 
|  | 9 | neutron.network_present: | 
|  | 10 | - name: Netone | 
|  | 11 | - provider_physical_network: PHysnet1 | 
|  | 12 | - provider_network_type: vlan | 
|  | 13 | ''' | 
|  | 14 | import logging | 
|  | 15 | from functools import wraps | 
|  | 16 | LOG = logging.getLogger(__name__) | 
|  | 17 |  | 
|  | 18 |  | 
|  | 19 | def __virtual__(): | 
|  | 20 | ''' | 
|  | 21 | Only load if neutron module is present in __salt__ | 
|  | 22 | ''' | 
|  | 23 | return 'neutronng' if 'neutron.list_networks' in __salt__ else False | 
|  | 24 |  | 
|  | 25 |  | 
|  | 26 | def _test_call(method): | 
|  | 27 | (resource, functionality) = method.func_name.split('_') | 
|  | 28 | if functionality == 'present': | 
|  | 29 | functionality = 'updated' | 
|  | 30 | else: | 
|  | 31 | functionality = 'removed' | 
|  | 32 |  | 
|  | 33 | @wraps(method) | 
|  | 34 | def check_for_testing(name, *args, **kwargs): | 
|  | 35 | if __opts__.get('test', None): | 
|  | 36 | return _no_change(name, resource, test=functionality) | 
|  | 37 | return method(name, *args, **kwargs) | 
|  | 38 | return check_for_testing | 
|  | 39 |  | 
|  | 40 |  | 
|  | 41 | def _neutron_module_call(method, *args, **kwargs): | 
|  | 42 | return __salt__['neutronng.{0}'.format(method)](*args, **kwargs) | 
|  | 43 |  | 
|  | 44 |  | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 45 | def _auth(profile=None, endpoint_type=None): | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 46 | ''' | 
|  | 47 | Set up neutron credentials | 
|  | 48 | ''' | 
|  | 49 | if profile: | 
|  | 50 | credentials = __salt__['config.option'](profile) | 
|  | 51 | user = credentials['keystone.user'] | 
|  | 52 | password = credentials['keystone.password'] | 
|  | 53 | tenant = credentials['keystone.tenant'] | 
|  | 54 | auth_url = credentials['keystone.auth_url'] | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 55 | kwargs = { | 
|  | 56 | 'connection_user': user, | 
|  | 57 | 'connection_password': password, | 
|  | 58 | 'connection_tenant': tenant, | 
| Richard Felkl | 22008f9 | 2017-03-29 16:14:24 +0200 | [diff] [blame^] | 59 | 'connection_auth_url': auth_url, | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 60 | 'connection_endpoint_type': endpoint_type | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 61 | } | 
|  | 62 |  | 
|  | 63 | return kwargs | 
|  | 64 |  | 
|  | 65 | @_test_call | 
|  | 66 | def network_present(name=None, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 67 | tenant=None, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 68 | provider_network_type=None, | 
|  | 69 | provider_physical_network=None, | 
|  | 70 | router_external=None, | 
|  | 71 | admin_state_up=None, | 
|  | 72 | shared=None, | 
|  | 73 | provider_segmentation_id=None, | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 74 | profile=None, | 
|  | 75 | endpoint_type=None): | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 76 | ''' | 
|  | 77 | Ensure that the neutron network is present with the specified properties. | 
|  | 78 | name | 
|  | 79 | The name of the network to manage | 
|  | 80 | ''' | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 81 | tenant_name = tenant | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 82 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 83 | try: | 
|  | 84 | tenant_id = __salt__['keystone.tenant_get']( | 
|  | 85 | name=tenant_name, **connection_args)[tenant_name]['id'] | 
|  | 86 | except: | 
|  | 87 | tenant_id = None | 
|  | 88 | LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format( | 
|  | 89 | connection_args['connection_user'])) | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 90 | existing_network = _neutron_module_call( | 
|  | 91 | 'list_networks', name=name, **connection_args) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 92 | network_arguments = _get_non_null_args( | 
|  | 93 | name=name, | 
|  | 94 | provider_network_type=provider_network_type, | 
|  | 95 | provider_physical_network=provider_physical_network, | 
|  | 96 | router_external=router_external, | 
|  | 97 | admin_state_up=admin_state_up, | 
|  | 98 | shared=shared, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 99 | tenant_id=tenant_id, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 100 | provider_segmentation_id=provider_segmentation_id) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 101 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 102 | if not existing_network: | 
|  | 103 | network_arguments.update(connection_args) | 
|  | 104 | _neutron_module_call('create_network', **network_arguments) | 
| Richard Felkl | b97cbe7 | 2017-02-28 22:37:59 +0100 | [diff] [blame] | 105 | existing_networks = _neutron_module_call( | 
| Richard Felkl | 22008f9 | 2017-03-29 16:14:24 +0200 | [diff] [blame^] | 106 | 'list_networks',name=name, **connection_args) | 
| Richard Felkl | b97cbe7 | 2017-02-28 22:37:59 +0100 | [diff] [blame] | 107 | for network in existing_networks: | 
|  | 108 | if network.get(name) == name: | 
|  | 109 | existing_network = network | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 110 | if existing_network: | 
|  | 111 | return _created(name, 'network', existing_network[name]) | 
|  | 112 | return _update_failed(name, 'network') | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 113 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 114 | LOG.info('CONNECTION STRINGS' + str(connection_args)) | 
|  | 115 | LOG.info('existing ' + str(existing_network)) | 
|  | 116 | LOG.info('new ' + str(network_arguments)) | 
|  | 117 | existing_network = dict((key.replace(':', '_', 1), value) | 
|  | 118 | for key, value in | 
|  | 119 | existing_network[name].iteritems()) | 
|  | 120 | # generate differential | 
|  | 121 | diff = dict((key, value) for key, value in network_arguments.iteritems() | 
|  | 122 | if existing_network.get(key, None) != value) | 
|  | 123 | if diff: | 
|  | 124 | # update the changes | 
|  | 125 | network_arguments = diff.copy() | 
|  | 126 | network_arguments.update(connection_args) | 
|  | 127 | try: | 
|  | 128 | LOG.debug('updating network {0} with changes {1}'.format( | 
|  | 129 | name, str(diff))) | 
|  | 130 | _neutron_module_call('update_network', | 
|  | 131 | existing_network['id'], | 
|  | 132 | **network_arguments) | 
|  | 133 | changes_dict = _created(name, 'network', diff) | 
|  | 134 | changes_dict['comment'] = '{1} {0} updated'.format(name, 'network') | 
|  | 135 | return changes_dict | 
|  | 136 | except: | 
|  | 137 | LOG.exception('Could not update network {0}'.format(name)) | 
|  | 138 | return _update_failed(name, 'network') | 
|  | 139 | return _no_change(name, 'network') | 
|  | 140 |  | 
|  | 141 |  | 
|  | 142 | @_test_call | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 143 | def network_absent(name, profile=None,endpoint_type=None): | 
|  | 144 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 145 | existing_network = _neutron_module_call( | 
|  | 146 | 'list_networks', name=name, **connection_args) | 
|  | 147 | if existing_network: | 
|  | 148 | _neutron_module_call( | 
|  | 149 | 'delete_network', existing_network[name]['id'], **connection_args) | 
|  | 150 | if _neutron_module_call('list_networks', name=name, **connection_args): | 
|  | 151 | return _delete_failed(name, 'network') | 
|  | 152 | return _deleted(name, 'network', existing_network[name]) | 
|  | 153 | return _absent(name, 'network') | 
|  | 154 |  | 
|  | 155 |  | 
|  | 156 | @_test_call | 
|  | 157 | def subnet_present(name=None, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 158 | tenant=None, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 159 | network=None, | 
|  | 160 | cidr=None, | 
|  | 161 | ip_version=4, | 
|  | 162 | enable_dhcp=True, | 
|  | 163 | allocation_pools=None, | 
|  | 164 | gateway_ip=None, | 
|  | 165 | dns_nameservers=None, | 
|  | 166 | host_routes=None, | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 167 | profile=None, | 
|  | 168 | endpoint_type=None): | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 169 | ''' | 
|  | 170 | Ensure that the neutron subnet is present with the specified properties. | 
|  | 171 | name | 
|  | 172 | The name of the subnet to manage | 
|  | 173 | ''' | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 174 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 175 | tenant_name = tenant | 
|  | 176 | try: | 
|  | 177 | tenant_id = __salt__['keystone.tenant_get']( | 
|  | 178 | name=tenant_name, **connection_args)[tenant_name]['id'] | 
|  | 179 | except: | 
|  | 180 | tenant_id = None | 
|  | 181 | LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format( | 
|  | 182 | connection_args['connection_user'])) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 183 | existing_subnet = _neutron_module_call( | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 184 | 'list_subnets', tenant_id=tenant_id, name=name, **connection_args) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 185 | subnet_arguments = _get_non_null_args( | 
|  | 186 | name=name, | 
|  | 187 | network=network, | 
|  | 188 | cidr=cidr, | 
|  | 189 | ip_version=ip_version, | 
|  | 190 | enable_dhcp=enable_dhcp, | 
|  | 191 | allocation_pools=allocation_pools, | 
|  | 192 | gateway_ip=gateway_ip, | 
|  | 193 | dns_nameservers=dns_nameservers, | 
|  | 194 | host_routes=host_routes) | 
|  | 195 | # replace network with network_id | 
|  | 196 | if 'network' in subnet_arguments: | 
|  | 197 | network = subnet_arguments.pop('network', None) | 
|  | 198 | existing_network = _neutron_module_call( | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 199 | 'list_networks', tenant_id=tenant_id, name=network, **connection_args) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 200 | if existing_network: | 
|  | 201 | subnet_arguments['network_id'] = existing_network[network]['id'] | 
|  | 202 | if not existing_subnet: | 
|  | 203 | subnet_arguments.update(connection_args) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 204 | _neutron_module_call('create_subnet', tenant_id=tenant_id, **subnet_arguments) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 205 | existing_subnet = _neutron_module_call( | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 206 | 'list_subnets', tenant_id=tenant_id, name=name, **connection_args) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 207 | if existing_subnet: | 
|  | 208 | return _created(name, 'subnet', existing_subnet[name]) | 
|  | 209 | return _update_failed(name, 'subnet') | 
|  | 210 | # change from internal representation | 
|  | 211 | existing_subnet = existing_subnet[name] | 
|  | 212 | # create differential | 
|  | 213 | LOG.error('existing ' + str(existing_subnet)) | 
|  | 214 | LOG.error('new ' + str(subnet_arguments)) | 
|  | 215 | diff = dict((key, value) for key, value in subnet_arguments.iteritems() | 
|  | 216 | if existing_subnet.get(key, None) != value) | 
|  | 217 | if diff: | 
|  | 218 | # update the changes | 
|  | 219 | subnet_arguments = diff.copy() | 
|  | 220 | subnet_arguments.update(connection_args) | 
|  | 221 | try: | 
|  | 222 | LOG.debug('updating subnet {0} with changes {1}'.format( | 
|  | 223 | name, str(diff))) | 
|  | 224 | _neutron_module_call('update_subnet', | 
|  | 225 | existing_subnet['id'], | 
|  | 226 | **subnet_arguments) | 
|  | 227 | changes_dict = _created(name, 'subnet', diff) | 
|  | 228 | changes_dict['comment'] = '{1} {0} updated'.format(name, 'subnet') | 
|  | 229 | return changes_dict | 
|  | 230 | except: | 
|  | 231 | LOG.exception('Could not update subnet {0}'.format(name)) | 
|  | 232 | return _update_failed(name, 'subnet') | 
|  | 233 | return _no_change(name, 'subnet') | 
|  | 234 |  | 
|  | 235 |  | 
|  | 236 | @_test_call | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 237 | def subnet_absent(name, profile=None, endpoint_type=None): | 
|  | 238 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 239 | existing_subnet = _neutron_module_call( | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 240 | 'list_subnets', tenant_id=tenant_id, name=name, **connection_args) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 241 | if existing_subnet: | 
|  | 242 | _neutron_module_call( | 
|  | 243 | 'delete_subnet', existing_subnet[name]['id'], **connection_args) | 
|  | 244 | if _neutron_module_call('list_subnets', name=name, **connection_args): | 
|  | 245 | return _delete_failed(name, 'subnet') | 
|  | 246 | return _deleted(name, 'subnet', existing_subnet[name]) | 
|  | 247 | return _absent(name, 'subnet') | 
|  | 248 | return _absent(name, 'network') | 
|  | 249 |  | 
|  | 250 |  | 
|  | 251 | @_test_call | 
|  | 252 | def router_present(name=None, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 253 | tenant=None, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 254 | gateway_network=None, | 
|  | 255 | interfaces=None, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 256 | admin_state_up=True, | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 257 | profile=None, | 
|  | 258 | endpoint_type=None): | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 259 | ''' | 
|  | 260 | Ensure that the neutron router is present with the specified properties. | 
|  | 261 | name | 
|  | 262 | The name of the subnet to manage | 
|  | 263 | gateway_network | 
|  | 264 | The network that would be the router's default gateway | 
|  | 265 | interfaces | 
|  | 266 | list of subnets the router attaches to | 
|  | 267 | ''' | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 268 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 269 | tenant_name = tenant | 
|  | 270 | try: | 
|  | 271 | tenant_id = __salt__['keystone.tenant_get']( | 
|  | 272 | name=tenant_name, **connection_args)[tenant_name]['id'] | 
|  | 273 | except: | 
|  | 274 | tenant_id = None | 
|  | 275 | LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format( | 
|  | 276 | connection_args['connection_user'])) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 277 | existing_router = _neutron_module_call( | 
|  | 278 | 'list_routers', name=name, **connection_args) | 
|  | 279 | if not existing_router: | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 280 | _neutron_module_call('create_router', name=name, tenant_id=tenant_id, admin_state_up=admin_state_up, **connection_args) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 281 | created_router = _neutron_module_call( | 
|  | 282 | 'list_routers', name=name, **connection_args) | 
|  | 283 | if created_router: | 
|  | 284 | router_id = created_router[name]['id'] | 
|  | 285 | network = _neutron_module_call( | 
|  | 286 | 'list_networks', name=gateway_network, **connection_args) | 
|  | 287 | gateway_network_id = network[gateway_network]['id'] | 
|  | 288 | _neutron_module_call('router_gateway_set', | 
|  | 289 | router_id=router_id, | 
|  | 290 | external_gateway=gateway_network_id, | 
|  | 291 | **connection_args) | 
|  | 292 | for interface in interfaces: | 
|  | 293 | subnet = _neutron_module_call( | 
|  | 294 | 'list_subnets', name=interface, **connection_args) | 
|  | 295 | subnet_id = subnet[interface]['id'] | 
|  | 296 | _neutron_module_call('router_add_interface', | 
|  | 297 | router_id=router_id, | 
|  | 298 | subnet_id=subnet_id, | 
|  | 299 | **connection_args) | 
|  | 300 | return _created(name, | 
|  | 301 | 'router', | 
|  | 302 | _neutron_module_call('list_routers', | 
|  | 303 | name=name, | 
|  | 304 | **connection_args)) | 
|  | 305 | return _create_failed(name, 'router') | 
|  | 306 |  | 
|  | 307 | router_id = existing_router[name]['id'] | 
|  | 308 | existing_router = existing_router[name] | 
|  | 309 | diff = {} | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 310 | if ( admin_state_up == True or admin_state_up == False ) and existing_router['admin_state_up'] != admin_state_up: | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 311 | diff.update({'admin_state_up': admin_state_up}) | 
|  | 312 | if gateway_network: | 
|  | 313 | network = _neutron_module_call( | 
|  | 314 | 'list_networks', name=gateway_network, **connection_args) | 
|  | 315 | gateway_network_id = network[gateway_network]['id'] | 
|  | 316 | if not existing_router['external_gateway_info'] and not existing_router['external_gateway_info'] == None: | 
|  | 317 | if existing_router['external_gateway_info']['network_id'] != gateway_network_id: | 
|  | 318 | diff.update({'external_gateway_info': {'network_id': gateway_network_id}}) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 319 | elif not existing_router['external_gateway_info'] == None: | 
|  | 320 | if not 'network_id' in existing_router['external_gateway_info'] or existing_router['external_gateway_info']['network_id'] != gateway_network_id: | 
|  | 321 | diff.update({'external_gateway_info': {'network_id': gateway_network_id}}) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 322 | if diff: | 
|  | 323 | # update the changes | 
|  | 324 | router_args = diff.copy() | 
|  | 325 | router_args.update(connection_args) | 
|  | 326 | try: | 
|  | 327 | _neutron_module_call('update_router', existing_router['id'], **router_args) | 
|  | 328 | changes_dict = _created(name, 'router', diff) | 
|  | 329 | changes_dict['comment'] = 'Router {0} updated'.format(name) | 
|  | 330 | return changes_dict | 
|  | 331 | except: | 
|  | 332 | LOG.exception('Router {0} could not be updated'.format(name)) | 
|  | 333 | return _update_failed(name, 'router') | 
|  | 334 | return _no_change(name, 'router') | 
|  | 335 |  | 
| Jiri Broulik | de2e290 | 2017-02-13 15:03:47 +0100 | [diff] [blame] | 336 |  | 
|  | 337 | def floatingip_present(name=None, | 
|  | 338 | tenant_name=None, | 
|  | 339 | subnet=None, | 
|  | 340 | tenant=None, | 
|  | 341 | network=None, | 
|  | 342 | port_id=None, | 
|  | 343 | fip_exists=False, | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 344 | profile=None, | 
|  | 345 | endpoint_type=None): | 
| Jiri Broulik | de2e290 | 2017-02-13 15:03:47 +0100 | [diff] [blame] | 346 | ''' | 
|  | 347 | Ensure that the floating ip address is present for an instance | 
|  | 348 | ''' | 
|  | 349 | instance_id = __salt__['novang.server_get'](name=name, tenant_name=tenant_name, profile=profile) | 
|  | 350 | subnet_name = subnet | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 351 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | de2e290 | 2017-02-13 15:03:47 +0100 | [diff] [blame] | 352 | existing_subnet = _neutron_module_call( | 
|  | 353 | 'list_subnets', name=subnet_name, **connection_args) | 
|  | 354 | subnet_id = existing_subnet[subnet_name]['id'] | 
|  | 355 |  | 
|  | 356 | ret = {} | 
|  | 357 | existing_ports = _neutron_module_call( | 
|  | 358 | 'list_ports', **connection_args) | 
|  | 359 | existing_floatingips = _neutron_module_call( | 
|  | 360 | 'list_floatingips', **connection_args) | 
|  | 361 |  | 
|  | 362 | tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args) | 
|  | 363 | tenant_id = tenant[tenant_name]['id'] | 
|  | 364 | existing_network = _neutron_module_call( | 
|  | 365 | 'list_networks', name=network, **connection_args) | 
|  | 366 | floating_network_id = existing_network[network]['id'] | 
|  | 367 |  | 
|  | 368 | for key, value in existing_ports.iteritems(): | 
|  | 369 | try: | 
|  | 370 | if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id: | 
|  | 371 | port_id=value['id'] | 
|  | 372 | except: | 
|  | 373 | pass | 
|  | 374 | for key, value in existing_floatingips.iteritems(): | 
|  | 375 | try: | 
|  | 376 | if value['floating_network_id'] == floating_network_id and value['port_id'] == port_id and value['tenant_id'] == tenant_id: | 
|  | 377 | fip_exists = True | 
|  | 378 | break | 
|  | 379 | except: | 
|  | 380 | pass | 
|  | 381 |  | 
|  | 382 | if fip_exists == False: | 
|  | 383 | for key, value in existing_ports.iteritems(): | 
|  | 384 | try: | 
|  | 385 | if value['fixed_ips'][0]['subnet_id'] == subnet_id and value['device_id'] == instance_id: | 
|  | 386 | ret = _neutron_module_call('create_floatingip', floating_network_id=floating_network_id, port_id=value['id'], tenant_id=tenant_id, **connection_args) | 
|  | 387 | except: | 
|  | 388 | pass | 
|  | 389 | return _created('port', 'floatingip', ret) | 
|  | 390 | else: | 
|  | 391 | return _no_change('for instance {0}'.format(name), 'floatingip') | 
|  | 392 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 393 | def security_group_present(name=None, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 394 | tenant=None, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 395 | description=None, | 
|  | 396 | rules=[], | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 397 | profile=None, | 
|  | 398 | endpoint_type=None): | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 399 | ''' | 
|  | 400 | Ensure that the security group is present with the specified properties. | 
|  | 401 | name | 
|  | 402 | The name of the security group | 
|  | 403 | description | 
|  | 404 | The description of the security group | 
|  | 405 | rules | 
|  | 406 | list of rules to be added to the given security group | 
|  | 407 | ''' | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 408 | # If the user is an admin, he's able to see the security groups from | 
|  | 409 | # other tenants. In this case, we'll use the tenant id to get an existing | 
|  | 410 | # security group. | 
| Richard Felkl | aac256a | 2017-03-23 15:43:49 +0100 | [diff] [blame] | 411 | connection_args = _auth(profile, endpoint_type) | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 412 | tenant_name = tenant | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 413 | try: | 
|  | 414 | tenant_id = __salt__['keystone.tenant_get']( | 
|  | 415 | name=tenant_name, **connection_args)[tenant_name]['id'] | 
|  | 416 | except: | 
|  | 417 | tenant_id = None | 
|  | 418 | LOG.debug('Cannot get the tenant id. User {0} is not an admin.'.format( | 
|  | 419 | connection_args['connection_user'])) | 
|  | 420 | if tenant_id: | 
|  | 421 | security_group = _neutron_module_call( | 
|  | 422 | 'list_security_groups', name=name, tenant_id=tenant_id, | 
|  | 423 | **connection_args) | 
|  | 424 | else: | 
|  | 425 | security_group = _neutron_module_call( | 
|  | 426 | 'list_security_groups', name=name, **connection_args) | 
|  | 427 |  | 
|  | 428 | if not security_group: | 
|  | 429 | # Create the security group as it doesn't exist already. | 
|  | 430 | security_group_id = _neutron_module_call('create_security_group', | 
|  | 431 | name=name, | 
|  | 432 | description=description, | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 433 | tenant_id=tenant_id, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 434 | **connection_args) | 
|  | 435 | else: | 
|  | 436 | security_group_id = security_group[name]['id'] | 
|  | 437 |  | 
|  | 438 | # Set the missing rules attributes (in case the user didn't specify them | 
|  | 439 | # in pillar) to some default values. | 
|  | 440 | rules_attributes_defaults = { | 
|  | 441 | 'direction': 'ingress', | 
|  | 442 | 'ethertype': 'IPv4', | 
|  | 443 | 'protocol': 'TCP', | 
|  | 444 | 'port_range_min': None, | 
|  | 445 | 'port_range_max': None, | 
|  | 446 | 'remote_ip_prefix': None | 
|  | 447 | } | 
|  | 448 | for rule in rules: | 
|  | 449 | for attribute in rules_attributes_defaults.keys(): | 
|  | 450 | if not rule.has_key(attribute): | 
|  | 451 | rule[attribute] = rules_attributes_defaults[attribute] | 
|  | 452 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 453 | # Remove all the duplicates rules given by the user in pillar. | 
|  | 454 | unique_rules = [] | 
|  | 455 | for rule in rules: | 
|  | 456 | if rule not in unique_rules: | 
|  | 457 | unique_rules.append(rule) | 
|  | 458 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 459 | # Get the existing security group rules. | 
|  | 460 | existing_rules = _neutron_module_call( | 
|  | 461 | 'list_security_groups', | 
|  | 462 | id=security_group_id, | 
|  | 463 | **connection_args)[name]['security_group_rules'] | 
|  | 464 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 465 | new_rules = {} | 
|  | 466 | for rule in unique_rules: | 
|  | 467 | rule_found = False | 
|  | 468 | for existing_rule in existing_rules: | 
|  | 469 | attributes_match = True | 
|  | 470 | # Compare the attributes of the existing security group rule with | 
|  | 471 | # the attributes of the rule that we want to add. | 
|  | 472 | for attribute in rules_attributes_defaults.keys(): | 
|  | 473 | existing_attribute = '' if not existing_rule[attribute] \ | 
|  | 474 | else str(existing_rule[attribute]).lower() | 
|  | 475 | attribute = '' if not rule[attribute] \ | 
|  | 476 | else str(rule[attribute]).lower() | 
|  | 477 | if existing_attribute != attribute: | 
|  | 478 | attributes_match = False | 
|  | 479 | break | 
|  | 480 | if attributes_match: | 
|  | 481 | rule_found = True | 
|  | 482 | break | 
|  | 483 | if rule_found: | 
|  | 484 | # Skip adding the rule as it already exists. | 
|  | 485 | continue | 
|  | 486 | rule_index = len(new_rules) + 1 | 
|  | 487 | new_rules.update({'Rule {0}'.format(rule_index): rule}) | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 488 | _neutron_module_call('create_security_group_rule', | 
|  | 489 | security_group_id=security_group_id, | 
|  | 490 | direction=rule['direction'], | 
|  | 491 | ethertype=rule['ethertype'], | 
|  | 492 | protocol=rule['protocol'], | 
|  | 493 | port_range_min=rule['port_range_min'], | 
|  | 494 | port_range_max=rule['port_range_max'], | 
|  | 495 | remote_ip_prefix=rule['remote_ip_prefix'], | 
| Jiri Broulik | 5368cc5 | 2017-02-08 18:53:59 +0100 | [diff] [blame] | 496 | tenant_id=tenant_id, | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 497 | **connection_args) | 
|  | 498 |  | 
| Jiri Broulik | f1b3aa4 | 2017-01-26 17:08:44 +0100 | [diff] [blame] | 499 | if not security_group: | 
|  | 500 | # The security group didn't exist. It was created and specified | 
|  | 501 | # rules were added to it. | 
|  | 502 | security_group = _neutron_module_call('list_security_groups', | 
|  | 503 | id=security_group_id, | 
|  | 504 | **connection_args)[name] | 
|  | 505 | return _created(name, 'security_group', security_group) | 
|  | 506 | if len(new_rules) == 0: | 
|  | 507 | # Security group already exists and specified rules are already | 
|  | 508 | # present. | 
|  | 509 | return _no_change(name, 'security_group') | 
|  | 510 | # Security group already exists, but the specified rules were added to it. | 
|  | 511 | return _updated(name, 'security_group', {'New Rules': new_rules}) | 
|  | 512 |  | 
|  | 513 | def _created(name, resource, resource_definition): | 
|  | 514 | changes_dict = {'name': name, | 
|  | 515 | 'changes': resource_definition, | 
|  | 516 | 'result': True, | 
|  | 517 | 'comment': '{0} {1} created'.format(resource, name)} | 
|  | 518 | return changes_dict | 
|  | 519 |  | 
|  | 520 | def _updated(name, resource, resource_definition): | 
|  | 521 | changes_dict = {'name': name, | 
|  | 522 | 'changes': resource_definition, | 
|  | 523 | 'result': True, | 
|  | 524 | 'comment': '{0} {1} updated'.format(resource, name)} | 
|  | 525 | return changes_dict | 
|  | 526 |  | 
|  | 527 | def _no_change(name, resource, test=False): | 
|  | 528 | changes_dict = {'name': name, | 
|  | 529 | 'changes': {}, | 
|  | 530 | 'result': True} | 
|  | 531 | if test: | 
|  | 532 | changes_dict['comment'] = \ | 
|  | 533 | '{0} {1} will be {2}'.format(resource, name, test) | 
|  | 534 | else: | 
|  | 535 | changes_dict['comment'] = \ | 
|  | 536 | '{0} {1} is in correct state'.format(resource, name) | 
|  | 537 | return changes_dict | 
|  | 538 |  | 
|  | 539 |  | 
|  | 540 | def _deleted(name, resource, resource_definition): | 
|  | 541 | changes_dict = {'name': name, | 
|  | 542 | 'changes': {}, | 
|  | 543 | 'comment': '{0} {1} removed'.format(resource, name), | 
|  | 544 | 'result': True} | 
|  | 545 | return changes_dict | 
|  | 546 |  | 
|  | 547 |  | 
|  | 548 | def _absent(name, resource): | 
|  | 549 | changes_dict = {'name': name, | 
|  | 550 | 'changes': {}, | 
|  | 551 | 'comment': '{0} {1} not present'.format(resource, name), | 
|  | 552 | 'result': True} | 
|  | 553 | return changes_dict | 
|  | 554 |  | 
|  | 555 |  | 
|  | 556 | def _delete_failed(name, resource): | 
|  | 557 | changes_dict = {'name': name, | 
|  | 558 | 'changes': {}, | 
|  | 559 | 'comment': '{0} {1} failed to delete'.format(resource, | 
|  | 560 | name), | 
|  | 561 | 'result': False} | 
|  | 562 | return changes_dict | 
|  | 563 |  | 
|  | 564 | def _create_failed(name, resource): | 
|  | 565 | changes_dict = {'name': name, | 
|  | 566 | 'changes': {}, | 
|  | 567 | 'comment': '{0} {1} failed to create'.format(resource, | 
|  | 568 | name), | 
|  | 569 | 'result': False} | 
|  | 570 | return changes_dict | 
|  | 571 |  | 
|  | 572 | def _update_failed(name, resource): | 
|  | 573 | changes_dict = {'name': name, | 
|  | 574 | 'changes': {}, | 
|  | 575 | 'comment': '{0} {1} failed to update'.format(resource, | 
|  | 576 | name), | 
|  | 577 | 'result': False} | 
|  | 578 | return changes_dict | 
|  | 579 |  | 
|  | 580 |  | 
|  | 581 | def _get_non_null_args(**kwargs): | 
|  | 582 | ''' | 
|  | 583 | Return those kwargs which are not null | 
|  | 584 | ''' | 
|  | 585 | return dict((key, value,) for key, value in kwargs.iteritems() | 
|  | 586 | if value is not None) |