summaryrefslogtreecommitdiffstats
path: root/snaps/openstack/utils/neutron_utils.py
blob: 6c92d2e7aed7ac4454dde63bdcc321c84b06acbd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# Copyright (c) 2016 Cable Television Laboratories, Inc. ("CableLabs")
#                    and others.  All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging

from neutronclient.common.exceptions import NotFound
from neutronclient.neutron.client import Client
import keystone_utils

__author__ = 'spisarski'

logger = logging.getLogger('neutron_utils')

"""
Utilities for basic neutron API calls
"""


def neutron_client(os_creds):
    """
    Instantiates and returns a client for communications with OpenStack's Neutron server
    :param os_creds: the credentials for connecting to the OpenStack remote API
    :return: the client object
    """
    return Client(api_version=os_creds.network_api_version, session=keystone_utils.keystone_session(os_creds))


def create_network(neutron, os_creds, network_settings):
    """
    Creates a network for OpenStack
    :param neutron: the client
    :param os_creds: the OpenStack credentials
    :param network_settings: A dictionary containing the network configuration and is responsible for creating the
                            network request JSON body
    :return: the network object
    """
    if neutron and network_settings:
        logger.info('Creating network with name ' + network_settings.name)
        json_body = network_settings.dict_for_neutron(os_creds)
        return neutron.create_network(body=json_body)
    else:
        logger.error("Failed to create network")
        raise Exception


def delete_network(neutron, network):
    """
    Deletes a network for OpenStack
    :param neutron: the client
    :param network: the network object
    """
    if neutron and network:
        logger.info('Deleting network with name ' + network['network']['name'])
        neutron.delete_network(network['network']['id'])


def get_network(neutron, network_name, project_id=None):
    """
    Returns an object (dictionary) of the first network found with a given name and project_id (if included)
    :param neutron: the client
    :param network_name: the name of the network to retrieve
    :param project_id: the id of the network's project
    :return:
    """
    net_filter = dict()
    if network_name:
        net_filter['name'] = network_name
    if project_id:
        net_filter['project_id'] = project_id

    networks = neutron.list_networks(**net_filter)
    for network, netInsts in networks.iteritems():
        for inst in netInsts:
            if inst.get('name') == network_name:
                if project_id and inst.get('project_id') == project_id:
                    return {'network': inst}
                else:
                    return {'network': inst}
    return None


def get_network_by_id(neutron, network_id):
    """
    Returns the network object (dictionary) with the given ID
    :param neutron: the client
    :param network_id: the id of the network to retrieve
    :return:
    """
    networks = neutron.list_networks(**{'id': network_id})
    for network, netInsts in networks.iteritems():
        for inst in netInsts:
            if inst.get('id') == network_id:
                return {'network': inst}
    return None


def create_subnet(neutron, subnet_settings, os_creds, network=None):
    """
    Creates a network subnet for OpenStack
    :param neutron: the client
    :param network: the network object
    :param subnet_settings: A dictionary containing the subnet configuration and is responsible for creating the subnet
                            request JSON body
    :param os_creds: the OpenStack credentials
    :return: the subnet object
    """
    if neutron and network and subnet_settings:
        json_body = {'subnets': [subnet_settings.dict_for_neutron(os_creds, network=network)]}
        logger.info('Creating subnet with name ' + subnet_settings.name)
        subnets = neutron.create_subnet(body=json_body)
        return {'subnet': subnets['subnets'][0]}
    else:
        logger.error("Failed to create subnet.")
        raise Exception


def delete_subnet(neutron, subnet):
    """
    Deletes a network subnet for OpenStack
    :param neutron: the client
    :param subnet: the subnet object
    """
    if neutron and subnet:
        logger.info('Deleting subnet with name ' + subnet['subnet']['name'])
        neutron.delete_subnet(subnet['subnet']['id'])


def get_subnet_by_name(neutron, subnet_name):
    """
    Returns the first subnet object (dictionary) found with a given name
    :param neutron: the client
    :param subnet_name: the name of the network to retrieve
    :return:
    """
    subnets = neutron.list_subnets(**{'name': subnet_name})
    for subnet, subnetInst in subnets.iteritems():
        for inst in subnetInst:
            if inst.get('name') == subnet_name:
                return {'subnet': inst}
    return None


def create_router(neutron, os_creds, router_settings):
    """
    Creates a router for OpenStack
    :param neutron: the client
    :param os_creds: the OpenStack credentials
    :param router_settings: A dictionary containing the router configuration and is responsible for creating the subnet
                            request JSON body
    :return: the router object
    """
    if neutron:
        json_body = router_settings.dict_for_neutron(neutron, os_creds)
        logger.info('Creating router with name - ' + router_settings.name)
        return neutron.create_router(json_body)
    else:
        logger.error("Failed to create router.")
        raise Exception


def delete_router(neutron, router):
    """
    Deletes a router for OpenStack
    :param neutron: the client
    :param router: the router object
    """
    if neutron and router:
        logger.info('Deleting router with name - ' + router['router']['name'])
        neutron.delete_router(router=router['router']['id'])
        return True


def get_router_by_name(neutron, router_name):
    """
    Returns the first router object (dictionary) found with a given name
    :param neutron: the client
    :param router_name: the name of the network to retrieve
    :return:
    """
    routers = neutron.list_routers(**{'name': router_name})
    for router, routerInst in routers.iteritems():
        for inst in routerInst:
            if inst.get('name') == router_name:
                return {'router': inst}
    return None


def add_interface_router(neutron, router, subnet=None, port=None):
    """
    Adds an interface router for OpenStack for either a subnet or port. Exception will be raised if requesting for both.
    :param neutron: the client
    :param router: the router object
    :param subnet: the subnet object
    :param port: the port object
    :return: the interface router object
    """
    if subnet and port:
        raise Exception('Cannot add interface to the router. Both subnet and port were sent in. Either or please.')

    if neutron and router and (router or subnet):
        logger.info('Adding interface to router with name ' + router['router']['name'])
        return neutron.add_interface_router(router=router['router']['id'], body=__create_port_json_body(subnet, port))
    else:
        raise Exception("Unable to create interface router as neutron client, router or subnet were not created")


def remove_interface_router(neutron, router, subnet=None, port=None):
    """
    Removes an interface router for OpenStack
    :param neutron: the client
    :param router: the router object
    :param subnet: the subnet object (either subnet or port, not both)
    :param port: the port object
    """
    if router:
        try:
            logger.info('Removing router interface from router named ' + router['router']['name'])
            neutron.remove_interface_router(router=router['router']['id'], body=__create_port_json_body(subnet, port))
        except NotFound as e:
            logger.warn('Could not remove router interface. NotFound - ' + e.message)
            pass
    else:
        logger.warn('Could not remove router interface, No router object')


def __create_port_json_body(subnet=None, port=None):
    """
    Returns the dictionary required for creating and deleting router interfaces. Will only work on a subnet or port
    object. Will throw and exception if parameters contain both or neither
    :param subnet: the subnet object
    :param port: the port object
    :return: the dict
    """
    if subnet and port:
        raise Exception('Cannot create JSON body with both subnet and port')
    if not subnet and not port:
        raise Exception('Cannot create JSON body without subnet or port')

    if subnet:
        return {"subnet_id": subnet['subnet']['id']}
    else:
        return {"port_id": port['port']['id']}


def create_port(neutron, os_creds, port_settings):
    """
    Creates a port for OpenStack
    :param neutron: the client
    :param os_creds: the OpenStack credentials
    :param port_settings: the settings object for port configuration
    :return: the port object
    """
    json_body = port_settings.dict_for_neutron(neutron, os_creds)
    logger.info('Creating port for network with name - ' + port_settings.network_name)
    return neutron.create_port(body=json_body)


def delete_port(neutron, port):
    """
    Removes an OpenStack port
    :param neutron: the client
    :param port: the port object
    :return:
    """
    logger.info('Deleting port with name ' + port['port']['name'])
    neutron.delete_port(port['port']['id'])


def get_port_by_name(neutron, port_name):
    """
    Returns the first port object (dictionary) found with a given name
    :param neutron: the client
    :param port_name: the name of the port to retrieve
    :return:
    """
    ports = neutron.list_ports(**{'name': port_name})
    for port in ports['ports']:
        if port['name'] == port_name:
            return {'port': port}
    return None


def create_security_group(neutron, keystone, sec_grp_settings):
    """
    Creates a security group object in OpenStack
    :param neutron: the Neutron client
    :param keystone: the Keystone client
    :param sec_grp_settings: the security group settings
    :return: the security group object
    """
    logger.info('Creating security group with name - ' + sec_grp_settings.name)
    return neutron.create_security_group(sec_grp_settings.dict_for_neutron(keystone))


def delete_security_group(neutron, sec_grp):
    """
    Deletes a security group object from OpenStack
    :param neutron: the client
    :param sec_grp: the security group object to delete
    """
    logger.info('Deleting security group with name - ' + sec_grp['security_group']['name'])
    return neutron.delete_security_group(sec_grp['security_group']['id'])


def get_security_group(neutron, name):
    """
    Returns the first security group object of the given name else None
    :param neutron: the client
    :param name: the name of security group object to retrieve
    """
    logger.info('Retrieving security group with name - ' + name)

    groups = neutron.list_security_groups(**{'name': name})
    for group in groups['security_groups']:
        if group['name'] == name:
            return {'security_group': group}
    return None


def get_security_group_by_id(neutron, sec_grp_id):
    """
    Returns the first security group object of the given name else None
    :param neutron: the client
    :param sec_grp_id: the id of the security group to retrieve
    """
    logger.info('Retrieving security group with ID - ' + sec_grp_id)

    groups = neutron.list_security_groups(**{'sec_grp_id': sec_grp_id})
    for group in groups['security_groups']:
        return {'security_group': group}
    return None


def create_security_group_rule(neutron, sec_grp_rule_settings):
    """
    Creates a security group object in OpenStack
    :param neutron: the client
    :param sec_grp_rule_settings: the security group rule settings
    :return: the security group object
    """
    logger.info('Creating security group to security group - ' + sec_grp_rule_settings.sec_grp_name)
    return neutron.create_security_group_rule(sec_grp_rule_settings.dict_for_neutron(neutron))


def delete_security_group_rule(neutron, sec_grp_rule):
    """
    Deletes a security group object from OpenStack
    :param neutron: the client
    :param sec_grp_rule: the security group rule object to delete
    """
    logger.info('Deleting security group rule with ID - ' + sec_grp_rule['security_group_rule']['id'])
    neutron.delete_security_group_rule(sec_grp_rule['security_group_rule']['id'])


def get_rules_by_security_group(neutron, sec_grp):
    """
    Retrieves all of the rules for a given security group
    :param neutron: the client
    :param sec_grp: the security group object
    """
    logger.info('Retrieving security group rules associate with the security group - ' +
                sec_grp['security_group']['name'])
    out = list()
    rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
    for rule in rules['security_group_rules']:
        if rule['security_group_id'] == sec_grp['security_group']['id']:
            out.append({'security_group_rule': rule})
    return out


def get_rule_by_id(neutron, sec_grp, rule_id):
    """
    Deletes a security group object from OpenStack
    :param neutron: the client
    :param sec_grp: the security group object
    :param rule_id: the rule's ID
    """
    rules = neutron.list_security_group_rules(**{'security_group_id': sec_grp['security_group']['id']})
    for rule in rules['security_group_rules']:
        if rule['id'] == rule_id:
            return {'security_group_rule': rule}
    return None


def get_external_networks(neutron):
    """
    Returns a list of external OpenStack network object/dict for all external networks
    :param neutron: the client
    :return: a list of external networks (empty list if none configured)
    """
    out = list()
    for network in neutron.list_networks(**{'router:external': True})['networks']:
        out.append({'network': network})
    return out