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
|
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017 Cisco and/or its affiliates.
#
# 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 math
import logging
from vicn.core.resource import Resource
from netmodel.model.type import String, Bool
from vicn.core.attribute import Attribute
from vicn.resource.ip.prefix_tree import Inet6Prefix, PrefixTree, Inet4Prefix
from netmodel.model.type import Inet4Address, Inet6Address
from vicn.core.task import inline_task, async_task, EmptyTask
from vicn.core.task import inherit_parent
from vicn.core.exception import ResourceNotFound
log = logging.getLogger(__name__)
class IpAssignment(Resource):
prefix = Attribute(String, mandatory=True)
control_prefix = Attribute(String, description="prefix for control plane")
max_prefix_len = Attribute(String,
description="Maximum assigned prefix size for a link")
disjoint_addresses = Attribute(Bool, default=False)
PrefixClass = None
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._prefix = self.PrefixClass(self.prefix)
self._prefix_tree = PrefixTree(self._prefix)
self._assigned_prefixes = {}
if not self.max_prefix_len:
self.max_prefix_len = self.PrefixClass.MAX_PREFIX_SIZE
if self.control_prefix:
self._ctrl_prefix = self.PrefixClass(self.control_prefix)
self._ctrl_prefix_it = iter(self._ctrl_prefix)
next(self._ctrl_prefix_it) #Removes internet address
self._assigned_addresses = {}
def get_prefix(self, obj, prefix_len):
ret = None
if obj in self._assigned_prefixes:
ret = self._assigned_prefixes[obj]
else:
ret = self._prefix_tree.get_prefix(prefix_len)
self._assigned_prefixes[obj] = ret
return ret
def get_control_address(self, obj):
ret = None
if not self.control_prefix:
raise RuntimeError("No control prefix given")
try:
ret = self._assigned_addresses[obj]
except KeyError:
ret = next(self._ctrl_prefix_it)
self._assigned_addresses[obj] = ret
return ret
@inherit_parent
@inline_task
def __get__(self):
raise ResourceNotFound
@inherit_parent
#@inline_task
def __create__(self):
# XXX code from Channel.__create__, until Events are properly implemented.
# Iterate on channels for allocate IP addresses
task = EmptyTask()
for group in self.groups:
for channel in group.iter_by_type_str('channel'):
if channel.has_type("emulatedchannel"):
interfaces = [channel._ap_if]
interfaces.extend(channel._sta_ifs.values())
else:
interfaces = sorted(channel.interfaces, key = lambda x : x.device_name)
if not interfaces:
continue
num_required_ip = len(interfaces)
if num_required_ip > 2:
# We must reserve the first IP (the internet address) and the last one (the broadcast address)
# It does not apply in the case of 2 addresses as you do not need broadcast on the point2point link
num_required_ip = num_required_ip + 2
if channel.has_type("emulatedchannel"):
num_required_ip = num_required_ip + channel.nb_base_stations
min_prefix_len = math.ceil(math.log(num_required_ip, 2))
prefix_len = min(self.max_prefix_len,
self.PrefixClass.MAX_PREFIX_SIZE - min_prefix_len)
#XXX lte-emulator expects /24
if channel.has_type("emulatedltechannel") and self.PrefixClass is Inet4Prefix:
prefix_len = 24
# Retrieve a prefix for the whole channel
prefix = self.get_prefix(channel, prefix_len)
# by default iterate on /32 or /128, unless we require to
# iterate on less specific
it_prefix_len = self.PrefixClass.MAX_PREFIX_SIZE
if self.disjoint_addresses and prefix_len+min_prefix_len <= self.PrefixClass.MAX_PREFIX_SIZE:
it_prefix_len = min_prefix_len + prefix_len
# Use an iterator to assign IPs from the prefix to the
# interfaces
# Skip internet address if necessary, i.e., if prefix contains more than 2 addresses
skip_internet_address = (prefix_len < self.PrefixClass.MAX_PREFIX_SIZE -1)
it = prefix.get_iterator(it_prefix_len, skip_internet_address = skip_internet_address)
for interface in interfaces:
if_prefix = next(it)
if_prefix.prefix_len = prefix_len
if self.PrefixClass is Inet6Prefix:
address = Inet6Address(if_prefix.ip_address, prefix_len)
else:
address = Inet4Address(if_prefix.ip_address, prefix_len)
@async_task
async def set_ip(interface, address):
await interface.async_set(self.ATTR_ADDRESS, address)
task = task | set_ip(interface, address)
return task
class Ipv6Assignment(IpAssignment):
PrefixClass = Inet6Prefix
ATTR_ADDRESS = 'ip6_address'
class Ipv4Assignment(IpAssignment):
PrefixClass = Inet4Prefix
ATTR_ADDRESS = 'ip4_address'
|