blob: 5659f85cc457cb1e327de3bdc093ebb40d54634d [file] [log] [blame]
Zsolt Haraszti66862032016-11-28 14:28:39 -08001#
2# Copyright 2016 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""
18Agent to play gateway between CORE and an individual adapter.
19"""
20from uuid import uuid4
21
22import structlog
23from twisted.internet.defer import inlineCallbacks, returnValue
24from zope.interface import implementer
25
26from voltha.adapters.interface import IAdapterAgent
27from voltha.protos import third_party
28from voltha.protos.device_pb2 import Device, Port
29from voltha.protos.voltha_pb2 import DeviceGroup, LogicalDevice, \
30 LogicalPort, AdminState
31from voltha.registry import registry
32
33
34log = structlog.get_logger()
35
36@implementer(IAdapterAgent)
37class AdapterAgent(object):
38 """
39 Gate-keeper between CORE and device adapters.
40
41 On one side it interacts with Core's internal model and update/dispatch
42 mechanisms.
43
44 On the other side, it interacts with the adapters standard interface as
45 defined in
46 """
47
48 def __init__(self, adapter_name, adapter_cls):
49 self.adapter_name = adapter_name
50 self.adapter_cls = adapter_cls
51 self.core = registry('core')
52 self.adapter = None
53 self.adapter_node_proxy = None
54 self.root_proxy = self.core.get_proxy('/')
55
56 @inlineCallbacks
57 def start(self):
58 log.debug('starting')
59 config = self._get_adapter_config() # this may be None
60 adapter = self.adapter_cls(self, config)
61 yield adapter.start()
62 self.adapter = adapter
63 self.adapter_node_proxy = self._update_adapter_node()
64 self._update_device_types()
65 log.info('started')
66 returnValue(self)
67
68 @inlineCallbacks
69 def stop(self):
70 log.debug('stopping')
71 if self.adapter is not None:
72 yield self.adapter.stop()
73 self.adapter = None
74 log.info('stopped')
75
76 def _get_adapter_config(self):
77 """
78 Opportunistically load persisted adapter configuration.
79 Return None if no configuration exists yet.
80 """
81 proxy = self.core.get_proxy('/')
82 try:
83 config = proxy.get('/adapters/' + self.adapter_name)
84 return config
85 except KeyError:
86 return None
87
88 def _update_adapter_node(self):
89 """
90 Creates or updates the adapter node object based on self
91 description from the adapter.
92 """
93
94 adapter_desc = self.adapter.adapter_descriptor()
95 assert adapter_desc.id == self.adapter_name
96 path = self._make_up_to_date(
97 '/adapters', self.adapter_name, adapter_desc)
98 return self.core.get_proxy(path)
99
100 def _update_device_types(self):
101 """
102 Make sure device types are registered in Core
103 """
104 device_types = self.adapter.device_types()
105 for device_type in device_types.items:
106 key = device_type.id
107 self._make_up_to_date('/device_types', key, device_type)
108
109 def _make_up_to_date(self, container_path, key, data):
110 full_path = container_path + '/' + str(key)
111 root_proxy = self.core.get_proxy('/')
112 try:
113 root_proxy.get(full_path)
114 root_proxy.update(full_path, data)
115 except KeyError:
116 root_proxy.add(container_path, data)
117 return full_path
118
119 # ~~~~~~~~~~~~~~~~~~~~~ Core-Facing Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120
121 def adopt_device(self, device):
122 return self.adapter.adopt_device(device)
123
124 def abandon_device(self, device):
125 return self.adapter.abandon_device(device)
126
127 def deactivate_device(self, device):
128 return self.adapter.deactivate_device(device)
129
Zsolt Harasztic5c5d102016-12-07 21:12:27 -0800130 def update_flows_bulk(self, device, flows, groups):
131 return self.adapter.update_flows_bulk(device, flows, groups)
132
133 def update_flows_incrementally(self, device, flow_changes, group_changes):
134 return self.update_flows_incrementally(
135 device, flow_changes, group_changes)
136
Zsolt Haraszti66862032016-11-28 14:28:39 -0800137 # ~~~~~~~~~~~~~~~~~~~ Adapter-Facing Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
138
139 def get_device(self, device_id):
140 return self.root_proxy.get('/devices/{}'.format(device_id))
141
142 def add_device(self, device):
143 assert isinstance(device, Device)
144 self._make_up_to_date('/devices', device.id, device)
145
146 # TODO for now, just map everything into a single device group
147 # which we create if it does not yet exist
148
149 dg = DeviceGroup(id='1')
150 self._make_up_to_date('/device_groups', dg.id, dg)
151
152 # add device to device group
153 # TODO how to do that?
154
155 def update_device(self, device):
156 assert isinstance(device, Device)
157
158 # we run the update through the device_agent so that the change
159 # does not loop back to the adapter unnecessarily
160 device_agent = self.core.get_device_agent(device.id)
161 device_agent.update_device(device)
162
163 def remove_device(self, device_id):
164 device_agent = self.core.get_device_agent(device_id)
165 device_agent.remove_device(device_id)
166
167 def add_port(self, device_id, port):
168 assert isinstance(port, Port)
169
170 # for referential integrity, add/augment references
171 port.device_id = device_id
172 me_as_peer = Port.PeerPort(device_id=device_id, port_no=port.port_no)
173 for peer in port.peers:
174 peer_port_path = '/devices/{}/ports/{}'.format(
175 peer.device_id, peer.port_no)
176 peer_port = self.root_proxy.get(peer_port_path)
177 if me_as_peer not in peer_port.peers:
178 new = peer_port.peers.add()
179 new.CopyFrom(me_as_peer)
180 self.root_proxy.update(peer_port_path, peer_port)
181
182 self._make_up_to_date('/devices/{}/ports'.format(device_id),
183 port.port_no, port)
184
185 def create_logical_device(self, logical_device):
186 assert isinstance(logical_device, LogicalDevice)
187 self._make_up_to_date('/logical_devices',
188 logical_device.id, logical_device)
189
190 def add_logical_port(self, logical_device_id, port):
191 assert isinstance(port, LogicalPort)
192 self._make_up_to_date(
193 '/logical_devices/{}/ports'.format(logical_device_id),
194 port.id, port)
195
196 def child_device_detected(self,
197 parent_device_id,
198 parent_port_no,
199 child_device_type,
200 child_device_address_kw):
201 # we create new ONU device objects and insert them into the config
202 # TODO should we auto-enable the freshly created device? Probably
203 device = Device(
204 id=uuid4().hex[:12],
205 type=child_device_type,
206 parent_id=parent_device_id,
207 parent_port_no=parent_port_no,
208 admin_state=AdminState.ENABLED,
209 **child_device_address_kw
210 )
211 self._make_up_to_date(
212 '/devices', device.id, device)