blob: 60eec7d3fdd752512279bcfddf37aeeb79085b84 [file] [log] [blame]
Zsolt Haraszti7eeb2b32016-11-06 14:04:55 -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"""
18Interface definition for Voltha Adapters
19"""
Zsolt Haraszti00d9a842016-11-23 11:18:23 -080020import structlog
21from twisted.internet.defer import inlineCallbacks, returnValue
Zsolt Haraszti7eeb2b32016-11-06 14:04:55 -080022from zope.interface import Interface
Zsolt Haraszti00d9a842016-11-23 11:18:23 -080023from zope.interface import implementer
24
25from voltha.protos import third_party
26from voltha.protos.device_pb2 import Device, Port
27from voltha.protos.openflow_13_pb2 import ofp_port
28from voltha.protos.voltha_pb2 import DeviceGroup, LogicalDevice
29from voltha.registry import registry
30
31
32log = structlog.get_logger()
Zsolt Haraszti7eeb2b32016-11-06 14:04:55 -080033
34
35class IAdapterInterface(Interface):
36 """
37 A Voltha adapter
38 """
39
40 def start():
41 """
42 Called once after adapter instance is laoded. Can be used to async
43 initialization.
44 :return: (None or Deferred)
45 """
46
47 def stop():
48 """
49 Called once before adapter is unloaded. It can be used to perform
50 any cleanup after the adapter.
51 :return: (None or Deferred)
52 """
53
54 def adapter_descriptor():
55 """
56 Return the adapter descriptor object for this adapter.
57 :return: voltha.Adapter grpc object (see voltha/protos/adapter.proto),
58 with adapter-specific information and config extensions.
59 """
60
61 def device_types():
62 """
63 Return list of device types supported by the adapter.
64 :return: voltha.DeviceTypes protobuf object, with optional type
65 specific extensions.
66 """
67
68 def health():
69 """
70 Return a 3-state health status using the voltha.HealthStatus message.
71 :return: Deferred or direct return with voltha.HealthStatus message
72 """
73
74 def change_master_state(master):
75 """
76 Called to indicate if plugin shall assume or lose master role. The
77 master role can be used to perform functions that must be performed
78 from a single point in the cluster. In single-node deployments of
79 Voltha, the plugins are always in master role.
80 :param master: (bool) True to indicate the mastership needs to be
81 assumed; False to indicate that mastership needs to be abandoned.
82 :return: (Deferred) which is fired by the adapter when mastership is
83 assumed/dropped, respectively.
84 """
85
86 def adopt_device(device):
87 """
88 Make sure the adapter looks after given device. Called when a device
89 is provisioned top-down and needs to be activated by the adapter.
90 :param device: A voltha.Device object, with possible device-type
91 specific extensions. Such extensions shall be described as part of
92 the device type specification returned by device_types().
93 :return: (Deferred) Shall be fired to acknowledge device ownership.
94 """
95
96 def abandon_device(device):
97 """
98 Make sur ethe adapter no longer looks after device. This is called
99 if device ownership is taken over by another Voltha instance.
100 :param device: A Voltha.Device object.
101 :return: (Deferred) Shall be fired to acknowledge abandonment.
102 """
103
104 def deactivate_device(device):
105 """
106 Called if the device is to be deactivate based on a NBI call.
107 :return: (Deferred) Shall be fired to acknowledge deactivation.
108 """
109
110 # TODO work in progress
111 # ...
Zsolt Haraszti00d9a842016-11-23 11:18:23 -0800112
113
114class IAdapterProxy(Interface):
115 """
116 This object is passed in to the __init__ function of each adapter,
117 and can be used by the adapter implementation to initiate async calls
118 toward Voltha's CORE via the APIs defined here.
119 """
120
121 def create_device(device):
122 # TODO add doc
123 """"""
124
125 def add_port(device_id, port):
126 # TODO add doc
127 """"""
128
129 def create_logical_device(logical_device):
130 # TODO add doc
131 """"""
132
133 def add_logical_port(logical_device_id, port):
134 # TODO add doc
135 """"""
136
137 # TODO work in progress
138 pass
139
140
141@implementer(IAdapterProxy)
142class AdapterProxy(object):
143 """
144 Gate-keeper between CORE and device adapters.
145
146 On one side it interacts with Core's internal model and update/dispatch
147 mechanisms.
148
149 On the other side, it interacts with the adapters standard interface as
150 defined in
151 """
152
153 def __init__(self, adapter_name, adapter_cls):
154 self.adapter_name = adapter_name
155 self.adapter_cls = adapter_cls
156 self.core = registry('core')
157 self.adapter = None
158 self.adapter_node_proxy = None
159
160 @inlineCallbacks
161 def start(self):
162 log.debug('starting')
163 config = self._get_adapter_config() # this may be None
164 adapter = self.adapter_cls(self, config)
165 yield adapter.start()
166 self.adapter = adapter
167 self.adapter_node_proxy = self._update_adapter_node()
168 self._update_device_types()
169 log.info('started')
170 returnValue(self)
171
172 @inlineCallbacks
173 def stop(self):
174 log.debug('stopping')
175 if self.adapter is not None:
176 yield self.adapter.stop()
177 self.adapter = None
178 log.info('stopped')
179
180 def _get_adapter_config(self):
181 """
182 Opportunistically load persisted adapter configuration.
183 Return None if no configuration exists yet.
184 """
185 proxy = self.core.get_proxy('/')
186 try:
187 config = proxy.get('/adapters/' + self.adapter_name)
188 return config
189 except KeyError:
190 return None
191
192 def _update_adapter_node(self):
193 """
194 Creates or updates the adapter node object based on self
195 description from the adapter.
196 """
197
198 adapter_desc = self.adapter.adapter_descriptor()
199 assert adapter_desc.id == self.adapter_name
200 path = self._make_up_to_date(
201 '/adapters', self.adapter_name, adapter_desc)
202 return self.core.get_proxy(path)
203
204 def _update_device_types(self):
205 """
206 Make sure device types are registered in Core
207 """
208 device_types = self.adapter.device_types()
209 for device_type in device_types.items:
210 key = device_type.id
211 self._make_up_to_date('/device_types', key, device_type)
212
213 def _make_up_to_date(self, container_path, key, data):
214 full_path = container_path + '/' + str(key)
215 root_proxy = self.core.get_proxy('/')
216 try:
217 root_proxy.get(full_path)
218 root_proxy.update(full_path, data)
219 except KeyError:
220 root_proxy.add(container_path, data)
221 return full_path
222
223 # ~~~~~~~~~~~~~~~~~ Adapter-Facing Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224
225 def create_device(self, device):
226 assert isinstance(device, Device)
227 self._make_up_to_date('/devices', device.id, device)
228
229 # TODO for now, just map everything into a single device group
230 # which we create if it does not yet exist
231
232 dg = DeviceGroup(id='1')
233 self._make_up_to_date('/device_groups', dg.id, dg)
234
235 # add device to device group
236 # TODO how to do that?
237
238 def create_logical_device(self, logical_device):
239 assert isinstance(logical_device, LogicalDevice)
240 self._make_up_to_date('/logical_devices',
241 logical_device.id, logical_device)
242
243 # TODO link logical device to root device and back...
244
245 def add_port(self, device_id, port):
246 assert isinstance(port, Port)
247 self._make_up_to_date('/devices/{}/ports'.format(device_id),
248 port.id, port)
249
250 def add_logical_port(self, logical_device_id, port):
251 assert isinstance(port, ofp_port)
252 self._make_up_to_date(
253 '/logical_devices/{}/ports'.format(logical_device_id),
254 port.port_no, port)
255