/* * Copyright 2014-2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.xosintegration; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.google.common.collect.Maps; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.packet.VlanId; import org.onlab.util.Tools; import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flowobjective.DefaultForwardingObjective; import org.onosproject.net.flowobjective.FlowObjectiveService; import org.onosproject.net.flowobjective.ForwardingObjective; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import java.util.Dictionary; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.net.MediaType.JSON_UTF_8; import static java.net.HttpURLConnection.HTTP_CREATED; import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static java.net.HttpURLConnection.HTTP_OK; import static org.slf4j.LoggerFactory.getLogger; /** * XOS interface application. */ @Component(immediate = true) @Service public class OnosXosIntegrationManager implements VoltTenantService { private static final String XOS_SERVER_ADDRESS_PROPERTY_NAME = "xosServerAddress"; private static final String XOS_SERVER_PORT_PROPERTY_NAME = "xosServerPort"; private static final String XOS_PROVIDER_SERVICE_PROPERTY_NAME = "xosProviderService"; private static final String TEST_XOS_SERVER_ADDRESS = "10.254.1.22"; private static final int TEST_XOS_SERVER_PORT = 8000; private static final String XOS_TENANT_BASE_URI = "/xoslib/volttenant/"; private static final int TEST_XOS_PROVIDER_SERVICE = 1; private static final int PRIORITY = 50000; private static final DeviceId FABRIC_DEVICE_ID = DeviceId.deviceId("of:5e3e486e73000187"); private static final PortNumber FABRIC_OLT_CONNECT_POINT = PortNumber.portNumber(2); private static final PortNumber FABRIC_VCPE_CONNECT_POINT = PortNumber.portNumber(3); private static final String FABRIC_CONTROLLER_ADDRESS = "10.0.3.136"; private static final int FABRIC_SERVER_PORT = 8181; private static final String FABRIC_BASE_URI = "/onos/cordfabric/vlans/add"; private static final DeviceId OLT_DEVICE_ID = DeviceId.deviceId("of:90e2ba82f97791e9"); private static final int OLT_UPLINK_PORT = 129; private static final ConnectPoint FABRIC_PORT = new ConnectPoint( DeviceId.deviceId("of:000090e2ba82f974"), PortNumber.portNumber(2)); private final Logger log = getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ComponentConfigService cfgService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected FlowObjectiveService flowObjectiveService; @Property(name = XOS_SERVER_ADDRESS_PROPERTY_NAME, value = TEST_XOS_SERVER_ADDRESS, label = "XOS Server address") protected String xosServerAddress = TEST_XOS_SERVER_ADDRESS; @Property(name = XOS_SERVER_PORT_PROPERTY_NAME, intValue = TEST_XOS_SERVER_PORT, label = "XOS Server port") protected int xosServerPort = TEST_XOS_SERVER_PORT; @Property(name = XOS_PROVIDER_SERVICE_PROPERTY_NAME, intValue = TEST_XOS_PROVIDER_SERVICE, label = "XOS Provider Service") protected int xosProviderService = TEST_XOS_PROVIDER_SERVICE; private ApplicationId appId; private Map nodeToPort; private Map portToVlan; private Map portToSsid; @Activate public void activate(ComponentContext context) { log.info("XOS app is starting"); cfgService.registerProperties(getClass()); appId = coreService.registerApplication("org.onosproject.xosintegration"); setupMap(); readComponentConfiguration(context); log.info("XOS({}) started", appId.id()); } @Deactivate public void deactivate() { cfgService.unregisterProperties(getClass(), false); log.info("XOS({}) stopped", appId.id()); } @Modified public void modified(ComponentContext context) { readComponentConfiguration(context); } private void setupMap() { nodeToPort = Maps.newHashMap(); nodeToPort.put("cordcompute01.onlab.us", new ConnectPoint(FABRIC_DEVICE_ID, PortNumber.portNumber(4))); nodeToPort.put("cordcompute02.onlab.us", new ConnectPoint(FABRIC_DEVICE_ID, PortNumber.portNumber(3))); portToVlan = Maps.newHashMap(); portToVlan.putIfAbsent(1L, (short) 201); portToVlan.putIfAbsent(6L, (short) 401); portToSsid = Maps.newHashMap(); portToSsid.put(new ConnectPoint(OLT_DEVICE_ID, PortNumber.portNumber(1)), "0"); portToSsid.put(new ConnectPoint(FABRIC_DEVICE_ID, PortNumber.portNumber(6)), "1"); } /** * Converts a JSON representation of a tenant into a tenant object. * * @param jsonTenant JSON object representing the tenant * @return volt tenant object */ private VoltTenant jsonToTenant(JsonObject jsonTenant) { return VoltTenant.builder() .withHumanReadableName(jsonTenant.get("humanReadableName").asString()) .withId(jsonTenant.get("id").asInt()) .withProviderService(jsonTenant.get("provider_service").asInt()) .withServiceSpecificId(jsonTenant.get("service_specific_id").asString()) .withVlanId(jsonTenant.get("vlan_id").asString()) .build(); } /** * Converts a tenant object into a JSON string. * * @param tenant volt tenant object to convert * @return JSON string for the tenant */ private String tenantToJson(VoltTenant tenant) { return "{" + "\"humanReadableName\": \"" + tenant.humanReadableName() + "\"," + "\"id\": \"" + tenant.id() + "\"," + "\"provider_service\": \"" + tenant.providerService() + "\"," + "\"service_specific_id\": \"" + tenant.serviceSpecificId() + "\"," + "\"vlan_id\": \"" + tenant.vlanId() + "\"" + "}"; } /** * Gets a client web resource builder for the base XOS REST API * with no additional URI. * * @return web resource builder * @deprecated in Cardinal Release */ @Deprecated private WebResource.Builder getClientBuilder() { return getClientBuilder(""); } /** * Gets a client web resource builder for the base XOS REST API * with an optional additional URI. * * @return web resource builder * @deprecated in Cardinal Release */ @Deprecated private WebResource.Builder getClientBuilder(String uri) { String baseUrl = "http://" + xosServerAddress + ":" + Integer.toString(xosServerPort); Client client = Client.create(); client.addFilter(new HTTPBasicAuthFilter("padmin@vicci.org", "letmein")); WebResource resource = client.resource(baseUrl + XOS_TENANT_BASE_URI + uri); return resource.accept(JSON_UTF_8.toString()) .type(JSON_UTF_8.toString()); } /** * Performs a REST GET operation on the base XOS REST URI. * * @return JSON string fetched by the GET operation * @deprecated in Cardinal Release */ @Deprecated private String getRest() { return getRest(""); } /** * Performs a REST GET operation on the base XOS REST URI with * an optional additional URI. * * @return JSON string fetched by the GET operation * @deprecated in Cardinal Release */ @Deprecated private String getRest(String uri) { WebResource.Builder builder = getClientBuilder(uri); ClientResponse response = builder.get(ClientResponse.class); if (response.getStatus() != HTTP_OK) { log.info("REST GET request returned error code {}", response.getStatus()); } String jsonString = response.getEntity(String.class); log.info("JSON read:\n{}", jsonString); return jsonString; } /** * Performs a REST POST operation of a json string on the base * XOS REST URI with an optional additional URI. * * @param json JSON string to post * @deprecated in Cardinal Release */ @Deprecated private String postRest(String json) { WebResource.Builder builder = getClientBuilder(); ClientResponse response; try { response = builder.post(ClientResponse.class, json); } catch (ClientHandlerException e) { log.warn("Unable to contact REST server: {}", e.getMessage()); return "{ 'error' : 'oops no one home' }"; } if (response.getStatus() != HTTP_CREATED) { log.info("REST POST request returned error code {}", response.getStatus()); } return response.getEntity(String.class); } /** * Performs a REST DELETE operation on the base * XOS REST URI with an optional additional URI. * * @param uri optional additional URI * @deprecated in Cardinal Release */ @Deprecated private void deleteRest(String uri) { WebResource.Builder builder = getClientBuilder(uri); ClientResponse response = builder.delete(ClientResponse.class); if (response.getStatus() != HTTP_NO_CONTENT) { log.info("REST DELETE request returned error code {}", response.getStatus()); } } /** * Deletes the tenant with the given ID. * * @param tenantId ID of tenant to delete */ private void deleteTenant(long tenantId) { deleteRest(Long.toString(tenantId)); } @Override public Set getAllTenants() { String jsonString = getRest(); JsonArray voltTenantItems = JsonArray.readFrom(jsonString); return IntStream.range(0, voltTenantItems.size()) .mapToObj(index -> jsonToTenant(voltTenantItems.get(index).asObject())) .collect(Collectors.toSet()); } @Override public void removeTenant(long id) { deleteTenant(id); } @Override public VoltTenant addTenant(VoltTenant newTenant) { long providerServiceId = newTenant.providerService(); if (providerServiceId == -1) { providerServiceId = xosProviderService; } PortNumber onuPort = newTenant.port().port(); VlanId subscriberVlan = VlanId.vlanId(portToVlan.get(onuPort.toLong())); VoltTenant tenantToCreate = VoltTenant.builder() .withProviderService(providerServiceId) .withServiceSpecificId(portToSsid.get(newTenant.port())) .withVlanId(String.valueOf(subscriberVlan.toShort())) .withPort(newTenant.port()) .build(); String json = tenantToJson(tenantToCreate); provisionVlanOnPort(OLT_DEVICE_ID, OLT_UPLINK_PORT, onuPort, subscriberVlan.toShort()); String retJson = postRest(json); fetchCpeLocation(tenantToCreate, retJson); return newTenant; } private void fetchCpeLocation(VoltTenant newTenant, String jsonString) { JsonObject json = JsonObject.readFrom(jsonString); if (json.get("computeNodeName") != null) { ConnectPoint point = nodeToPort.get(json.get("computeNodeName").asString()); //ConnectPoint fromPoint = newTenant.port(); ConnectPoint oltPort = new ConnectPoint(FABRIC_DEVICE_ID, FABRIC_OLT_CONNECT_POINT); provisionFabric(VlanId.vlanId(Short.parseShort(newTenant.vlanId())), point, oltPort); } } @Override public VoltTenant getTenant(long id) { String jsonString = getRest(Long.toString(id)); JsonObject jsonTenant = JsonObject.readFrom(jsonString); if (jsonTenant.get("id") != null) { return jsonToTenant(jsonTenant); } else { return null; } } private void provisionVlanOnPort(DeviceId deviceId, int uplinkPort, PortNumber p, short vlanId) { TrafficSelector upstream = DefaultTrafficSelector.builder() .matchVlanId(VlanId.ANY) .matchInPort(p) .build(); TrafficSelector downstream = DefaultTrafficSelector.builder() .matchVlanId(VlanId.vlanId(vlanId)) .matchInPort(PortNumber.portNumber(uplinkPort)) .build(); TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder() .setVlanId(VlanId.vlanId(vlanId)) .setOutput(PortNumber.portNumber(uplinkPort)) .build(); TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder() .popVlan() .setOutput(p) .build(); ForwardingObjective upFwd = DefaultForwardingObjective.builder() .withFlag(ForwardingObjective.Flag.VERSATILE) .withPriority(1000) .makePermanent() .withSelector(upstream) .fromApp(appId) .withTreatment(upstreamTreatment) .add(); ForwardingObjective downFwd = DefaultForwardingObjective.builder() .withFlag(ForwardingObjective.Flag.VERSATILE) .withPriority(1000) .makePermanent() .withSelector(downstream) .fromApp(appId) .withTreatment(downstreamTreatment) .add(); flowObjectiveService.forward(deviceId, upFwd); flowObjectiveService.forward(deviceId, downFwd); } private void provisionDataPlane(VoltTenant tenant) { VlanId vlan = VlanId.vlanId(Short.parseShort(tenant.vlanId())); TrafficSelector fromGateway = DefaultTrafficSelector.builder() .matchInPhyPort(tenant.port().port()) .build(); TrafficSelector fromFabric = DefaultTrafficSelector.builder() .matchInPhyPort(FABRIC_PORT.port()) .matchVlanId(vlan) .build(); TrafficTreatment toFabric = DefaultTrafficTreatment.builder() .pushVlan() .setVlanId(vlan) .setOutput(FABRIC_PORT.port()) .build(); TrafficTreatment toGateway = DefaultTrafficTreatment.builder() .popVlan() .setOutput(tenant.port().port()) .build(); ForwardingObjective forwardToFabric = DefaultForwardingObjective.builder() .withFlag(ForwardingObjective.Flag.VERSATILE) .withPriority(PRIORITY) .makePermanent() .fromApp(appId) .withSelector(fromGateway) .withTreatment(toFabric) .add(); ForwardingObjective forwardToGateway = DefaultForwardingObjective.builder() .withFlag(ForwardingObjective.Flag.VERSATILE) .withPriority(PRIORITY) .makePermanent() .fromApp(appId) .withSelector(fromFabric) .withTreatment(toGateway) .add(); flowObjectiveService.forward(FABRIC_PORT.deviceId(), forwardToFabric); flowObjectiveService.forward(FABRIC_PORT.deviceId(), forwardToGateway); } private void provisionFabric(VlanId vlanId, ConnectPoint point, ConnectPoint fromPoint) { long vlan = vlanId.toShort(); JsonObject node = new JsonObject(); node.add("vlan", vlan); if (vlan == 201) { node.add("iptv", true); } else { node.add("iptv", false); } JsonArray array = new JsonArray(); JsonObject cp1 = new JsonObject(); JsonObject cp2 = new JsonObject(); cp1.add("device", point.deviceId().toString()); cp1.add("port", point.port().toLong()); cp2.add("device", fromPoint.deviceId().toString()); cp2.add("port", fromPoint.port().toLong()); array.add(cp1); array.add(cp2); node.add("ports", array); String baseUrl = "http://" + FABRIC_CONTROLLER_ADDRESS + ":" + Integer.toString(FABRIC_SERVER_PORT); Client client = Client.create(); WebResource resource = client.resource(baseUrl + FABRIC_BASE_URI); WebResource.Builder builder = resource.accept(JSON_UTF_8.toString()) .type(JSON_UTF_8.toString()); try { builder.post(ClientResponse.class, node.toString()); } catch (ClientHandlerException e) { log.warn("Unable to contact fabric REST server: {}", e.getMessage()); return; } } /** * Extracts properties from the component configuration context. * * @param context the component context */ private void readComponentConfiguration(ComponentContext context) { Dictionary properties = context.getProperties(); String newXosServerAddress = Tools.get(properties, XOS_SERVER_ADDRESS_PROPERTY_NAME); if (!isNullOrEmpty(newXosServerAddress)) { xosServerAddress = newXosServerAddress; } String newXosServerPortString = Tools.get(properties, XOS_SERVER_PORT_PROPERTY_NAME); if (!isNullOrEmpty(newXosServerPortString)) { xosServerPort = Integer.parseInt(newXosServerPortString); } String newXosProviderServiceString = Tools.get(properties, XOS_PROVIDER_SERVICE_PROPERTY_NAME); if (!isNullOrEmpty(newXosProviderServiceString)) { xosProviderService = Integer.parseInt(newXosProviderServiceString); } } }