blob: 9b99b81991757beee149ed29e68789e3488d35f5 [file] [log] [blame]
sanghoshinf25d2e02015-11-11 23:07:17 +09001/*
2 * Copyright 2015 Open Networking Laboratory
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 */
sangho93447f12016-02-24 00:33:22 +090016package org.onosproject.openstackinterface;
sanghoshinf25d2e02015-11-11 23:07:17 +090017
18import com.fasterxml.jackson.databind.ObjectMapper;
19import com.fasterxml.jackson.databind.node.ArrayNode;
20import com.fasterxml.jackson.databind.node.ObjectNode;
sangho93447f12016-02-24 00:33:22 +090021import com.google.common.collect.ImmutableSet;
sanghoshinf25d2e02015-11-11 23:07:17 +090022import com.google.common.collect.Lists;
23import com.sun.jersey.api.client.Client;
24import com.sun.jersey.api.client.WebResource;
sangho0c2a3da2016-02-16 13:39:07 +090025import org.apache.felix.scr.annotations.Activate;
26import org.apache.felix.scr.annotations.Component;
27import org.apache.felix.scr.annotations.Deactivate;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.apache.felix.scr.annotations.Service;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.net.Port;
sangho93447f12016-02-24 00:33:22 +090034import org.onosproject.net.config.ConfigFactory;
35import org.onosproject.net.config.NetworkConfigEvent;
36import org.onosproject.net.config.NetworkConfigListener;
37import org.onosproject.net.config.NetworkConfigRegistry;
38import org.onosproject.openstackinterface.web.OpenstackNetworkCodec;
39import org.onosproject.openstackinterface.web.OpenstackPortCodec;
40import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
41import org.onosproject.openstackinterface.web.OpenstackSecurityGroupCodec;
42import org.onosproject.openstackinterface.web.OpenstackSubnetCodec;
sanghoshinf25d2e02015-11-11 23:07:17 +090043import org.slf4j.Logger;
44import javax.ws.rs.core.MediaType;
45import java.io.IOException;
46import java.util.Collection;
Hyunsun Moonf7895202016-01-12 12:21:48 -080047import java.util.Collections;
sanghoshinf25d2e02015-11-11 23:07:17 +090048import java.util.List;
sangho93447f12016-02-24 00:33:22 +090049import java.util.Set;
50import java.util.concurrent.ExecutorService;
51import java.util.concurrent.Executors;
sangho0c2a3da2016-02-16 13:39:07 +090052import java.util.stream.Collectors;
sanghoshinf25d2e02015-11-11 23:07:17 +090053
54import static com.google.common.base.Preconditions.checkNotNull;
55import static com.google.common.net.MediaType.JSON_UTF_8;
sangho93447f12016-02-24 00:33:22 +090056import static org.onlab.util.Tools.groupedThreads;
57import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
sanghoshinf25d2e02015-11-11 23:07:17 +090058import static org.slf4j.LoggerFactory.getLogger;
59
60/**
61 * Handles REST Calls to Openstack Neutron.
62 *
63 */
sangho0c2a3da2016-02-16 13:39:07 +090064@Service
65@Component(immediate = true)
sangho93447f12016-02-24 00:33:22 +090066public class OpenstackInterfaceManager implements OpenstackInterfaceService {
sanghoshinf25d2e02015-11-11 23:07:17 +090067
sangho5db8e052016-01-29 16:08:23 +090068 private static final String URI_NETWORKS = "networks";
69 private static final String URI_PORTS = "ports";
70 private static final String URI_SUBNETS = "subnets";
71 private static final String URI_SECURITY_GROUPS = "security-groups";
72 private static final String URI_TOKENS = "tokens";
73
Daniel Park3a06c522016-01-28 20:51:12 +090074 private static final String PATH_ROUTERS = "routers";
sangho5db8e052016-01-29 16:08:23 +090075 private static final String PATH_NETWORKS = "networks";
76 private static final String PATH_PORTS = "ports";
77 private static final String PATH_SUBNETS = "subnets";
78 private static final String PATH_ACCESS = "access";
79 private static final String PATH_TOKEN = "token";
80 private static final String PATH_ID = "id";
81
82 private static final String HEADER_AUTH_TOKEN = "X-Auth-Token";
83
sanghoshinf25d2e02015-11-11 23:07:17 +090084 private final Logger log = getLogger(getClass());
85 private String neutronUrl;
86 private String keystoneUrl;
87 private String tokenId;
88 private String userName;
89 private String pass;
90
sangho0c2a3da2016-02-16 13:39:07 +090091 private static final String PORT_NAME = "portName";
92
93 private ApplicationId appId;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected CoreService coreService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
sangho93447f12016-02-24 00:33:22 +090099 protected NetworkConfigRegistry cfgService;
sangho0c2a3da2016-02-16 13:39:07 +0900100
sangho93447f12016-02-24 00:33:22 +0900101 private InternalConfigListener internalConfigListener = new InternalConfigListener();
102 private ExecutorService networkEventExcutorService =
103 Executors.newSingleThreadExecutor(groupedThreads("onos/openstackinterface", "config-event"));
104
105 private final Set<ConfigFactory> factories = ImmutableSet.of(
106 new ConfigFactory<ApplicationId, OpenstackNetworkingConfig>(APP_SUBJECT_FACTORY,
107 OpenstackNetworkingConfig.class,
108 "openstackinterface") {
109 @Override
110 public OpenstackNetworkingConfig createConfig() {
111 return new OpenstackNetworkingConfig();
112 }
113 }
114 );
115
sangho0c2a3da2016-02-16 13:39:07 +0900116
117 @Activate
118 protected void activate() {
119 appId = coreService
sangho93447f12016-02-24 00:33:22 +0900120 .registerApplication("org.onosproject.openstackinterface");
121
122 factories.forEach(cfgService::registerConfigFactory);
123 cfgService.addListener(internalConfigListener);
sangho0c2a3da2016-02-16 13:39:07 +0900124
125 log.info("started");
126 }
127
128 @Deactivate
129 protected void deactivate() {
sangho93447f12016-02-24 00:33:22 +0900130 cfgService.removeListener(internalConfigListener);
131 factories.forEach(cfgService::unregisterConfigFactory);
sangho0c2a3da2016-02-16 13:39:07 +0900132 log.info("stopped");
sanghoshinf25d2e02015-11-11 23:07:17 +0900133 }
134
135 /**
136 * Returns network information stored in Neutron.
137 *
138 * @return List of OpenstackNetwork
139 */
140 public Collection<OpenstackNetwork> getNetworks() {
141
sangho5db8e052016-01-29 16:08:23 +0900142 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_NETWORKS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900143 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900144 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900145
Hyunsun Moonf7895202016-01-12 12:21:48 -0800146 log.debug("networks response:" + response);
147
sanghoshinf25d2e02015-11-11 23:07:17 +0900148 ObjectMapper mapper = new ObjectMapper();
149 List<OpenstackNetwork> openstackNetworks = Lists.newArrayList();
150 try {
151 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900152 ArrayNode networkList = (ArrayNode) node.path(PATH_NETWORKS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900153 OpenstackNetworkCodec networkCodec = new OpenstackNetworkCodec();
154 networkList.forEach(n -> openstackNetworks.add(networkCodec.decode((ObjectNode) n, null)));
155 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800156 log.warn("getNetworks()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900157 }
158
Hyunsun Moonf7895202016-01-12 12:21:48 -0800159 openstackNetworks.removeAll(Collections.singleton(null));
sanghoshinf25d2e02015-11-11 23:07:17 +0900160 openstackNetworks.forEach(n -> log.debug("network ID: {}", n.id()));
161
162 return openstackNetworks;
163 }
164
165 /**
166 * Returns port information stored in Neutron.
167 *
168 * @return List of OpenstackPort
169 */
170 public Collection<OpenstackPort> getPorts() {
171
sangho5db8e052016-01-29 16:08:23 +0900172 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_PORTS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900173 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900174 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900175
176 ObjectMapper mapper = new ObjectMapper();
177 List<OpenstackPort> openstackPorts = Lists.newArrayList();
178 try {
179 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900180 ArrayNode portList = (ArrayNode) node.path(PATH_PORTS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900181 OpenstackPortCodec portCodec = new OpenstackPortCodec();
182 portList.forEach(p -> openstackPorts.add(portCodec.decode((ObjectNode) p, null)));
183 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800184 log.warn("getPorts()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900185 }
186
187 log.debug("port response:" + response);
188 openstackPorts.forEach(n -> log.debug("port ID: {}", n.id()));
189
190 return openstackPorts;
191 }
192
Daniel Park3a06c522016-01-28 20:51:12 +0900193 public Collection<OpenstackRouter> getRouters() {
194 WebResource.Builder builder = getClientBuilder(neutronUrl + PATH_ROUTERS);
195 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
196 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
197
198 ObjectMapper mapper = new ObjectMapper();
199 List<OpenstackRouter> openstackRouters = Lists.newArrayList();
200
201 try {
202 ObjectNode node = (ObjectNode) mapper.readTree(response);
203 ArrayNode routerList = (ArrayNode) node.path(PATH_ROUTERS);
204 OpenstackRouterCodec openstackRouterCodec = new OpenstackRouterCodec();
205 routerList.forEach(r -> openstackRouters
206 .add(openstackRouterCodec.decode((ObjectNode) r, null)));
207 } catch (IOException e) {
208 log.warn("getRouters()", e);
209 }
210
211 log.debug("router response:" + response);
212 openstackRouters.forEach(r -> log.debug("router ID: {}", r.id()));
213
214 return openstackRouters;
215 }
216
sanghoshinf25d2e02015-11-11 23:07:17 +0900217 /**
218 * Returns Subnet information in Neutron.
219 *
220 * @return List of OpenstackSubnet
221 */
222 public Collection<OpenstackSubnet> getSubnets() {
223
sangho5db8e052016-01-29 16:08:23 +0900224 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_SUBNETS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900225 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
sangho5db8e052016-01-29 16:08:23 +0900226 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
sanghoshinf25d2e02015-11-11 23:07:17 +0900227
228 ObjectMapper mapper = new ObjectMapper();
229 List<OpenstackSubnet> subnets = Lists.newArrayList();
230 try {
231 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900232 ArrayNode subnetList = (ArrayNode) node.path(PATH_SUBNETS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900233 OpenstackSubnetCodec subnetCodec = new OpenstackSubnetCodec();
234 subnetList.forEach(s -> subnets.add(subnetCodec.decode((ObjectNode) s, null)));
235 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800236 log.warn("getSubnets()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900237 }
238
239 log.debug("subnets response:" + response);
240 subnets.forEach(s -> log.debug("subnet ID: {}", s.id()));
241
242 return subnets;
243 }
244
sangho5db8e052016-01-29 16:08:23 +0900245 /**
246 * Extracts OpenstackSecurityGroup information for the ID.
247 *
248 * @param id Security Group ID
249 * @return OpenstackSecurityGroup object or null if fails
250 */
251 public OpenstackSecurityGroup getSecurityGroup(String id) {
252 WebResource.Builder builder = getClientBuilder(neutronUrl + URI_SECURITY_GROUPS + "/" + id);
253 String response = builder.accept(MediaType.APPLICATION_JSON_TYPE).
254 header(HEADER_AUTH_TOKEN, getToken()).get(String.class);
255
256 ObjectMapper mapper = new ObjectMapper();
257 OpenstackSecurityGroup securityGroup = null;
258 try {
259 ObjectNode node = (ObjectNode) mapper.readTree(response);
260 OpenstackSecurityGroupCodec sgCodec = new OpenstackSecurityGroupCodec();
261 securityGroup = sgCodec.decode(node, null);
262 } catch (IOException e) {
263 log.warn("getSecurityGroup()", e);
264 }
265
266 return securityGroup;
267 }
268
sanghoshinf25d2e02015-11-11 23:07:17 +0900269 private WebResource.Builder getClientBuilder(String uri) {
270 Client client = Client.create();
271 WebResource resource = client.resource(uri);
272 return resource.accept(JSON_UTF_8.toString())
273 .type(JSON_UTF_8.toString());
274 }
275
276 private String getToken() {
277 if (isTokenInvalid()) {
278 String request = "{\"auth\": {\"tenantName\": \"admin\", " +
279 "\"passwordCredentials\": {\"username\": \"" +
280 userName + "\",\"password\": \"" + pass + "\"}}}";
sangho5db8e052016-01-29 16:08:23 +0900281 WebResource.Builder builder = getClientBuilder(keystoneUrl + URI_TOKENS);
sanghoshinf25d2e02015-11-11 23:07:17 +0900282 String response = builder.accept(MediaType.APPLICATION_JSON).post(String.class, request);
283
284 ObjectMapper mapper = new ObjectMapper();
285 try {
286 ObjectNode node = (ObjectNode) mapper.readTree(response);
sangho5db8e052016-01-29 16:08:23 +0900287 tokenId = node.path(PATH_ACCESS).path(PATH_TOKEN).path(PATH_ID).asText();
sanghoshinf25d2e02015-11-11 23:07:17 +0900288 } catch (IOException e) {
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800289 log.warn("getToken()", e);
sanghoshinf25d2e02015-11-11 23:07:17 +0900290 }
291 log.debug("token response:" + response);
292 }
293
294 return tokenId;
295 }
296
297 private boolean isTokenInvalid() {
298 //TODO: validation check for the existing token
299 return true;
300 }
301
sangho0c2a3da2016-02-16 13:39:07 +0900302 @Override
303 public Collection<OpenstackPort> ports(String networkId) {
304 return getPorts().stream()
305 .filter(port -> port.networkId().equals(networkId))
306 .collect(Collectors.toList());
307 }
308
309 @Override
310 public Collection<OpenstackPort> ports() {
311 return getPorts();
312 }
313
314 @Override
315 public OpenstackPort port(Port port) {
316 String uuid = port.annotations().value(PORT_NAME).substring(3);
317 return getPorts().stream()
318 .filter(p -> p.id().startsWith(uuid))
319 .findAny().orElse(null);
320 }
321
322 @Override
323 public OpenstackPort port(String portId) {
324 return getPorts().stream()
325 .filter(p -> p.id().equals(portId))
326 .findAny().orElse(null);
327 }
328
329 @Override
330 public OpenstackNetwork network(String networkId) {
Hyunsun Moonb0f09be2016-02-23 04:21:42 -0800331 Collection<OpenstackSubnet> subnets = getSubnets().stream()
332 .filter(s -> s.networkId().equals(networkId))
333 .collect(Collectors.toList());
334
335 OpenstackNetwork openstackNetwork = getNetworks().stream()
sangho0c2a3da2016-02-16 13:39:07 +0900336 .filter(n -> n.id().equals(networkId))
337 .findAny().orElse(null);
Hyunsun Moonb0f09be2016-02-23 04:21:42 -0800338
339 if (openstackNetwork == null) {
340 return null;
341 }
342
343 return OpenstackNetwork.builder()
344 .id(openstackNetwork.id())
345 .name(openstackNetwork.name())
346 .networkType(openstackNetwork.networkType())
347 .segmentId(openstackNetwork.segmentId())
348 .tenantId(openstackNetwork.tenantId())
349 .subnets(subnets)
350 .build();
sangho0c2a3da2016-02-16 13:39:07 +0900351 }
352
353 @Override
354 public Collection<OpenstackNetwork> networks() {
355 return getNetworks();
356 }
357
358 @Override
359 public OpenstackSubnet subnet(String subnetId) {
360 return getSubnets().stream()
361 .filter(subnet -> subnet.id().equals(subnetId))
362 .findAny().orElse(null);
363 }
364
365 @Override
366 public Collection<OpenstackSubnet> subnets() {
367 return getSubnets();
368 }
369
370 @Override
371 public Collection<OpenstackRouter> routers() {
372 return getRouters();
373 }
374
375 @Override
376 public OpenstackRouter router(String routerId) {
377 return getRouters().stream()
378 .filter(router -> router.id().equals(routerId))
379 .findAny().orElse(null);
380 }
381
sangho93447f12016-02-24 00:33:22 +0900382 private class InternalConfigListener implements NetworkConfigListener {
sangho0c2a3da2016-02-16 13:39:07 +0900383
sangho93447f12016-02-24 00:33:22 +0900384 public void configureNetwork() {
385 OpenstackNetworkingConfig cfg =
386 cfgService.getConfig(appId, OpenstackNetworkingConfig.class);
387 if (cfg == null) {
388 log.error("There is no openstack server information in config.");
389 return;
390 }
391
392 neutronUrl = checkNotNull(cfg.neutronServer());
393 keystoneUrl = checkNotNull(cfg.keystoneServer());
394 userName = checkNotNull(cfg.userName());
395 pass = checkNotNull(cfg.password());
396 }
397
398 @Override
399 public void event(NetworkConfigEvent event) {
400 if (((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
401 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) &&
402 event.configClass().equals(OpenstackNetworkingConfig.class)) {
403
404 log.info("Network configuration changed");
405 networkEventExcutorService.execute(this::configureNetwork);
406 }
407 }
sangho0c2a3da2016-02-16 13:39:07 +0900408 }
409}