blob: 36faa0a6bea966576300dd79e7fb9c480fbcabf1 [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
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +053019import com.google.common.cache.LoadingCache;
20import com.google.common.cache.CacheBuilder;
21import com.google.common.cache.CacheLoader;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040022import com.google.common.collect.Maps;
23import io.grpc.ManagedChannel;
24import io.grpc.ManagedChannelBuilder;
25import io.grpc.NameResolverProvider;
26import io.grpc.internal.DnsNameResolverProvider;
27import org.apache.felix.scr.annotations.Activate;
28import org.apache.felix.scr.annotations.Component;
29import org.apache.felix.scr.annotations.Deactivate;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.apache.felix.scr.annotations.Service;
33import org.onosproject.event.AbstractListenerManager;
34import org.onosproject.grpc.api.GrpcChannelId;
35import org.onosproject.grpc.api.GrpcController;
36import org.onosproject.net.DeviceId;
37import org.onosproject.p4runtime.api.P4RuntimeClient;
38import org.onosproject.p4runtime.api.P4RuntimeController;
39import org.onosproject.p4runtime.api.P4RuntimeEvent;
40import org.onosproject.p4runtime.api.P4RuntimeEventListener;
Yi Tseng3e7f1452017-10-20 10:31:53 -070041import org.onosproject.store.service.AtomicCounter;
42import org.onosproject.store.service.StorageService;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040043import org.slf4j.Logger;
44
45import java.io.IOException;
46import java.util.Map;
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +053047import java.util.concurrent.TimeUnit;
Carmelo Cascone59f57de2017-07-11 19:55:09 -040048import java.util.concurrent.locks.ReadWriteLock;
49import java.util.concurrent.locks.ReentrantReadWriteLock;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040050
51import static com.google.common.base.Preconditions.checkNotNull;
52import static java.lang.String.format;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040053import static org.slf4j.LoggerFactory.getLogger;
54
55/**
56 * P4Runtime controller implementation.
57 */
58@Component(immediate = true)
59@Service
60public class P4RuntimeControllerImpl
61 extends AbstractListenerManager<P4RuntimeEvent, P4RuntimeEventListener>
62 implements P4RuntimeController {
63
Yi Tseng3e7f1452017-10-20 10:31:53 -070064 private static final String P4R_ELECTION = "p4runtime-election";
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +053065 private static final int DEVICE_LOCK_EXPIRE_TIME_IN_MIN = 10;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040066 private final Logger log = getLogger(getClass());
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040067 private final NameResolverProvider nameResolverProvider = new DnsNameResolverProvider();
Carmelo Cascone59f57de2017-07-11 19:55:09 -040068 private final Map<DeviceId, P4RuntimeClient> clients = Maps.newHashMap();
69 private final Map<DeviceId, GrpcChannelId> channelIds = Maps.newHashMap();
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +053070 private final LoadingCache<DeviceId, ReadWriteLock> deviceLocks = CacheBuilder.newBuilder()
71 .expireAfterAccess(DEVICE_LOCK_EXPIRE_TIME_IN_MIN, TimeUnit.MINUTES)
72 .build(new CacheLoader<DeviceId, ReadWriteLock>() {
73 @Override
74 public ReadWriteLock load(DeviceId deviceId) {
75 return new ReentrantReadWriteLock();
76 }
77 });
78
Yi Tseng3e7f1452017-10-20 10:31:53 -070079 private AtomicCounter electionIdGenerator;
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040080
Carmelo Cascone8d99b172017-07-18 17:26:31 -040081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Carmelo Casconec8e84982017-07-26 15:34:42 -040082 public GrpcController grpcController;
Carmelo Cascone8d99b172017-07-18 17:26:31 -040083
Yi Tseng3e7f1452017-10-20 10:31:53 -070084 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 public StorageService storageService;
86
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040087 @Activate
88 public void activate() {
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020089 eventDispatcher.addSink(P4RuntimeEvent.class, listenerRegistry);
Yi Tseng3e7f1452017-10-20 10:31:53 -070090 electionIdGenerator = storageService.getAtomicCounter(P4R_ELECTION);
91
Carmelo Casconef7aa3f92017-07-06 23:56:50 -040092 log.info("Started");
93 }
94
95
96 @Deactivate
97 public void deactivate() {
98 grpcController = null;
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +020099 eventDispatcher.removeSink(P4RuntimeEvent.class);
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400100 log.info("Stopped");
101 }
102
103
104 @Override
Carmelo Casconef423bec2017-08-30 01:56:25 +0200105 public boolean createClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder) {
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400106 checkNotNull(deviceId);
107 checkNotNull(channelBuilder);
108
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530109 deviceLocks.getUnchecked(deviceId).writeLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400110 log.info("Creating client for {} (with internal device id {})...", deviceId, p4DeviceId);
111
112 try {
113 if (clients.containsKey(deviceId)) {
114 throw new IllegalStateException(format("A client already exists for %s", deviceId));
115 } else {
116 return doCreateClient(deviceId, p4DeviceId, channelBuilder);
117 }
118 } finally {
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530119 deviceLocks.getUnchecked(deviceId).writeLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400120 }
121 }
122
Carmelo Casconef423bec2017-08-30 01:56:25 +0200123 private boolean doCreateClient(DeviceId deviceId, long p4DeviceId, ManagedChannelBuilder channelBuilder) {
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400124
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400125 GrpcChannelId channelId = GrpcChannelId.of(deviceId, "p4runtime");
126
127 // Channel defaults.
128 channelBuilder.nameResolverFactory(nameResolverProvider);
129
130 ManagedChannel channel;
131 try {
132 channel = grpcController.connectChannel(channelId, channelBuilder);
133 } catch (IOException e) {
134 log.warn("Unable to connect to gRPC server of {}: {}", deviceId, e.getMessage());
135 return false;
136 }
137
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400138 P4RuntimeClient client = new P4RuntimeClientImpl(deviceId, p4DeviceId, channel, this);
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400139
140 channelIds.put(deviceId, channelId);
141 clients.put(deviceId, client);
142
143 return true;
144 }
145
146 @Override
147 public P4RuntimeClient getClient(DeviceId deviceId) {
148
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530149 deviceLocks.getUnchecked(deviceId).readLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400150
151 try {
152 return clients.get(deviceId);
153 } finally {
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530154 deviceLocks.getUnchecked(deviceId).readLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400155 }
156 }
157
158 @Override
159 public void removeClient(DeviceId deviceId) {
160
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530161 deviceLocks.getUnchecked(deviceId).writeLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400162
163 try {
164 if (clients.containsKey(deviceId)) {
165 clients.get(deviceId).shutdown();
166 grpcController.disconnectChannel(channelIds.get(deviceId));
167 clients.remove(deviceId);
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400168 channelIds.remove(deviceId);
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400169 }
170 } finally {
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530171 deviceLocks.getUnchecked(deviceId).writeLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400172 }
173 }
174
175 @Override
176 public boolean hasClient(DeviceId deviceId) {
177
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530178 deviceLocks.getUnchecked(deviceId).readLock().lock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400179
180 try {
181 return clients.containsKey(deviceId);
182 } finally {
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530183 deviceLocks.getUnchecked(deviceId).readLock().unlock();
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400184 }
185 }
186
187 @Override
188 public boolean isReacheable(DeviceId deviceId) {
189
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530190 deviceLocks.getUnchecked(deviceId).readLock().lock();
Carmelo Cascone59f57de2017-07-11 19:55:09 -0400191
192 try {
193 if (!clients.containsKey(deviceId)) {
194 log.warn("No client for {}, can't check for reachability", deviceId);
195 return false;
196 }
197
198 return grpcController.isChannelOpen(channelIds.get(deviceId));
199 } finally {
Manjunath Vanaraj59ad6572017-12-26 11:10:57 +0530200 deviceLocks.getUnchecked(deviceId).readLock().unlock();
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400201 }
202 }
203
Yi Tseng3e7f1452017-10-20 10:31:53 -0700204 @Override
205 public long getNewMasterElectionId() {
206 return electionIdGenerator.incrementAndGet();
207 }
208
Yi Tseng82512da2017-08-16 19:46:36 -0700209 public void postEvent(P4RuntimeEvent event) {
Carmelo Casconef7aa3f92017-07-06 23:56:50 -0400210 post(event);
211 }
212}