blob: 319f0845c04a59b5497ab629a1617420e07124dc [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
130 # ~~~~~~~~~~~~~~~~~~~ Adapter-Facing Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
131
132 def get_device(self, device_id):
133 return self.root_proxy.get('/devices/{}'.format(device_id))
134
135 def add_device(self, device):
136 assert isinstance(device, Device)
137 self._make_up_to_date('/devices', device.id, device)
138
139 # TODO for now, just map everything into a single device group
140 # which we create if it does not yet exist
141
142 dg = DeviceGroup(id='1')
143 self._make_up_to_date('/device_groups', dg.id, dg)
144
145 # add device to device group
146 # TODO how to do that?
147
148 def update_device(self, device):
149 assert isinstance(device, Device)
150
151 # we run the update through the device_agent so that the change
152 # does not loop back to the adapter unnecessarily
153 device_agent = self.core.get_device_agent(device.id)
154 device_agent.update_device(device)
155
156 def remove_device(self, device_id):
157 device_agent = self.core.get_device_agent(device_id)
158 device_agent.remove_device(device_id)
159
160 def add_port(self, device_id, port):
161 assert isinstance(port, Port)
162
163 # for referential integrity, add/augment references
164 port.device_id = device_id
165 me_as_peer = Port.PeerPort(device_id=device_id, port_no=port.port_no)
166 for peer in port.peers:
167 peer_port_path = '/devices/{}/ports/{}'.format(
168 peer.device_id, peer.port_no)
169 peer_port = self.root_proxy.get(peer_port_path)
170 if me_as_peer not in peer_port.peers:
171 new = peer_port.peers.add()
172 new.CopyFrom(me_as_peer)
173 self.root_proxy.update(peer_port_path, peer_port)
174
175 self._make_up_to_date('/devices/{}/ports'.format(device_id),
176 port.port_no, port)
177
178 def create_logical_device(self, logical_device):
179 assert isinstance(logical_device, LogicalDevice)
180 self._make_up_to_date('/logical_devices',
181 logical_device.id, logical_device)
182
183 def add_logical_port(self, logical_device_id, port):
184 assert isinstance(port, LogicalPort)
185 self._make_up_to_date(
186 '/logical_devices/{}/ports'.format(logical_device_id),
187 port.id, port)
188
189 def child_device_detected(self,
190 parent_device_id,
191 parent_port_no,
192 child_device_type,
193 child_device_address_kw):
194 # we create new ONU device objects and insert them into the config
195 # TODO should we auto-enable the freshly created device? Probably
196 device = Device(
197 id=uuid4().hex[:12],
198 type=child_device_type,
199 parent_id=parent_device_id,
200 parent_port_no=parent_port_no,
201 admin_state=AdminState.ENABLED,
202 **child_device_address_kw
203 )
204 self._make_up_to_date(
205 '/devices', device.id, device)