-
Notifications
You must be signed in to change notification settings - Fork 87
Expand file tree
/
Copy pathnetworking.py
More file actions
624 lines (489 loc) · 22.7 KB
/
Copy pathnetworking.py
File metadata and controls
624 lines (489 loc) · 22.7 KB
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
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
from typing import Any, Dict, List, Optional, Union
from linode_api4.errors import UnexpectedResponseError
from linode_api4.groups import Group
from linode_api4.objects import (
VLAN,
Base,
Firewall,
FirewallCreateDevicesOptions,
FirewallSettings,
FirewallTemplate,
Instance,
IPAddress,
IPv6Pool,
IPv6Range,
NetworkTransferPrice,
Region,
)
from linode_api4.objects.base import _flatten_request_body_recursive
from linode_api4.objects.networking import ReservedIPAddress, ReservedIPType
from linode_api4.paginated_list import PaginatedList
from linode_api4.util import drop_null_keys
class NetworkingGroup(Group):
"""
Collections related to Linode Networking.
"""
def firewalls(self, *filters):
"""
Retrieves the Firewalls your user has access to.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-firewalls
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of Firewalls the acting user can access.
:rtype: PaginatedList of Firewall
"""
return self.client._get_and_filter(Firewall, *filters)
def firewall_create(
self,
label: str,
rules: Dict[str, Any],
devices: Optional[
Union[FirewallCreateDevicesOptions, Dict[str, Any]]
] = None,
**kwargs,
):
"""
Creates a new Firewall, either in the given Region or
attached to the given Instance.
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-firewalls
:param label: The label for the new Firewall.
:type label: str
:param rules: The rules to apply to the new Firewall. For more information on Firewall rules, see our `Firewalls Documentation`_.
:type rules: dict
:param devices: Represents devices to create created alongside a Linode Firewall.
:type devices: Optional[Union[FirewallCreateDevicesOptions, Dict[str, Any]]]
:returns: The new Firewall.
:rtype: Firewall
Example usage::
rules = {
'outbound': [
{
'action': 'ACCEPT',
'addresses': {
'ipv4': [
'0.0.0.0/0'
],
'ipv6': [
"ff00::/8"
]
},
'description': 'Allow HTTP out.',
'label': 'allow-http-out',
'ports': '80',
'protocol': 'TCP'
}
],
'outbound_policy': 'DROP',
'inbound': [],
'inbound_policy': 'DROP'
}
firewall = client.networking.firewall_create('my-firewall', rules)
.. _Firewalls Documentation: https://techdocs.akamai.com/linode-api/reference/post-firewalls
"""
params = {
"label": label,
"rules": rules,
"devices": devices,
}
params.update(kwargs)
result = self.client.post(
"/networking/firewalls",
data=drop_null_keys(_flatten_request_body_recursive(params)),
)
if not "id" in result:
raise UnexpectedResponseError(
"Unexpected response when creating Firewall!", json=result
)
f = Firewall(self.client, result["id"], result)
return f
def firewall_templates(self, *filters):
"""
Returns a list of Firewall Templates available to the current user.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-firewall-templates
NOTE: This feature may not currently be available to all users.
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of Firewall Templates available to the current user.
:rtype: PaginatedList of FirewallTemplate
"""
return self.client._get_and_filter(FirewallTemplate, *filters)
def firewall_settings(self) -> FirewallSettings:
"""
Returns an object representing the Linode Firewall settings for the current user.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-firewall-settings
NOTE: This feature may not currently be available to all users.
:returns: An object representing the Linode Firewall settings for the current user.
:rtype: FirewallSettings
"""
result = self.client.get("/networking/firewalls/settings")
if "default_firewall_ids" not in result:
raise UnexpectedResponseError(
"Unexpected response when getting firewall settings!",
json=result,
)
return FirewallSettings(self.client, None, result)
def ips(self, *filters):
"""
Returns a list of IP addresses on this account, excluding private addresses.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-ips
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of IP addresses on this account.
:rtype: PaginatedList of IPAddress
"""
return self.client._get_and_filter(IPAddress, *filters)
def ipv6_ranges(self, *filters):
"""
Returns a list of IPv6 ranges on this account.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-ipv6-ranges
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of IPv6 ranges on this account.
:rtype: PaginatedList of IPv6Range
"""
return self.client._get_and_filter(IPv6Range, *filters)
def ipv6_range_allocate(
self,
prefix_length: int,
route_target: Optional[str] = None,
linode: Optional[Union[Instance, int]] = None,
**kwargs,
) -> IPv6Range:
"""
Creates an IPv6 Range and assigns it based on the provided Linode or route target IPv6 SLAAC address.
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-ipv6-range
Create an IPv6 range assigned to a Linode by ID::
range = client.networking.ipv6_range_allocate(64, linode_id=123)
Create an IPv6 range assigned to a Linode by SLAAC::
range = client.networking.ipv6_range_allocate(
64,
route_target=instance.ipv6.split("/")[0]
)
:param prefix_length: The prefix length of the IPv6 range.
:type prefix_length: int
:param route_target: The IPv6 SLAAC address to assign this range to. Required if linode is not specified.
:type route_target: str
:param linode: The ID of the Linode to assign this range to.
The SLAAC address for the provided Linode is used as the range's route_target.
Required if linode is not specified.
:type linode: Instance or int
:returns: The new IPAddress.
:rtype: IPAddress
"""
params = {
"prefix_length": prefix_length,
"route_target": route_target,
"linode_id": linode,
}
params.update(**kwargs)
result = self.client.post(
"/networking/ipv6/ranges",
data=drop_null_keys(_flatten_request_body_recursive(params)),
)
if not "range" in result:
raise UnexpectedResponseError(
"Unexpected response when allocating IPv6 range!", json=result
)
result = IPv6Range(self.client, result["range"], result)
return result
def ipv6_pools(self, *filters):
"""
Returns a list of IPv6 pools on this account.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-ipv6-pools
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of IPv6 pools on this account.
:rtype: PaginatedList of IPv6Pool
"""
return self.client._get_and_filter(IPv6Pool, *filters)
def vlans(self, *filters):
"""
.. note:: This endpoint is in beta. This will only function if base_url is set to `https://api.linode.com/v4beta`.
Returns a list of VLANs on your account.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-vlans
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A List of VLANs on your account.
:rtype: PaginatedList of VLAN
"""
return self.client._get_and_filter(VLAN, *filters)
def ips_assign(self, region, *assignments):
"""
Redistributes :any:`IP Addressees<IPAddress>` within a single region.
This function takes a :any:`Region` and a list of assignments to make,
then requests that the assignments take place. If any :any:`Instance`
ends up without a public IP, or with more than one private IP, all of
the assignments will fail.
.. note::
This function *does not* update the local Linode Instance objects
when called. In order to see the new addresses on the local
instance objects, be sure to invalidate them with ``invalidate()``
after this completes.
Example usage::
linode1 = Instance(client, 123)
linode2 = Instance(client, 456)
# swap IPs between linodes 1 and 2
client.networking.assign_ips(linode1.region,
linode1.ips.ipv4.public[0].to(linode2),
linode2.ips.ipv4.public[0].to(linode1))
# make sure linode1 and linode2 have updated ipv4 and ips values
linode1.invalidate()
linode2.invalidate()
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-assign-ipv4s
:param region: The Region in which the assignments should take place.
All Instances and IPAddresses involved in the assignment
must be within this region.
:type region: str or Region
:param assignments: Any number of assignments to make. See
:any:`IPAddress.to` for details on how to construct
assignments.
:type assignments: dct
DEPRECATED: Use ip_addresses_assign() instead
"""
for a in assignments:
if not "address" in a or not "linode_id" in a:
raise ValueError("Invalid assignment: {}".format(a))
if isinstance(region, Region):
region = region.id
self.client.post(
"/networking/ipv4/assign",
data={
"region": region,
"assignments": assignments,
},
)
def ip_allocate(
self,
linode: Optional[Union[Instance, int]] = None,
public: bool = True,
reserved: bool = False,
region: Optional[Union[Region, str]] = None,
) -> IPAddress:
"""
Allocates an IP to an Instance you own, or reserves a new IP address.
When ``reserved`` is False (default), ``linode`` is required and an
ephemeral IP is allocated and assigned to that Instance.
When ``reserved`` is True, either ``region`` or ``linode`` must be
provided. Passing only ``region`` creates an unassigned reserved IP.
Passing ``linode`` (with or without ``region``) creates a reserved IP
in the Instance's region and assigns it to that Instance.
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-allocate-ip
:param linode: The Instance to allocate the new IP for.
:type linode: Instance or int
:param public: If True, allocate a public IP address. Defaults to True.
:type public: bool
:param reserved: If True, reserve the new IP address.
NOTE: Reserved IP feature may not currently be available to all users.
:type reserved: bool
:param region: The region for the reserved IP (required when reserved=True and linode is not set).
NOTE: Reserved IP feature may not currently be available to all users.
:type region: str or Region
:returns: The new IPAddress.
:rtype: IPAddress
"""
if not reserved and linode is None:
raise ValueError("linode is required when reserved is False.")
if reserved and linode is None and region is None:
raise ValueError(
"Either linode or region must be provided when reserved is True."
)
if not reserved and region is not None:
raise ValueError("region is only valid when reserved is True.")
data = {
"type": "ipv4",
"public": public,
}
if linode is not None:
data["linode_id"] = (
linode.id if isinstance(linode, Base) else linode
)
if reserved:
data["reserved"] = True
if region is not None:
data["region"] = region.id if isinstance(region, Base) else region
result = self.client.post("/networking/ips/", data=data)
if not "address" in result:
raise UnexpectedResponseError(
"Unexpected response when adding IPv4 address!", json=result
)
ip = IPAddress(self.client, result["address"], result)
return ip
def ips_share(self, linode, *ips):
"""
Shares the given list of :any:`IPAddresses<IPAddress>` with the provided
:any:`Instance`. This will enable the provided Instance to bring up the
shared IP Addresses even though it does not own them.
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-share-ipv4s
:param linode: The Instance to share the IPAddresses with. This Instance
will be able to bring up the given addresses.
:type: linode: int or Instance
:param ips: Any number of IPAddresses to share to the Instance.
:type ips: str or IPAddress
DEPRECATED: Use ip_addresses_share() instead
"""
if not isinstance(linode, Instance):
# make this an object
linode = Instance(self.client, linode)
params = []
for ip in ips:
if isinstance(ip, str):
params.append(ip)
elif isinstance(ip, IPAddress):
params.append(ip.address)
else:
params.append(str(ip)) # and hope that works
params = {"ips": params}
self.client.post(
"{}/networking/ipv4/share".format(Instance.api_endpoint),
model=linode,
data=params,
)
linode.invalidate() # clear the Instance's shared IPs
def ip_addresses_share(self, ips, linode):
"""
Configure shared IPs. IP sharing allows IP address reassignment
(also referred to as IP failover) from one Linode to another if the
primary Linode becomes unresponsive. This means that requests to the primary Linode’s
IP address can be automatically rerouted to secondary Linodes at the configured shared IP addresses.
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-share-ips
:param linode: The id of the Instance or the Instance to share the IPAddresses with.
This Instance will be able to bring up the given addresses.
:type: linode: int or Instance
:param ips: Any number of IPAddresses to share to the Instance. Enter an empty array to
remove all shared IP addresses.
:type ips: str or IPAddress
"""
shared_ips = []
for ip in ips:
if isinstance(ip, str):
shared_ips.append(ip)
elif isinstance(ip, IPAddress):
shared_ips.append(ip.address)
else:
shared_ips.append(str(ip)) # and hope that works
params = {
"ips": shared_ips,
"linode_id": (
linode if not isinstance(linode, Instance) else linode.id
),
}
self.client.post("/networking/ips/share", model=self, data=params)
def ip_addresses_assign(self, assignments, region):
"""
Assign multiple IPv4 addresses and/or IPv6 ranges to multiple Linodes in one Region.
This allows swapping, shuffling, or otherwise reorganizing IPs to your Linodes.
The following restrictions apply:
- All Linodes involved must have at least one public IPv4 address after assignment.
- Linodes may have no more than one assigned private IPv4 address.
- Linodes may have no more than one assigned IPv6 range.
:param region: The Region in which the assignments should take place.
All Instances and IPAddresses involved in the assignment
must be within this region.
:type region: str or Region
:param assignments: Any number of assignments to make. See
:any:`IPAddress.to` for details on how to construct
assignments.
:type assignments: list
"""
for a in assignments:
if not "address" in a or not "linode_id" in a:
raise ValueError("Invalid assignment: {}".format(a))
if isinstance(region, Region):
region = region.id
params = {"assignments": assignments, "region": region}
self.client.post("/networking/ips/assign", model=self, data=params)
def transfer_prices(self, *filters):
"""
Returns a :any:`PaginatedList` of :any:`NetworkTransferPrice` objects that represents a valid network transfer price.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-network-transfer-prices
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A Paginated List of network transfer price that match the query.
:rtype: PaginatedList of NetworkTransferPrice
"""
return self.client._get_and_filter(
NetworkTransferPrice, *filters, endpoint="/network-transfer/prices"
)
def delete_vlan(self, vlan, region):
"""
This operation deletes a VLAN.
You can't delete a VLAN if it's still attached to a Linode.
API Documentation: https://techdocs.akamai.com/linode-api/reference/delete-vlan
:param vlan: The label of the VLAN to be deleted.
:type vlan: str or VLAN
:param region: The VLAN's region.
:type region: str or Region
"""
if isinstance(region, Region):
region = region.id
if isinstance(vlan, VLAN):
vlan = vlan.label
resp = self.client.delete(
"/networking/vlans/{}/{}".format(region, vlan),
model=self,
)
if "error" in resp:
return False
return True
def reserved_ips(self, *filters) -> PaginatedList:
"""
Returns a list of reserved IPv4 addresses on your account.
NOTE: Reserved IP feature may not currently be available to all users.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-reserved-ips
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of reserved IP addresses on the account.
:rtype: PaginatedList of ReservedIPAddress
"""
return self.client._get_and_filter(ReservedIPAddress, *filters)
def reserved_ip_create(
self,
region: Union[Region, str],
tags: Optional[List[str]] = None,
**kwargs,
) -> ReservedIPAddress:
"""
Reserves a new IPv4 address in the given region.
NOTE: Reserved IP feature may not currently be available to all users.
API Documentation: https://techdocs.akamai.com/linode-api/reference/post-reserved-ip
:param region: The region in which to reserve the IP.
:type region: str or Region
:param tags: Tags to apply to the reserved IP.
:type tags: list of str
:returns: The new reserved IP address.
:rtype: ReservedIPAddress
"""
params = {
"region": region.id if isinstance(region, Region) else region,
}
if tags is not None:
params["tags"] = tags
params.update(kwargs)
result = self.client.post("/networking/reserved/ips", data=params)
if "address" not in result:
raise UnexpectedResponseError(
"Unexpected response when reserving IP address!", json=result
)
return ReservedIPAddress(self.client, result["address"], result)
def reserved_ip_types(self, *filters) -> PaginatedList:
"""
Returns a list of reserved IP types with pricing information.
NOTE: Reserved IP feature may not currently be available to all users.
API Documentation: https://techdocs.akamai.com/linode-api/reference/get-reserved-ip-types
:param filters: Any number of filters to apply to this query.
See :doc:`Filtering Collections</linode_api4/objects/filtering>`
for more details on filtering.
:returns: A list of reserved IP types.
:rtype: PaginatedList of ReservedIPType
"""
return self.client._get_and_filter(
ReservedIPType, *filters, endpoint="/networking/reserved/ips/types"
)