blob: 57703a1b96a5512f8a76d3595d84fd312a33d7ca [file] [log] [blame]
Carmelo Casconef7aa3f92017-07-06 23:56:50 -04001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Carmelo Casconef7aa3f92017-07-06 23:56:50 -04003 *
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
17package org.onosproject.p4runtime.ctl;
18
19import com.google.common.collect.Maps;
20import io.grpc.ManagedChannel;
21import io.grpc.ManagedChannelBuilder;
22import io.grpc.NameResolverProvider;
23import io.grpc.internal.DnsNameResolverProvider;
24import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
29import org.apache.felix.scr.annotations.Service;
30import org.onosproject.event.AbstractListenerManager;
31import org.onosproject.grpc.api.GrpcChannelId;
32import org.onosproject.grpc.api.GrpcController;
33import org.onosproject.net.DeviceId;
34import org.onosproject.p4runtime.api.P4RuntimeClient;
35import org.onosproject.p4runtime.api.P4RuntimeController;
36import org.onosproject.p4runtime.api.P4RuntimeEvent;
37import org.onosproject.p4runtime.api.P4RuntimeEventListener;
Yi Tseng3e7f1452017-10-20 10:31:53 -070038import org.onosproject.store.service.AtomicCounter;
39import org.onosproject.store.service.StorageService;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040040import org.slf4j.Logger;
41
42import java.io.IOException;
43import java.util.Map;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040044import java.util.concurrent.locks.ReadWriteLock;
45import java.util.concurrent.locks.ReentrantReadWriteLock;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040046
47import static com.google.common.base.Preconditions.checkNotNull;
48import static java.lang.String.format;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040049import static org.slf4j.LoggerFactory.getLogger;
50
51/**
52 * P4Runtime controller implementation.
53 */
54@Component(immediate = true)
55@Service
56public class P4RuntimeControllerImpl
57 extends AbstractListenerManager<P4RuntimeEvent, P4RuntimeEventListener>
58 implements P4RuntimeController {
59
Yi Tseng3e7f1452017-10-20 10:31:53 -070060 private static final String P4R_ELECTION = "p4runtime-election";
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040061 private final Logger log = getLogger(getClass());
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040062 private final NameResolverProvider nameResolverProvider = new DnsNameResolverProvider();
Carmelo Cascone59f57de2017-07-11 19:55:09 -040063 private final Map<DeviceId, P4RuntimeClient> clients = Maps.newHashMap();
64 private final Map<DeviceId, GrpcChannelId> channelIds = Maps.newHashMap();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040065 // TODO: should use a cache to delete unused locks.
Carmelo Cascone59f57de2017-07-11 19:55:09 -040066 private final Map<DeviceId, ReadWriteLock> deviceLocks = Maps.newConcurrentMap();
Yi Tseng3e7f1452017-10-20 10:31:53 -070067 private AtomicCounter electionIdGenerator;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040068
Carmelo Cascone8d99b172017-07-18 17:26:31 -040069 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Carmelo Casconec8e84982017-07-26 15:34:42 -040070 public GrpcController grpcController;
Carmelo Cascone8d99b172017-07-18 17:26:31 -040071
Yi Tseng3e7f1452017-10-20 10:31:53 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 public StorageService storageService;
74
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040075 @Activate
76 public void activate() {
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020077 eventDispatcher.addSink(P4RuntimeEvent.class, listenerRegistry);
Yi Tseng3e7f1452017-10-20 10:31:53 -070078 electionIdGenerator = storageService.getAtomicCounter(P4R_ELECTION);
79
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040080 log.info("Started");
81 }
82
83
84 @Deactivate
85 public void deactivate() {
86 grpcController = null;
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020087 eventDispatcher.removeSink(P4RuntimeEvent.class);
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040088 log.info("Stopped");
89 }
90
91
92 @Override
Carmelo Casconef423bec2017-08-30 01:56:25 +020093 public boolean createClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder) {
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040094 checkNotNull(deviceId);
95 checkNotNull(channelBuilder);
96
Carmelo Cascone59f57de2017-07-11 19:55:09 -040097 deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock());
98 deviceLocks.get(deviceId).writeLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040099
100 log.info("Creating client for {} (with internal device id {})...", deviceId, p4DeviceId);
101
102 try {
103 if (clients.containsKey(deviceId)) {
104 throw new IllegalStateException(format("A client already exists for %s", deviceId));
105 } else {
106 return doCreateClient(deviceId, p4DeviceId, channelBuilder);
107 }
108 } finally {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400109 deviceLocks.get(deviceId).writeLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400110 }
111 }
112
Carmelo Casconef423bec2017-08-30 01:56:25 +0200113 private boolean doCreateClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder) {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400114
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400115 GrpcChannelId channelId = GrpcChannelId.of(deviceId, "p4runtime");
116
117 // Channel defaults.
118 channelBuilder.nameResolverFactory(nameResolverProvider);
119
120 ManagedChannel channel;
121 try {
122 channel = grpcController.connectChannel(channelId, channelBuilder);
123 } catch (IOException e) {
124 log.warn("Unable to connect to gRPC server of {}: {}", deviceId, e.getMessage());
125 return false;
126 }
127
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400128 P4RuntimeClient client = new P4RuntimeClientImpl(deviceId, p4DeviceId, channel, this);
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400129
130 channelIds.put(deviceId, channelId);
131 clients.put(deviceId, client);
132
133 return true;
134 }
135
136 @Override
137 public P4RuntimeClient getClient(DeviceId deviceId) {
138
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400139 deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock());
140 deviceLocks.get(deviceId).readLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400141
142 try {
143 return clients.get(deviceId);
144 } finally {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400145 deviceLocks.get(deviceId).readLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400146 }
147 }
148
149 @Override
150 public void removeClient(DeviceId deviceId) {
151
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400152 deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock());
153 deviceLocks.get(deviceId).writeLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400154
155 try {
156 if (clients.containsKey(deviceId)) {
157 clients.get(deviceId).shutdown();
158 grpcController.disconnectChannel(channelIds.get(deviceId));
159 clients.remove(deviceId);
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400160 channelIds.remove(deviceId);
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400161 }
162 } finally {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400163 deviceLocks.get(deviceId).writeLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400164 }
165 }
166
167 @Override
168 public boolean hasClient(DeviceId deviceId) {
169
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400170 deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock());
171 deviceLocks.get(deviceId).readLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400172
173 try {
174 return clients.containsKey(deviceId);
175 } finally {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400176 deviceLocks.get(deviceId).readLock().unlock();
177 }
178 }
179
180 @Override
181 public boolean isReacheable(DeviceId deviceId) {
182
183 deviceLocks.putIfAbsent(deviceId, new ReentrantReadWriteLock());
184 deviceLocks.get(deviceId).readLock().lock();
185
186 try {
187 if (!clients.containsKey(deviceId)) {
188 log.warn("No client for {}, can't check for reachability", deviceId);
189 return false;
190 }
191
192 return grpcController.isChannelOpen(channelIds.get(deviceId));
193 } finally {
194 deviceLocks.get(deviceId).readLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400195 }
196 }
197
Yi Tseng3e7f1452017-10-20 10:31:53 -0700198 @Override
199 public long getNewMasterElectionId() {
200 return electionIdGenerator.incrementAndGet();
201 }
202
Yi Tseng82512da2017-08-16 19:46:36 -0700203 public void postEvent(P4RuntimeEvent event) {
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400204 post(event);
205 }
206}