1f686aeb8f252b8f341e7160a8638671ccd1d131
[onosfw.git] /
1 /*
2  * Copyright 2014-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  */
16 package org.onosproject.xosintegration;
17
18 import com.eclipsesource.json.JsonArray;
19 import com.eclipsesource.json.JsonObject;
20 import com.google.common.collect.Maps;
21 import com.sun.jersey.api.client.Client;
22 import com.sun.jersey.api.client.ClientHandlerException;
23 import com.sun.jersey.api.client.ClientResponse;
24 import com.sun.jersey.api.client.WebResource;
25 import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
26 import org.apache.felix.scr.annotations.Activate;
27 import org.apache.felix.scr.annotations.Component;
28 import org.apache.felix.scr.annotations.Deactivate;
29 import org.apache.felix.scr.annotations.Modified;
30 import org.apache.felix.scr.annotations.Property;
31 import org.apache.felix.scr.annotations.Reference;
32 import org.apache.felix.scr.annotations.ReferenceCardinality;
33 import org.apache.felix.scr.annotations.Service;
34 import org.onlab.packet.VlanId;
35 import org.onlab.util.Tools;
36 import org.onosproject.cfg.ComponentConfigService;
37 import org.onosproject.core.ApplicationId;
38 import org.onosproject.core.CoreService;
39 import org.onosproject.net.ConnectPoint;
40 import org.onosproject.net.DeviceId;
41 import org.onosproject.net.PortNumber;
42 import org.onosproject.net.flow.DefaultTrafficSelector;
43 import org.onosproject.net.flow.DefaultTrafficTreatment;
44 import org.onosproject.net.flow.TrafficSelector;
45 import org.onosproject.net.flow.TrafficTreatment;
46 import org.onosproject.net.flowobjective.DefaultForwardingObjective;
47 import org.onosproject.net.flowobjective.FlowObjectiveService;
48 import org.onosproject.net.flowobjective.ForwardingObjective;
49 import org.osgi.service.component.ComponentContext;
50 import org.slf4j.Logger;
51
52 import java.util.Dictionary;
53 import java.util.Map;
54 import java.util.Set;
55 import java.util.stream.Collectors;
56 import java.util.stream.IntStream;
57
58 import static com.google.common.base.Strings.isNullOrEmpty;
59 import static com.google.common.net.MediaType.JSON_UTF_8;
60 import static java.net.HttpURLConnection.HTTP_CREATED;
61 import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
62 import static java.net.HttpURLConnection.HTTP_OK;
63 import static org.slf4j.LoggerFactory.getLogger;
64
65
66 /**
67  * XOS interface application.
68  */
69 @Component(immediate = true)
70 @Service
71 public class OnosXOSIntegrationManager implements VoltTenantService {
72     private static final String XOS_SERVER_ADDRESS_PROPERTY_NAME =
73             "xosServerAddress";
74     private static final String XOS_SERVER_PORT_PROPERTY_NAME =
75             "xosServerPort";
76     private static final String XOS_PROVIDER_SERVICE_PROPERTY_NAME =
77             "xosProviderService";
78
79     private static final String TEST_XOS_SERVER_ADDRESS = "10.254.1.22";
80     private static final int TEST_XOS_SERVER_PORT = 8000;
81     private static final String XOS_TENANT_BASE_URI = "/xoslib/volttenant/";
82     private static final int TEST_XOS_PROVIDER_SERVICE = 1;
83
84     private static final int PRIORITY = 50000;
85     private static final DeviceId FABRIC_DEVICE_ID = DeviceId.deviceId("of:5e3e486e73000187");
86     private static final PortNumber FABRIC_OLT_CONNECT_POINT = PortNumber.portNumber(2);
87     private static final PortNumber FABRIC_VCPE_CONNECT_POINT = PortNumber.portNumber(3);
88     private static final String FABRIC_CONTROLLER_ADDRESS = "10.0.3.136";
89     private static final int FABRIC_SERVER_PORT = 8181;
90     private static final String FABRIC_BASE_URI = "/onos/cordfabric/vlans/add";
91
92     private static final DeviceId OLT_DEVICE_ID = DeviceId.deviceId("of:90e2ba82f97791e9");
93     private static final int OLT_UPLINK_PORT = 129;
94
95     private static final ConnectPoint FABRIC_PORT = new ConnectPoint(
96             DeviceId.deviceId("of:000090e2ba82f974"),
97             PortNumber.portNumber(2));
98
99     private final Logger log = getLogger(getClass());
100     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101     protected CoreService coreService;
102     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103     protected ComponentConfigService cfgService;
104
105     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106     protected FlowObjectiveService flowObjectiveService;
107
108     @Property(name = XOS_SERVER_ADDRESS_PROPERTY_NAME,
109               value = TEST_XOS_SERVER_ADDRESS,
110               label = "XOS Server address")
111     protected String xosServerAddress = TEST_XOS_SERVER_ADDRESS;
112
113     @Property(name = XOS_SERVER_PORT_PROPERTY_NAME,
114               intValue = TEST_XOS_SERVER_PORT,
115               label = "XOS Server port")
116     protected int xosServerPort = TEST_XOS_SERVER_PORT;
117
118     @Property(name = XOS_PROVIDER_SERVICE_PROPERTY_NAME,
119             intValue = TEST_XOS_PROVIDER_SERVICE,
120             label = "XOS Provider Service")
121     protected int xosProviderService = TEST_XOS_PROVIDER_SERVICE;
122
123     private ApplicationId appId;
124     private Map<String, ConnectPoint> nodeToPort;
125     private Map<Long, Short> portToVlan;
126     private Map<ConnectPoint, String> portToSsid;
127
128     @Activate
129     public void activate(ComponentContext context) {
130         log.info("XOS app is starting");
131         cfgService.registerProperties(getClass());
132         appId = coreService.registerApplication("org.onosproject.xosintegration");
133
134         setupMap();
135
136         readComponentConfiguration(context);
137
138         log.info("XOS({}) started", appId.id());
139     }
140
141     @Deactivate
142     public void deactivate() {
143         cfgService.unregisterProperties(getClass(), false);
144         log.info("XOS({}) stopped", appId.id());
145     }
146
147     @Modified
148     public void modified(ComponentContext context) {
149         readComponentConfiguration(context);
150     }
151
152     private void setupMap() {
153         nodeToPort = Maps.newHashMap();
154
155         nodeToPort.put("cordcompute01.onlab.us", new ConnectPoint(FABRIC_DEVICE_ID,
156                                                                   PortNumber.portNumber(4)));
157
158         nodeToPort.put("cordcompute02.onlab.us", new ConnectPoint(FABRIC_DEVICE_ID,
159                                                                   PortNumber.portNumber(3)));
160
161         portToVlan = Maps.newHashMap();
162         portToVlan.putIfAbsent(1L, (short) 201);
163         portToVlan.putIfAbsent(6L, (short) 401);
164
165         portToSsid = Maps.newHashMap();
166         portToSsid.put(new ConnectPoint(OLT_DEVICE_ID, PortNumber.portNumber(1)), "0");
167         portToSsid.put(new ConnectPoint(FABRIC_DEVICE_ID, PortNumber.portNumber(6)), "1");
168     }
169
170     /**
171      * Converts a JSON representation of a tenant into a tenant object.
172      *
173      * @param jsonTenant JSON object representing the tenant
174      * @return volt tenant object
175      */
176     private VoltTenant jsonToTenant(JsonObject jsonTenant) {
177         return VoltTenant.builder()
178                 .withHumanReadableName(jsonTenant.get("humanReadableName").asString())
179                 .withId(jsonTenant.get("id").asInt())
180                 .withProviderService(jsonTenant.get("provider_service").asInt())
181                 .withServiceSpecificId(jsonTenant.get("service_specific_id").asString())
182                 .withVlanId(jsonTenant.get("vlan_id").asString())
183                 .build();
184     }
185
186     /**
187      * Converts a tenant object into a JSON string.
188      *
189      * @param tenant volt tenant object to convert
190      * @return JSON string for the tenant
191      */
192     private String tenantToJson(VoltTenant tenant) {
193         return "{"
194                     + "\"humanReadableName\": \"" + tenant.humanReadableName() + "\","
195                     + "\"id\": \"" + tenant.id() + "\","
196                     + "\"provider_service\": \"" + tenant.providerService() + "\","
197                     + "\"service_specific_id\": \"" + tenant.serviceSpecificId() + "\","
198                     + "\"vlan_id\": \"" + tenant.vlanId() + "\""
199                     + "}";
200     }
201
202     /**
203      * Gets a client web resource builder for the base XOS REST API
204      * with no additional URI.
205      *
206      * @return web resource builder
207      * @deprecated in Cardinal Release
208      */
209     @Deprecated
210     private WebResource.Builder getClientBuilder() {
211         return getClientBuilder("");
212     }
213
214     /**
215      * Gets a client web resource builder for the base XOS REST API
216      * with an optional additional URI.
217      *
218      * @return web resource builder
219      * @deprecated in Cardinal Release
220      */
221     @Deprecated
222     private WebResource.Builder getClientBuilder(String uri) {
223         String baseUrl = "http://" + xosServerAddress + ":"
224                 + Integer.toString(xosServerPort);
225         Client client = Client.create();
226         client.addFilter(new HTTPBasicAuthFilter("padmin@vicci.org", "letmein"));
227         WebResource resource = client.resource(baseUrl
228                 + XOS_TENANT_BASE_URI + uri);
229         return resource.accept(JSON_UTF_8.toString())
230                 .type(JSON_UTF_8.toString());
231     }
232
233     /**
234      * Performs a REST GET operation on the base XOS REST URI.
235      *
236      * @return JSON string fetched by the GET operation
237      * @deprecated in Cardinal Release
238      */
239     @Deprecated
240     private String getRest() {
241         return getRest("");
242     }
243
244     /**
245      * Performs a REST GET operation on the base XOS REST URI with
246      * an optional additional URI.
247      *
248      * @return JSON string fetched by the GET operation
249      * @deprecated in Cardinal Release
250      */
251     @Deprecated
252     private String getRest(String uri) {
253         WebResource.Builder builder = getClientBuilder(uri);
254         ClientResponse response = builder.get(ClientResponse.class);
255
256         if (response.getStatus() != HTTP_OK) {
257             log.info("REST GET request returned error code {}",
258                     response.getStatus());
259         }
260         String jsonString = response.getEntity(String.class);
261         log.info("JSON read:\n{}", jsonString);
262
263         return jsonString;
264     }
265
266     /**
267      * Performs a REST POST operation of a json string on the base
268      * XOS REST URI with an optional additional URI.
269      *
270      * @param json JSON string to post
271      * @deprecated in Cardinal Release
272      */
273     @Deprecated
274     private String postRest(String json) {
275         WebResource.Builder builder = getClientBuilder();
276         ClientResponse response;
277
278         try {
279             response = builder.post(ClientResponse.class, json);
280         } catch (ClientHandlerException e) {
281             log.warn("Unable to contact REST server: {}", e.getMessage());
282             return "{ 'error' : 'oops no one home' }";
283         }
284
285         if (response.getStatus() != HTTP_CREATED) {
286             log.info("REST POST request returned error code {}",
287                     response.getStatus());
288         }
289         return response.getEntity(String.class);
290     }
291
292     /**
293      * Performs a REST DELETE operation on the base
294      * XOS REST URI with an optional additional URI.
295      *
296      * @param uri optional additional URI
297      * @deprecated in Cardinal Release
298      */
299     @Deprecated
300     private void deleteRest(String uri) {
301         WebResource.Builder builder = getClientBuilder(uri);
302         ClientResponse response = builder.delete(ClientResponse.class);
303
304         if (response.getStatus() != HTTP_NO_CONTENT) {
305             log.info("REST DELETE request returned error code {}",
306                     response.getStatus());
307         }
308     }
309
310     /**
311      * Deletes the tenant with the given ID.
312      *
313      * @param tenantId ID of tenant to delete
314      */
315     private void deleteTenant(long tenantId) {
316         deleteRest(Long.toString(tenantId));
317     }
318
319     @Override
320     public Set<VoltTenant> getAllTenants() {
321         String jsonString = getRest();
322
323         JsonArray voltTenantItems = JsonArray.readFrom(jsonString);
324
325         return IntStream.range(0, voltTenantItems.size())
326                 .mapToObj(index -> jsonToTenant(voltTenantItems.get(index).asObject()))
327                 .collect(Collectors.toSet());
328     }
329
330     @Override
331     public void removeTenant(long id) {
332         deleteTenant(id);
333     }
334
335     @Override
336     public VoltTenant addTenant(VoltTenant newTenant) {
337         long providerServiceId = newTenant.providerService();
338         if (providerServiceId == -1) {
339             providerServiceId = xosProviderService;
340         }
341
342         PortNumber onuPort = newTenant.port().port();
343         VlanId subscriberVlan = VlanId.vlanId(portToVlan.get(onuPort.toLong()));
344
345         VoltTenant tenantToCreate = VoltTenant.builder()
346                 .withProviderService(providerServiceId)
347                 .withServiceSpecificId(portToSsid.get(newTenant.port()))
348                 .withVlanId(String.valueOf(subscriberVlan.toShort()))
349                 .withPort(newTenant.port())
350                 .build();
351         String json = tenantToJson(tenantToCreate);
352
353
354         provisionVlanOnPort(OLT_DEVICE_ID, OLT_UPLINK_PORT, onuPort, subscriberVlan.toShort());
355
356         String retJson = postRest(json);
357
358         fetchCPELocation(tenantToCreate, retJson);
359
360         return newTenant;
361     }
362
363     private void fetchCPELocation(VoltTenant newTenant, String jsonString) {
364         JsonObject json = JsonObject.readFrom(jsonString);
365
366         if (json.get("computeNodeName") != null) {
367             ConnectPoint point = nodeToPort.get(json.get("computeNodeName").asString());
368             //ConnectPoint fromPoint = newTenant.port();
369             ConnectPoint oltPort = new ConnectPoint(FABRIC_DEVICE_ID, FABRIC_OLT_CONNECT_POINT);
370
371             provisionFabric(VlanId.vlanId(Short.parseShort(newTenant.vlanId())),
372                             point, oltPort);
373         }
374
375     }
376
377     @Override
378     public VoltTenant getTenant(long id) {
379         String jsonString = getRest(Long.toString(id));
380         JsonObject jsonTenant = JsonObject.readFrom(jsonString);
381         if (jsonTenant.get("id") != null) {
382             return jsonToTenant(jsonTenant);
383         } else {
384             return null;
385         }
386     }
387
388     private void provisionVlanOnPort(DeviceId deviceId, int uplinkPort, PortNumber p, short vlanId) {
389
390         TrafficSelector upstream = DefaultTrafficSelector.builder()
391                 .matchVlanId(VlanId.ANY)
392                 .matchInPort(p)
393                 .build();
394
395         TrafficSelector downstream = DefaultTrafficSelector.builder()
396                 .matchVlanId(VlanId.vlanId(vlanId))
397                 .matchInPort(PortNumber.portNumber(uplinkPort))
398                 .build();
399
400         TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
401                 .setVlanId(VlanId.vlanId(vlanId))
402                 .setOutput(PortNumber.portNumber(uplinkPort))
403                 .build();
404
405         TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
406                 .popVlan()
407                 .setOutput(p)
408                 .build();
409
410
411         ForwardingObjective upFwd = DefaultForwardingObjective.builder()
412                 .withFlag(ForwardingObjective.Flag.VERSATILE)
413                 .withPriority(1000)
414                 .makePermanent()
415                 .withSelector(upstream)
416                 .fromApp(appId)
417                 .withTreatment(upstreamTreatment)
418                 .add();
419
420         ForwardingObjective downFwd = DefaultForwardingObjective.builder()
421                 .withFlag(ForwardingObjective.Flag.VERSATILE)
422                 .withPriority(1000)
423                 .makePermanent()
424                 .withSelector(downstream)
425                 .fromApp(appId)
426                 .withTreatment(downstreamTreatment)
427                 .add();
428
429         flowObjectiveService.forward(deviceId, upFwd);
430         flowObjectiveService.forward(deviceId, downFwd);
431
432     }
433
434     private void provisionDataPlane(VoltTenant tenant) {
435         VlanId vlan = VlanId.vlanId(Short.parseShort(tenant.vlanId()));
436
437         TrafficSelector fromGateway = DefaultTrafficSelector.builder()
438                 .matchInPhyPort(tenant.port().port())
439                 .build();
440
441         TrafficSelector fromFabric = DefaultTrafficSelector.builder()
442                 .matchInPhyPort(FABRIC_PORT.port())
443                 .matchVlanId(vlan)
444                 .build();
445
446         TrafficTreatment toFabric = DefaultTrafficTreatment.builder()
447                 .pushVlan()
448                 .setVlanId(vlan)
449                 .setOutput(FABRIC_PORT.port())
450                 .build();
451
452         TrafficTreatment toGateway = DefaultTrafficTreatment.builder()
453                 .popVlan()
454                 .setOutput(tenant.port().port())
455                 .build();
456
457         ForwardingObjective forwardToFabric = DefaultForwardingObjective.builder()
458                 .withFlag(ForwardingObjective.Flag.VERSATILE)
459                 .withPriority(PRIORITY)
460                 .makePermanent()
461                 .fromApp(appId)
462                 .withSelector(fromGateway)
463                 .withTreatment(toFabric)
464                 .add();
465
466         ForwardingObjective forwardToGateway = DefaultForwardingObjective.builder()
467                 .withFlag(ForwardingObjective.Flag.VERSATILE)
468                 .withPriority(PRIORITY)
469                 .makePermanent()
470                 .fromApp(appId)
471                 .withSelector(fromFabric)
472                 .withTreatment(toGateway)
473                 .add();
474
475         flowObjectiveService.forward(FABRIC_PORT.deviceId(), forwardToFabric);
476         flowObjectiveService.forward(FABRIC_PORT.deviceId(), forwardToGateway);
477     }
478
479     private void provisionFabric(VlanId vlanId, ConnectPoint point, ConnectPoint fromPoint) {
480
481         long vlan = vlanId.toShort();
482
483         JsonObject node = new JsonObject();
484         node.add("vlan", vlan);
485         if (vlan == 201) {
486             node.add("iptv", true);
487         } else {
488             node.add("iptv", false);
489         }
490         JsonArray array = new JsonArray();
491         JsonObject cp1 = new JsonObject();
492         JsonObject cp2 = new JsonObject();
493         cp1.add("device", point.deviceId().toString());
494         cp1.add("port", point.port().toLong());
495         cp2.add("device", fromPoint.deviceId().toString());
496         cp2.add("port", fromPoint.port().toLong());
497         array.add(cp1);
498         array.add(cp2);
499         node.add("ports", array);
500
501
502         String baseUrl = "http://" + FABRIC_CONTROLLER_ADDRESS + ":"
503                 + Integer.toString(FABRIC_SERVER_PORT);
504         Client client = Client.create();
505         WebResource resource = client.resource(baseUrl + FABRIC_BASE_URI);
506         WebResource.Builder builder = resource.accept(JSON_UTF_8.toString())
507                 .type(JSON_UTF_8.toString());
508
509         try {
510             builder.post(ClientResponse.class, node.toString());
511         } catch (ClientHandlerException e) {
512             log.warn("Unable to contact fabric REST server: {}", e.getMessage());
513             return;
514         }
515     }
516
517     /**
518      * Extracts properties from the component configuration context.
519      *
520      * @param context the component context
521      */
522     private void readComponentConfiguration(ComponentContext context) {
523         Dictionary<?, ?> properties = context.getProperties();
524
525         String newXosServerAddress =
526                 Tools.get(properties, XOS_SERVER_ADDRESS_PROPERTY_NAME);
527         if (!isNullOrEmpty(newXosServerAddress)) {
528             xosServerAddress = newXosServerAddress;
529         }
530
531         String newXosServerPortString =
532                 Tools.get(properties, XOS_SERVER_PORT_PROPERTY_NAME);
533         if (!isNullOrEmpty(newXosServerPortString)) {
534             xosServerPort = Integer.parseInt(newXosServerPortString);
535         }
536
537         String newXosProviderServiceString =
538                 Tools.get(properties, XOS_PROVIDER_SERVICE_PROPERTY_NAME);
539         if (!isNullOrEmpty(newXosProviderServiceString)) {
540             xosProviderService = Integer.parseInt(newXosProviderServiceString);
541         }
542     }
543 }
544
545