Hyunsun Moon

Changed to use XOS client to get service and port information

XOS client still gets these information from OpenStack temporarily
until XOS provides these APIs

Change-Id: I1ef9302f719a18a7377221f63b84431c2cdface8
......@@ -6,13 +6,9 @@ COMPILE_DEPS = [
'//lib:org.apache.karaf.shell.console',
'//lib:javax.ws.rs-api',
'//lib:jsch',
'//lib:openstack4j-core',
'//lib:openstack4j-http-connector',
'//lib:openstack4j-httpclient',
'//utils/rest:onlab-rest',
'//cli:onos-cli',
'//core/store/serializers:onos-core-serializers',
'//apps/openstackinterface/api:onos-apps-openstackinterface-api',
'//apps/dhcp/api:onos-apps-dhcp-api',
'//apps/xosclient:onos-apps-xosclient',
'//protocols/ovsdb/api:onos-protocols-ovsdb-api',
......@@ -20,11 +16,7 @@ COMPILE_DEPS = [
]
BUNDLES = [
'//apps/openstackinterface/api:onos-apps-openstackinterface-api',
'//apps/cordvtn:onos-apps-cordvtn',
'//lib:openstack4j-core',
'//lib:openstack4j-http-connector',
'//lib:openstack4j-httpclient',
]
EXCLUDED_BUNDLES = [
......@@ -43,5 +35,5 @@ onos_app (
included_bundles = BUNDLES,
excluded_bundles = EXCLUDED_BUNDLES,
description = 'APIs for interacting with the CORD VTN application.',
required_apps = [ 'org.onosproject.xosclient', 'org.onosproject.dhcp', 'org.onosproject.ovsdb', 'org.onosproject.openstackinterface' ],
required_apps = [ 'org.onosproject.xosclient', 'org.onosproject.dhcp', 'org.onosproject.ovsdb' ],
)
......
......@@ -112,21 +112,6 @@
<artifactId>jsch</artifactId>
<version>0.1.53</version>
</dependency>
<dependency>
<groupId>org.pacesys</groupId>
<artifactId>openstack4j-core</artifactId>
<version>2.11</version>
</dependency>
<dependency>
<groupId>org.pacesys.openstack4j.connectors</groupId>
<artifactId>openstack4j-http-connector</artifactId>
<version>2.11</version>
</dependency>
<dependency>
<groupId>org.pacesys.openstack4j.connectors</groupId>
<artifactId>openstack4j-httpclient</artifactId>
<version>2.11</version>
</dependency>
</dependencies>
<build>
......@@ -146,15 +131,8 @@
${project.groupId}.${project.artifactId}
</Bundle-SymbolicName>
<Import-Package>
!org.apache.http.*,
!com.fasterxml.jackson.dataformat.*,
*,org.glassfish.jersey.servlet
</Import-Package>
<Embed-Dependency>
openstack4j-core,
openstack4j-http-connector,
openstack4j-httpclient
</Embed-Dependency>
<Web-ContextPath>${web.context}</Web-ContextPath>
</instructions>
</configuration>
......
/*
* Copyright 2015-present 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.cordvtn.api;
import com.google.common.base.MoreObjects;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onosproject.net.Host;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Subnet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
public final class CordService {
public enum ServiceType {
PRIVATE,
PUBLIC,
MANAGEMENT
}
private final CordServiceId id;
private final long segmentationId;
private final ServiceType serviceType;
private final IpPrefix serviceIpRange;
private final IpAddress serviceIp;
private final Map<Host, IpAddress> hosts;
private final Set<CordServiceId> tenantServices;
private final Set<CordServiceId> providerServices;
/**
* Default constructor.
*
* @param osNet OpenStack network
* @param osSubnet OpenStack subnet
* @param hosts host and tunnel ip map
* @param tenantServices list of tenant service ids
* @param providerServices list of provider service ids
*/
public CordService(Network osNet, Subnet osSubnet,
Map<Host, IpAddress> hosts, Set<CordServiceId> tenantServices,
Set<CordServiceId> providerServices) {
this.id = CordServiceId.of(osNet.getId());
this.segmentationId = Long.parseLong(osNet.getProviderSegID());
this.serviceType = getServiceType(osNet.getName());
this.serviceIpRange = IpPrefix.valueOf(osSubnet.getCidr());
this.serviceIp = IpAddress.valueOf(osSubnet.getGateway());
this.hosts = hosts;
this.tenantServices = tenantServices;
this.providerServices = providerServices;
}
/**
* Returns service ID.
*
* @return service id
*/
public CordServiceId id() {
return id;
}
/**
* Returns segmentation ID of this service.
*
* @return segmentation id
*/
public long segmentationId() {
return segmentationId;
}
/**
* Returns service type.
*
* @return service type
*/
public ServiceType serviceType() {
return serviceType;
}
/**
* Returns service IP range.
*
* @return CIDR
*/
public IpPrefix serviceIpRange() {
return serviceIpRange;
}
/**
* Returns service IP address.
*
* @return ip address
*/
public IpAddress serviceIp() {
return serviceIp;
}
/**
* Returns hosts associated with this service.
*
* @return list of hosts
*/
public Map<Host, IpAddress> hosts() {
return hosts;
}
/**
* Returns tenant service IDs.
*
* @return list of tenant service id
*/
public Set<CordServiceId> tenantServices() {
return tenantServices;
}
/**
* Returns provider service IDs.
*
* @return list of provider service id
*/
public Set<CordServiceId> providerServices() {
return providerServices;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CordService)) {
return false;
}
final CordService other = (CordService) obj;
return Objects.equals(this.id, other.id);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id)
.add("segmentationId", segmentationId)
.add("serviceType", serviceType)
.add("serviceIpRange", serviceIpRange)
.add("serviceIp", serviceIp)
.add("tenantServices", tenantServices)
.add("providerServices", providerServices)
.toString();
}
/**
* Returns network type from network name.
* It assumes that network name contains network type.
*
* @param netName network name
* @return network type, or PRIVATE if it doesn't match any type
*/
private ServiceType getServiceType(String netName) {
checkNotNull(netName);
String name = netName.toUpperCase();
if (name.contains(ServiceType.PUBLIC.toString())) {
return ServiceType.PUBLIC;
} else if (name.contains(ServiceType.MANAGEMENT.toString())) {
return ServiceType.MANAGEMENT;
} else {
return ServiceType.PRIVATE;
}
}
}
/*
* Copyright 2015-present 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.cordvtn.api;
import org.onlab.util.Identifier;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Representation of service identifier.
*/
public final class CordServiceId extends Identifier<String> {
/**
* Default constructor.
*
* @param id service identifier
*/
private CordServiceId(String id) {
super(id);
}
/**
* Returns the CordServiceId with value.
*
* @param id service id
* @return CordServiceId
*/
public static CordServiceId of(String id) {
checkNotNull(id);
return new CordServiceId(id);
}
}
......@@ -25,6 +25,7 @@ import org.onlab.packet.TpPort;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.config.Config;
import org.onosproject.xosclient.api.OpenStackAccess;
import org.onosproject.xosclient.api.XosAccess;
import org.slf4j.Logger;
......@@ -209,9 +210,9 @@ public class CordVtnConfig extends Config<ApplicationId> {
/**
* Returns OpenStack API access information.
*
* @return openstack config
* @return openstack access
*/
public OpenStackConfig openstackConfig() {
public OpenStackAccess openstackAccess() {
JsonNode jsonNode = object.get(OPENSTACK);
if (jsonNode == null) {
log.error("Failed to get OpenStack configurations");
......@@ -219,7 +220,7 @@ public class CordVtnConfig extends Config<ApplicationId> {
}
try {
return new OpenStackConfig(
return new OpenStackAccess(
jsonNode.path(ENDPOINT).asText(),
jsonNode.path(TENANT).asText(),
jsonNode.path(USER).asText(),
......@@ -229,66 +230,4 @@ public class CordVtnConfig extends Config<ApplicationId> {
return null;
}
}
/**
* Configuration for OpenStack API access.
*/
public static class OpenStackConfig {
private final String endpoint;
private final String tenant;
private final String user;
private final String password;
/**
* Default constructor.
*
* @param endpoint Keystone endpoint
* @param tenant tenant name
* @param user user name
* @param password passwowrd
*/
public OpenStackConfig(String endpoint, String tenant, String user, String password) {
this.endpoint = endpoint;
this.tenant = tenant;
this.user = user;
this.password = password;
}
/**
* Returns OpenStack API endpoint.
*
* @return endpoint
*/
public String endpoint() {
return this.endpoint;
}
/**
* Returns OpenStack tenant name.
*
* @return tenant name
*/
public String tenant() {
return this.tenant;
}
/**
* Returns OpenStack user.
*
* @return user name
*/
public String user() {
return this.user;
}
/**
* Returns OpenStack password for the user.
*
* @return password
*/
public String password() {
return this.password;
}
}
}
......
......@@ -19,6 +19,7 @@ import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import org.onosproject.xosclient.api.VtnServiceId;
import java.util.Map;
......@@ -51,8 +52,7 @@ public interface CordVtnService {
* @param pServiceId id of the service which provide dependency
* @param isBidirectional true to enable bidirectional connectivity between two services
*/
void createServiceDependency(CordServiceId tServiceId,
CordServiceId pServiceId,
void createServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
boolean isBidirectional);
/**
......@@ -61,7 +61,7 @@ public interface CordVtnService {
* @param tServiceId id of the service which has a dependency
* @param pServiceId id of the service which provide dependency
*/
void removeServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId);
void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId);
/**
* Updates virtual service gateways.
......
......@@ -15,6 +15,7 @@
*/
package org.onosproject.cordvtn.impl;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
......@@ -29,8 +30,6 @@ import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.cordvtn.api.CordService;
import org.onosproject.cordvtn.api.CordServiceId;
import org.onosproject.cordvtn.api.CordVtnConfig;
import org.onosproject.cordvtn.api.CordVtnNode;
import org.onosproject.cordvtn.api.CordVtnService;
......@@ -48,7 +47,6 @@ import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.NetworkConfigService;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.FlowRuleService;
......@@ -66,16 +64,15 @@ import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.xosclient.api.OpenStackAccess;
import org.onosproject.xosclient.api.VtnPort;
import org.onosproject.xosclient.api.VtnPortApi;
import org.onosproject.xosclient.api.VtnPortId;
import org.onosproject.xosclient.api.VtnService;
import org.onosproject.xosclient.api.VtnServiceApi;
import org.onosproject.xosclient.api.VtnServiceId;
import org.onosproject.xosclient.api.XosAccess;
import org.onosproject.xosclient.api.XosClientService;
import org.openstack4j.api.OSClient;
import org.openstack4j.api.exceptions.AuthenticationException;
import org.openstack4j.model.identity.Access;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Subnet;
import org.openstack4j.openstack.OSFactory;
import org.slf4j.Logger;
import java.util.List;
......@@ -108,9 +105,6 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
protected NetworkConfigRegistry configRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService configService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostProviderRegistry hostProviderRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -145,14 +139,17 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
}
};
private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
private static final String DEFAULT_TUNNEL = "vxlan";
private static final String SERVICE_ID = "serviceId";
private static final String OPENSTACK_PORT_ID = "openstackPortId";
private static final String PORT_ID = "vtnPortId";
private static final String DATA_PLANE_IP = "dataPlaneIp";
private static final String DATA_PLANE_INTF = "dataPlaneIntf";
private static final String S_TAG = "stag";
private static final String VSG_HOST_ID = "vsgHostId";
private static final String CREATED_TIME = "createdTime";
private static final String CREATE_TIME = "createTime";
private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
......@@ -169,7 +166,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
private CordVtnArpProxy arpProxy;
private volatile XosAccess xosAccess = null;
private volatile Access osAccess = null;
private volatile OpenStackAccess osAccess = null;
private volatile MacAddress privateGatewayMac = MacAddress.NONE;
/**
......@@ -185,6 +182,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
deviceService,
groupService,
hostService,
configRegistry,
DEFAULT_TUNNEL);
......@@ -196,7 +194,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
hostProvider = hostProviderRegistry.register(this);
configRegistry.registerConfigFactory(configFactory);
configService.addListener(configListener);
configRegistry.addListener(configListener);
log.info("Started");
}
......@@ -209,7 +207,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
packetService.removeProcessor(packetProcessor);
configRegistry.unregisterConfigFactory(configFactory);
configService.removeListener(configListener);
configRegistry.removeListener(configListener);
eventExecutor.shutdown();
log.info("Stopped");
......@@ -224,88 +222,85 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
}
@Override
public void createServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId,
public void createServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
boolean isBidirectional) {
CordService tService = getCordService(tServiceId);
CordService pService = getCordService(pServiceId);
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
// TODO remove openstack access when XOS provides all information
VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
VtnService tService = serviceApi.service(tServiceId, osAccess);
VtnService pService = serviceApi.service(pServiceId, osAccess);
if (tService == null || pService == null) {
log.error("Failed to create CordService for {}", tServiceId.id());
log.error("Failed to create dependency between {} and {}",
tServiceId, pServiceId);
return;
}
log.info("Service dependency from {} to {} created.", tService.id().id(), pService.id().id());
log.info("Created dependency between {} and {}", tService.name(), pService.name());
ruleInstaller.populateServiceDependencyRules(tService, pService, isBidirectional, true);
}
@Override
public void removeServiceDependency(CordServiceId tServiceId, CordServiceId pServiceId) {
CordService tService = getCordService(tServiceId);
CordService pService = getCordService(pServiceId);
public void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) {
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
// TODO remove openstack access when XOS provides all information
VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
VtnService tService = serviceApi.service(tServiceId, osAccess);
VtnService pService = serviceApi.service(pServiceId, osAccess);
if (tService == null || pService == null) {
log.error("Failed to create CordService for {}", tServiceId.id());
log.error("Failed to remove dependency between {} and {}",
tServiceId, pServiceId);
return;
}
log.info("Service dependency from {} to {} removed.", tService.id().id(), pService.id().id());
log.info("Removed dependency between {} and {}", tService.name(), pService.name());
ruleInstaller.populateServiceDependencyRules(tService, pService, true, false);
}
@Override
public void addServiceVm(CordVtnNode node, ConnectPoint connectPoint) {
checkNotNull(osAccess, "OpenStack access is not set");
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
OSClient osClient = OSFactory.clientFromAccess(osAccess);
Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
org.openstack4j.model.network.Port osPort = osClient.networking().port().list()
.stream()
.filter(p -> p.getId().contains(getPortName(port).substring(3)))
.findFirst().orElse(null);
if (osPort == null) {
log.warn("Failed to get OpenStack port for {}", getPortName(port));
return;
}
MacAddress mac = MacAddress.valueOf(osPort.getMacAddress());
HostId hostId = HostId.hostId(mac);
String portName = port.annotations().value("portName");
Host existingHost = hostService.getHost(hostId);
if (existingHost != null) {
String serviceId = existingHost.annotations().value(SERVICE_ID);
if (serviceId == null || !serviceId.equals(osPort.getNetworkId())) {
// this host is not injected by cordvtn or a stale host, remove it
hostProvider.hostVanished(existingHost.id());
}
// TODO remove openstack access when XOS provides all information
VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
if (vtnPort == null) {
log.warn("Failed to get port information of {}", portName);
return;
}
// Included CREATED_TIME to annotation intentionally to trigger HOST_UPDATED
// event so that the flow rule population for this host can happen.
// This ensures refreshing data plane by pushing network config always make
// the data plane synced.
Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
.map(ip -> IpAddress.valueOf(ip.getIpAddress()))
.collect(Collectors.toSet());
// Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
// existing instances.
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
.set(SERVICE_ID, osPort.getNetworkId())
.set(OPENSTACK_PORT_ID, osPort.getId())
.set(SERVICE_ID, vtnPort.serviceId().id())
.set(PORT_ID, vtnPort.id().id())
.set(DATA_PLANE_IP, node.dpIp().ip().toString())
.set(DATA_PLANE_INTF, node.dpIntf())
.set(CREATED_TIME, String.valueOf(System.currentTimeMillis()));
.set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
String serviceVlan = getServiceVlan(osPort);
if (serviceVlan != null) {
// TODO address service specific task in a separate package
String serviceVlan = getServiceVlan(vtnPort);
if (!Strings.isNullOrEmpty(serviceVlan)) {
annotations.set(S_TAG, serviceVlan);
}
HostDescription hostDesc = new DefaultHostDescription(
mac,
vtnPort.mac(),
VlanId.NONE,
new HostLocation(connectPoint, System.currentTimeMillis()),
fixedIps,
Sets.newHashSet(vtnPort.ip()),
annotations.build());
HostId hostId = HostId.hostId(vtnPort.mac());
hostProvider.hostDetected(hostId, hostDesc, false);
}
......@@ -317,6 +312,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
}
@Override
// TODO address service specific task in a separate package
public void updateVirtualSubscriberGateways(HostId vSgHostId, String serviceVlan,
Map<IpAddress, MacAddress> vSgs) {
Host vSgHost = hostService.getHost(vSgHostId);
......@@ -351,6 +347,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param vSgMac vSG mac address
* @param serviceVlan service vlan
*/
// TODO address service specific task in a separate package
private void addVirtualSubscriberGateway(Host vSgHost, IpAddress vSgIp, MacAddress vSgMac,
String serviceVlan) {
log.info("vSG with IP({}) MAC({}) added", vSgIp.toString(), vSgMac.toString());
......@@ -359,7 +356,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
.set(S_TAG, serviceVlan)
.set(VSG_HOST_ID, vSgHost.id().toString())
.set(CREATED_TIME, String.valueOf(System.currentTimeMillis()));
.set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
HostDescription hostDesc = new DefaultHostDescription(
vSgMac,
......@@ -377,125 +374,45 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param vSgHost vSG host
* @return map of ip and mac address, or empty map
*/
// TODO address service specific task in a separate package
private Map<IpAddress, MacAddress> getSubscriberGateways(Host vSgHost) {
checkNotNull(osAccess, "OpenStack access is not set");
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
String osPortId = vSgHost.annotations().value(OPENSTACK_PORT_ID);
String serviceVlan = vSgHost.annotations().value(S_TAG);
String vtnPortId = vSgHost.annotations().value(PORT_ID);
String sTag = vSgHost.annotations().value(S_TAG);
OSClient osClient = OSFactory.clientFromAccess(osAccess);
org.openstack4j.model.network.Port osPort = osClient.networking().port().get(osPortId);
if (osPort == null) {
log.warn("Failed to get OpenStack port {} for VM {}", osPortId, vSgHost.id());
if (Strings.isNullOrEmpty(vtnPortId) || Strings.isNullOrEmpty(sTag)) {
log.warn("PORT_ID and S_TAG is not set, ignore {}", vSgHost);
return Maps.newHashMap();
}
if (!serviceVlan.equals(getServiceVlan(osPort))) {
log.error("Host({}) s-tag does not match with OpenStack port s-tag", vSgHost.id());
// TODO remove openstack access when XOS provides all information
VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
VtnPort vtnPort = portApi.vtnPort(VtnPortId.of(vtnPortId), osAccess);
if (vtnPort == null) {
log.warn("Failed to get port information of {}", vSgHost);
return Maps.newHashMap();
}
Map<IpAddress, MacAddress> addressPairs = Maps.newHashMap();
osPort.getAllowedAddressPairs()
.stream().forEach(p -> addressPairs.put(
IpAddress.valueOf(p.getIpAddress()),
MacAddress.valueOf(p.getMacAddress())));
return addressPairs;
}
/**
* Returns CordService by service ID.
*
* @param serviceId service id
* @return cord service, or null if it fails to get network from OpenStack
*/
private CordService getCordService(CordServiceId serviceId) {
checkNotNull(osAccess, "OpenStack access is not set");
OSClient osClient = OSFactory.clientFromAccess(osAccess);
Network osNet = osClient.networking().network().get(serviceId.id());
if (osNet == null) {
log.warn("Couldn't find OpenStack network for service {}", serviceId.id());
return null;
}
return getCordService(osNet);
}
/**
* Returns CordService by OpenStack network.
*
* @param osNet OpenStack network
* @return cord service
*/
private CordService getCordService(Network osNet) {
checkNotNull(osNet);
CordServiceId serviceId = CordServiceId.of(osNet.getId());
// here it assumes all cord service networks has only one subnet
Subnet osSubnet = osNet.getNeutronSubnets().stream()
.findFirst().orElse(null);
if (osSubnet == null) {
log.warn("Couldn't find OpenStack subnet for network {}", serviceId.id());
return null;
}
Map<Host, IpAddress> hosts = getHostsWithOpenstackNetwork(osNet)
.stream()
.collect(Collectors.toMap(host -> host, this::getTunnelIp));
// allows working without XOS for now
Set<CordServiceId> tServices = Sets.newHashSet();
Set<CordServiceId> pServices = Sets.newHashSet();
if (xosAccess != null) {
VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService();
tServices = vtnServiceApi.tenantServices(VtnServiceId.of(serviceId.id()))
.stream()
.map(id -> CordServiceId.of(id.id()))
.collect(Collectors.toSet());
pServices = vtnServiceApi.providerServices(VtnServiceId.of(serviceId.id()))
.stream()
.map(id -> CordServiceId.of(id.id()))
.collect(Collectors.toSet());
}
return new CordService(osNet, osSubnet, hosts, tServices, pServices);
}
/**
* Returns IP address for tunneling for a given host.
*
* @param host host
* @return ip address, or null
*/
private IpAddress getTunnelIp(Host host) {
String ip = host.annotations().value(DATA_PLANE_IP);
return ip == null ? null : IpAddress.valueOf(ip);
if (!sTag.equals(getServiceVlan(vtnPort))) {
log.error("Host({}) s-tag does not match with VTN port s-tag", vSgHost);
return Maps.newHashMap();
}
/**
* Returns port name.
*
* @param port port
* @return port name
*/
private String getPortName(Port port) {
return port.annotations().value("portName");
return vtnPort.addressPairs();
}
/**
* Returns s-tag from a given OpenStack port.
* Returns s-tag from a given VTN port.
*
* @param osPort openstack port
* @param vtnPort vtn port
* @return s-tag string
*/
private String getServiceVlan(org.openstack4j.model.network.Port osPort) {
checkNotNull(osPort);
// TODO address service specific task in a separate package
private String getServiceVlan(VtnPort vtnPort) {
checkNotNull(vtnPort);
String portName = osPort.getName();
String portName = vtnPort.name();
if (portName != null && portName.startsWith(S_TAG)) {
return portName.split("-")[1];
} else {
......@@ -504,27 +421,16 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
}
/**
* Returns service ID of this host.
*
* @param host host
* @return service id, or null if not found
*/
private String getServiceId(Host host) {
return host.annotations().value(SERVICE_ID);
}
/**
* Returns hosts associated with a given OpenStack network.
* Returns instances with a given network service.
*
* @param osNet openstack network
* @param serviceId service id
* @return set of hosts
*/
private Set<Host> getHostsWithOpenstackNetwork(Network osNet) {
checkNotNull(osNet);
String osNetId = osNet.getId();
private Set<Host> getInstances(VtnServiceId serviceId) {
return StreamSupport.stream(hostService.getHosts().spliterator(), false)
.filter(host -> Objects.equals(osNetId, getServiceId(host)))
.filter(host -> Objects.equals(
serviceId.id(),
host.annotations().value(SERVICE_ID)))
.collect(Collectors.toSet());
}
......@@ -534,9 +440,9 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param host host
* @param service cord service
*/
private void registerDhcpLease(Host host, CordService service) {
private void registerDhcpLease(Host host, VtnService service) {
List<Ip4Address> options = Lists.newArrayList();
options.add(Ip4Address.makeMaskPrefix(service.serviceIpRange().prefixLength()));
options.add(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()));
options.add(service.serviceIp().getIp4Address());
options.add(service.serviceIp().getIp4Address());
options.add(DEFAULT_DNS);
......@@ -554,28 +460,32 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param host host
*/
private void serviceVmAdded(Host host) {
checkNotNull(osAccess, "OpenStack access is not set");
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
// TODO address service specific task in a separate package
String serviceVlan = host.annotations().value(S_TAG);
if (serviceVlan != null) {
virtualSubscriberGatewayAdded(host, serviceVlan);
}
String serviceId = host.annotations().value(SERVICE_ID);
if (serviceId == null) {
// ignore this host, it is not a service VM
if (Strings.isNullOrEmpty(serviceId)) {
// ignore this host, it is not a service instance
return;
}
log.info("VM is detected, MAC: {} IP: {}", host.mac(), host.ipAddresses());
log.info("Instance is detected {}", host);
CordService service = getCordService(CordServiceId.of(serviceId));
// TODO remove openstack access when XOS provides all information
VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
VtnService service = serviceApi.service(VtnServiceId.of(serviceId), osAccess);
if (service == null) {
log.warn("Failed to get CordService for {}", serviceId);
log.warn("Failed to get VtnService for {}", serviceId);
return;
}
switch (service.serviceType()) {
switch (service.networkType()) {
case MANAGEMENT:
ruleInstaller.populateManagementNetworkRules(host, service);
break;
......@@ -606,32 +516,36 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param host host
*/
private void serviceVmRemoved(Host host) {
checkNotNull(osAccess, "OpenStack access is not set");
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
// TODO address service specific task in a separate package
if (host.annotations().value(S_TAG) != null) {
virtualSubscriberGatewayRemoved(host);
}
String serviceId = host.annotations().value(SERVICE_ID);
if (serviceId == null) {
// ignore it, it's not a service VM
if (Strings.isNullOrEmpty(serviceId)) {
// ignore this host, it is not a service instance
return;
}
log.info("VM is vanished, MAC: {} IP: {}", host.mac(), host.ipAddresses());
log.info("Instance is vanished {}", host);
CordService service = getCordService(CordServiceId.of(serviceId));
// TODO remove openstack access when XOS provides all information
VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService();
VtnService service = vtnServiceApi.service(VtnServiceId.of(serviceId), osAccess);
if (service == null) {
log.warn("Failed to get CORD service for {}", serviceId);
log.warn("Failed to get VtnService for {}", serviceId);
return;
}
// TODO need to consider the case that the network is removed also
switch (service.serviceType()) {
// TODO need to consider the case that the service is removed also
switch (service.networkType()) {
case MANAGEMENT:
break;
case PRIVATE:
if (service.hosts().isEmpty()) {
if (getInstances(VtnServiceId.of(serviceId)).isEmpty()) {
arpProxy.removeGateway(service.serviceIp());
}
case PUBLIC:
......@@ -656,13 +570,14 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param host new host with stag, it can be vsg VM or vsg
* @param serviceVlan service vlan
*/
// TODO address service specific task in a separate package
private void virtualSubscriberGatewayAdded(Host host, String serviceVlan) {
Map<IpAddress, MacAddress> vSgs;
Host vSgHost;
String vSgHostId = host.annotations().value(VSG_HOST_ID);
if (vSgHostId == null) {
log.debug("vSG VM detected {}", host.id());
log.info("vSG VM detected {}", host.id());
vSgHost = host;
vSgs = getSubscriberGateways(vSgHost);
......@@ -677,7 +592,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
return;
}
log.debug("vSG detected {}", host.id());
log.info("vSG detected {}", host.id());
vSgs = getSubscriberGateways(vSgHost);
}
......@@ -689,6 +604,7 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
*
* @param vSg vsg host to remove
*/
// TODO address service specific task in a separate package
private void virtualSubscriberGatewayRemoved(Host vSg) {
String vSgHostId = vSg.annotations().value(VSG_HOST_ID);
if (vSgHostId == null) {
......@@ -712,7 +628,8 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
* @param newMac mac address to update
*/
private void setPrivateGatewayMac(MacAddress newMac) {
checkNotNull(osAccess, "OpenStack access is not set");
checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
checkNotNull(xosAccess, XOS_ACCESS_ERROR);
if (newMac == null || newMac.equals(privateGatewayMac)) {
// no updates, do nothing
......@@ -722,14 +639,12 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
privateGatewayMac = newMac;
log.debug("Set service gateway MAC address to {}", privateGatewayMac.toString());
OSClient osClient = OSFactory.clientFromAccess(osAccess);
List<Network> vNets = Lists.newArrayList(osClient.networking().network().list().iterator());
vNets.stream().forEach(vNet -> {
CordService service = getCordService(vNet);
VtnServiceApi vtnServiceApi = xosClient.getClient(xosAccess).vtnService();
vtnServiceApi.services().stream().forEach(serviceId -> {
VtnService service = vtnServiceApi.service(serviceId, osAccess);
if (service != null) {
arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
arpProxy.sendGratuitousArpForGateway(service.serviceIp(), service.hosts().keySet());
arpProxy.sendGratuitousArpForGateway(service.serviceIp(), getInstances(serviceId));
}
});
}
......@@ -751,51 +666,6 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
}
/**
* Sets OpenStack access information.
* Access is the entity returned when authenticated and provides a singleton client
* between multiple threads.
*
* @param osConfig openstack config
*/
private void setOpenstackAccess(CordVtnConfig.OpenStackConfig osConfig) {
checkNotNull(osConfig, "OpenStack access is not configured");
log.debug("Get OpenStack access with Endpoint: {} Tenant: {} User: {} Passwd: {}",
osConfig.endpoint(),
osConfig.tenant(),
osConfig.user(),
osConfig.password());
try {
osAccess = OSFactory.builder()
.endpoint(osConfig.endpoint())
.credentials(osConfig.user(), osConfig.password())
.tenantName(osConfig.tenant())
.authenticate()
.getAccess();
} catch (AuthenticationException e) {
log.error("Failed to get OpenStack Access");
}
}
/**
* Sets XOS access information.
*
* @param xosAccess xos access
*/
private void setXosAccess(XosAccess xosAccess) {
if (xosAccess == null) {
log.warn("XOS access is not configured");
return;
}
log.debug("Set XOS access with Endpoint: {} User: {} Passwd: {}",
xosAccess.endpoint(),
xosAccess.username(),
xosAccess.password());
this.xosAccess = xosAccess;
}
/**
* Updates configurations.
*/
private void readConfiguration() {
......@@ -805,8 +675,9 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
return;
}
setXosAccess(config.xosAccess());
setOpenstackAccess(config.openstackConfig());
xosAccess = config.xosAccess();
osAccess = config.openstackAccess();
setPrivateGatewayMac(config.privateGatewayMac());
setPublicGatewayMac(config.publicGateways());
}
......@@ -824,10 +695,10 @@ public class CordVtn extends AbstractProvider implements CordVtnService, HostPro
switch (event.type()) {
case HOST_UPDATED:
case HOST_ADDED:
eventExecutor.submit(() -> serviceVmAdded(host));
eventExecutor.execute(() -> serviceVmAdded(host));
break;
case HOST_REMOVED:
eventExecutor.submit(() -> serviceVmRemoved(host));
eventExecutor.execute(() -> serviceVmRemoved(host));
break;
default:
break;
......
......@@ -237,6 +237,7 @@ public class CordVtnNodeManager {
ruleInstaller = new CordVtnRuleInstaller(appId, flowRuleService,
deviceService,
groupService,
hostService,
configRegistry,
DEFAULT_TUNNEL);
......
......@@ -28,8 +28,6 @@ import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onlab.packet.VlanId;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.cordvtn.api.CordService;
import org.onosproject.cordvtn.api.CordServiceId;
import org.onosproject.cordvtn.api.CordVtnConfig;
import org.onosproject.cordvtn.api.CordVtnNode;
import org.onosproject.core.ApplicationId;
......@@ -68,6 +66,9 @@ import org.onosproject.net.group.GroupBuckets;
import org.onosproject.net.group.GroupDescription;
import org.onosproject.net.group.GroupKey;
import org.onosproject.net.group.GroupService;
import org.onosproject.net.host.HostService;
import org.onosproject.xosclient.api.VtnService;
import org.onosproject.xosclient.api.VtnServiceId;
import org.slf4j.Logger;
import java.util.ArrayList;
......@@ -76,6 +77,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
......@@ -112,11 +114,13 @@ public class CordVtnRuleInstaller {
private static final String DATA_PLANE_INTF = "dataPlaneIntf";
private static final String DATA_PLANE_IP = "dataPlaneIp";
private static final String S_TAG = "stag";
private static final String SERVICE_ID = "serviceId";
private final ApplicationId appId;
private final FlowRuleService flowRuleService;
private final DeviceService deviceService;
private final GroupService groupService;
private final HostService hostService;
private final NetworkConfigRegistry configRegistry;
private final String tunnelType;
......@@ -134,12 +138,14 @@ public class CordVtnRuleInstaller {
FlowRuleService flowRuleService,
DeviceService deviceService,
GroupService groupService,
HostService hostService,
NetworkConfigRegistry configRegistry,
String tunnelType) {
this.appId = appId;
this.flowRuleService = flowRuleService;
this.deviceService = deviceService;
this.groupService = groupService;
this.hostService = hostService;
this.configRegistry = configRegistry;
this.tunnelType = checkNotNull(tunnelType);
}
......@@ -177,7 +183,7 @@ public class CordVtnRuleInstaller {
* @param service cord service
* @param install true to install or false to remove
*/
public void populateBasicConnectionRules(Host host, CordService service, boolean install) {
public void populateBasicConnectionRules(Host host, VtnService service, boolean install) {
checkNotNull(host);
checkNotNull(service);
......@@ -186,8 +192,8 @@ public class CordVtnRuleInstaller {
MacAddress dstMac = host.mac();
IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
long tunnelId = service.segmentationId();
Ip4Prefix serviceIpRange = service.serviceIpRange().getIp4Prefix();
long tunnelId = service.vni();
Ip4Prefix serviceIpRange = service.subnet().getIp4Prefix();
populateLocalInPortRule(deviceId, inPort, hostIp, install);
populateDstIpRule(deviceId, inPort, dstMac, hostIp, tunnelId, getTunnelIp(host), install);
......@@ -196,7 +202,7 @@ public class CordVtnRuleInstaller {
if (install) {
populateDirectAccessRule(serviceIpRange, serviceIpRange, true);
populateServiceIsolationRule(serviceIpRange, true);
} else if (service.hosts().isEmpty()) {
} else if (getInstances(service.id()).isEmpty()) {
// removes network related rules only if there's no hosts left in this network
populateDirectAccessRule(serviceIpRange, serviceIpRange, false);
populateServiceIsolationRule(serviceIpRange, false);
......@@ -211,13 +217,13 @@ public class CordVtnRuleInstaller {
* @param isBidirectional true to enable bidirectional connection between two services
* @param install true to install or false to remove
*/
public void populateServiceDependencyRules(CordService tService, CordService pService,
public void populateServiceDependencyRules(VtnService tService, VtnService pService,
boolean isBidirectional, boolean install) {
checkNotNull(tService);
checkNotNull(pService);
Ip4Prefix srcRange = tService.serviceIpRange().getIp4Prefix();
Ip4Prefix dstRange = pService.serviceIpRange().getIp4Prefix();
Ip4Prefix srcRange = tService.subnet().getIp4Prefix();
Ip4Prefix dstRange = pService.subnet().getIp4Prefix();
Ip4Address serviceIp = pService.serviceIp().getIp4Address();
Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
......@@ -227,7 +233,7 @@ public class CordVtnRuleInstaller {
GroupId groupId = createServiceGroup(deviceId, pService);
outGroups.put(deviceId, groupId);
Set<PortNumber> tServiceVms = tService.hosts().keySet()
Set<PortNumber> tServiceVms = getInstances(tService.id())
.stream()
.filter(host -> host.location().deviceId().equals(deviceId))
.map(host -> host.location().port())
......@@ -248,7 +254,7 @@ public class CordVtnRuleInstaller {
*
* @param service cord service
*/
public void updateProviderServiceGroup(CordService service) {
public void updateProviderServiceGroup(VtnService service) {
checkNotNull(service);
GroupKey groupKey = getGroupKey(service.id());
......@@ -262,7 +268,7 @@ public class CordVtnRuleInstaller {
List<GroupBucket> oldBuckets = group.buckets().buckets();
List<GroupBucket> newBuckets = getServiceGroupBuckets(
deviceId, service.segmentationId(), service.hosts()).buckets();
deviceId, service.vni(), getInstances(service.id())).buckets();
if (oldBuckets.equals(newBuckets)) {
continue;
......@@ -296,7 +302,7 @@ public class CordVtnRuleInstaller {
* @param host removed vm
* @param service tenant service
*/
public void updateTenantServiceVm(Host host, CordService service) {
public void updateTenantServiceVm(Host host, VtnService service) {
checkNotNull(host);
checkNotNull(service);
......@@ -320,7 +326,7 @@ public class CordVtnRuleInstaller {
* @param host host which has management network interface
* @param mService management network service
*/
public void populateManagementNetworkRules(Host host, CordService mService) {
public void populateManagementNetworkRules(Host host, VtnService mService) {
checkNotNull(mService);
DeviceId deviceId = host.location().deviceId();
......@@ -372,7 +378,7 @@ public class CordVtnRuleInstaller {
selector = DefaultTrafficSelector.builder()
.matchInPort(PortNumber.LOCAL)
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(mService.serviceIpRange())
.matchIPDst(mService.subnet())
.build();
treatment = DefaultTrafficTreatment.builder()
......@@ -1237,7 +1243,7 @@ public class CordVtnRuleInstaller {
* @param service cord service
* @return group id, or null if it fails to create
*/
private GroupId createServiceGroup(DeviceId deviceId, CordService service) {
private GroupId createServiceGroup(DeviceId deviceId, VtnService service) {
checkNotNull(service);
GroupKey groupKey = getGroupKey(service.id());
......@@ -1250,7 +1256,7 @@ public class CordVtnRuleInstaller {
}
GroupBuckets buckets = getServiceGroupBuckets(
deviceId, service.segmentationId(), service.hosts());
deviceId, service.vni(), getInstances(service.id()));
GroupDescription groupDescription = new DefaultGroupDescription(
deviceId,
GroupDescription.Type.SELECT,
......@@ -1273,33 +1279,26 @@ public class CordVtnRuleInstaller {
* @return group buckets
*/
private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId,
Map<Host, IpAddress> hosts) {
Set<Host> hosts) {
List<GroupBucket> buckets = Lists.newArrayList();
for (Map.Entry<Host, IpAddress> entry : hosts.entrySet()) {
Host host = entry.getKey();
Ip4Address remoteIp = entry.getValue().getIp4Address();
hosts.stream().forEach(host -> {
Ip4Address tunnelIp = getTunnelIp(host).getIp4Address();
DeviceId hostDevice = host.location().deviceId();
TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
.builder()
.setEthDst(host.mac());
if (deviceId.equals(hostDevice)) {
tBuilder.setOutput(host.location().port());
} else {
ExtensionTreatment tunnelDst = getTunnelDst(deviceId, remoteIp);
if (tunnelDst == null) {
continue;
}
ExtensionTreatment tunnelDst = getTunnelDst(deviceId, tunnelIp);
tBuilder.extension(tunnelDst, deviceId)
.setTunnelId(tunnelId)
.setOutput(getTunnelPort(hostDevice));
}
buckets.add(DefaultGroupBucket.createSelectGroupBucket(tBuilder.build()));
}
});
return new GroupBuckets(buckets);
}
......@@ -1311,7 +1310,7 @@ public class CordVtnRuleInstaller {
* @param deviceId device id
* @return group id
*/
private GroupId getGroupId(CordServiceId serviceId, DeviceId deviceId) {
private GroupId getGroupId(VtnServiceId serviceId, DeviceId deviceId) {
return new DefaultGroupId(Objects.hash(serviceId, deviceId));
}
......@@ -1321,7 +1320,7 @@ public class CordVtnRuleInstaller {
* @param serviceId service id
* @return group key
*/
private GroupKey getGroupKey(CordServiceId serviceId) {
private GroupKey getGroupKey(VtnServiceId serviceId) {
return new DefaultGroupKey(serviceId.id().getBytes());
}
......@@ -1370,5 +1369,19 @@ public class CordVtnRuleInstaller {
return config.cordVtnNodes().stream()
.map(CordVtnNode::intBrId).collect(Collectors.toSet());
}
/**
* Returns instances with a given network service.
*
* @param serviceId service id
* @return set of hosts
*/
private Set<Host> getInstances(VtnServiceId serviceId) {
return StreamSupport.stream(hostService.getHosts().spliterator(), false)
.filter(host -> Objects.equals(
serviceId.id(),
host.annotations().value(SERVICE_ID)))
.collect(Collectors.toSet());
}
}
......
......@@ -16,8 +16,8 @@
package org.onosproject.cordvtn.rest;
import org.onosproject.cordvtn.api.CordVtnService;
import org.onosproject.cordvtn.api.CordServiceId;
import org.onosproject.rest.AbstractWebResource;
import org.onosproject.xosclient.api.VtnServiceId;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
......@@ -48,8 +48,8 @@ public class ServiceDependencyWebResource extends AbstractWebResource {
@Produces(MediaType.APPLICATION_JSON)
public Response createServiceDependency(@PathParam("tenantServiceId") String tServiceId,
@PathParam("providerServiceId") String pServiceId) {
service.createServiceDependency(CordServiceId.of(tServiceId),
CordServiceId.of(pServiceId),
service.createServiceDependency(VtnServiceId.of(tServiceId),
VtnServiceId.of(pServiceId),
false);
return Response.status(Response.Status.OK).build();
}
......@@ -68,8 +68,8 @@ public class ServiceDependencyWebResource extends AbstractWebResource {
public Response createServiceDependency(@PathParam("tenantServiceId") String tServiceId,
@PathParam("providerServiceId") String pServiceId,
@PathParam("direction") String direction) {
service.createServiceDependency(CordServiceId.of(tServiceId),
CordServiceId.of(pServiceId),
service.createServiceDependency(VtnServiceId.of(tServiceId),
VtnServiceId.of(pServiceId),
direction.equals(BIDIRECTION));
return Response.status(Response.Status.OK).build();
}
......@@ -85,7 +85,7 @@ public class ServiceDependencyWebResource extends AbstractWebResource {
@Path("{tenantServiceId}/{providerServiceId}")
public Response removeServiceDependency(@PathParam("tenantServiceId") String tServiceId,
@PathParam("providerServiceId") String pServiceId) {
service.removeServiceDependency(CordServiceId.of(tServiceId), CordServiceId.of(pServiceId));
service.removeServiceDependency(VtnServiceId.of(tServiceId), VtnServiceId.of(pServiceId));
return Response.noContent().build();
}
}
......
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:javax.ws.rs-api',
'//lib:openstack4j-core',
'//lib:openstack4j-http-connector',
'//lib:openstack4j-httpclient',
]
osgi_jar_with_tests (
......
......@@ -49,6 +49,45 @@
<artifactId>onlab-misc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.pacesys</groupId>
<artifactId>openstack4j-core</artifactId>
<version>2.11</version>
</dependency>
<dependency>
<groupId>org.pacesys.openstack4j.connectors</groupId>
<artifactId>openstack4j-http-connector</artifactId>
<version>2.11</version>
</dependency>
<dependency>
<groupId>org.pacesys.openstack4j.connectors</groupId>
<artifactId>openstack4j-httpclient</artifactId>
<version>2.11</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Import-Package>
!org.apache.http.*,
!com.fasterxml.jackson.dataformat.*,
!javax.annotation,*
</Import-Package>
<Embed-Dependency>
openstack4j-core,
openstack4j-http-connector,
openstack4j-httpclient
</Embed-Dependency>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
......
/*
* Copyright 2016-present 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.xosclient.api;
import com.google.common.base.MoreObjects;
import java.util.Objects;
/**
* Object holding OpenStack API access information.
*/
// TODO remove this when XOS provides this information
public class OpenStackAccess {
private final String endpoint;
private final String tenant;
private final String user;
private final String password;
/**
* Default constructor.
*
* @param endpoint Keystone endpoint
* @param tenant tenant name
* @param user user name
* @param password password
*/
public OpenStackAccess(String endpoint, String tenant, String user, String password) {
this.endpoint = endpoint;
this.tenant = tenant;
this.user = user;
this.password = password;
}
/**
* Returns OpenStack API endpoint.
*
* @return endpoint
*/
public String endpoint() {
return this.endpoint;
}
/**
* Returns OpenStack tenant name.
*
* @return tenant name
*/
public String tenant() {
return this.tenant;
}
/**
* Returns OpenStack user.
*
* @return user name
*/
public String user() {
return this.user;
}
/**
* Returns OpenStack password for the user.
*
* @return password
*/
public String password() {
return this.password;
}
@Override
public int hashCode() {
return Objects.hash(endpoint, tenant, user, password);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj instanceof OpenStackAccess)) {
OpenStackAccess that = (OpenStackAccess) obj;
if (Objects.equals(endpoint, that.endpoint) &&
Objects.equals(tenant, that.tenant) &&
Objects.equals(user, that.user) &&
Objects.equals(password, that.password)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(getClass())
.add("endpoint", endpoint)
.add("tenant", tenant)
.add("user", user)
.add("password", password)
.toString();
}
}
......@@ -44,4 +44,22 @@ public interface VtnPortApi {
* @return vtn port; null if it fails to get port information
*/
VtnPort vtnPort(VtnPortId portId);
/**
* Returns port information from OpenStack with port id.
*
* @param portId port id
* @return vtn port; null if it fails to get port information
*/
// TODO remove this when XOS provides port information
VtnPort vtnPort(VtnPortId portId, OpenStackAccess osAccess);
/**
* Returns port information from OpenStack with port name.
*
* @param portName port name
* @return vtn port; null if it fails to get port information
*/
// TODO remove this when XOS provides port information
VtnPort vtnPort(String portName, OpenStackAccess osAccess);
}
......
......@@ -52,4 +52,14 @@ public interface VtnServiceApi {
* @return set of service ids
*/
Set<VtnServiceId> providerServices(VtnServiceId tServiceId);
/**
* Returns VTN service from OpenStack.
*
* @param serviceId service id
* @param osAccess openstack access
* @return vtn service
*/
// TODO remove this when XOS provides service information
VtnService service(VtnServiceId serviceId, OpenStackAccess osAccess);
}
......
......@@ -15,18 +15,33 @@
*/
package org.onosproject.xosclient.impl;
import com.google.common.collect.Maps;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.xosclient.api.OpenStackAccess;
import org.onosproject.xosclient.api.VtnPort;
import org.onosproject.xosclient.api.VtnPortApi;
import org.onosproject.xosclient.api.VtnPortId;
import org.onosproject.xosclient.api.VtnServiceId;
import org.onosproject.xosclient.api.XosAccess;
import org.openstack4j.api.OSClient;
import org.openstack4j.model.network.IP;
import org.openstack4j.model.network.Port;
import org.openstack4j.openstack.OSFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Provides CORD VTN port APIs.
*/
public class DefaultVtnPortApi extends XosApi implements VtnPortApi {
public final class DefaultVtnPortApi extends XosApi implements VtnPortApi {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* Default constructor.
......@@ -55,4 +70,72 @@ public class DefaultVtnPortApi extends XosApi implements VtnPortApi {
// TODO implement this when XOS provides this information
return null;
}
@Override
// TODO remove this when XOS provides this information
public VtnPort vtnPort(String portName, OpenStackAccess osAccess) {
checkNotNull(osAccess);
OSClient osClient = getOpenStackClient(osAccess);
Port osPort = osClient.networking().port().list()
.stream()
.filter(p -> p.getId().contains(portName.substring(3)))
.findFirst().orElse(null);
if (osPort == null) {
log.warn("Failed to get OpenStack port for {}", portName);
return null;
}
return getVtnPort(osPort);
}
@Override
// TODO remove this when XOS provides this information
public VtnPort vtnPort(VtnPortId portId, OpenStackAccess osAccess) {
checkNotNull(osAccess);
OSClient osClient = getOpenStackClient(osAccess);
Port osPort = osClient.networking().port().get(portId.id());
if (osPort == null) {
log.warn("Failed to get OpenStack port {}", portId);
return null;
}
return getVtnPort(osPort);
}
// TODO remove this when XOS provides this information
private VtnPort getVtnPort(Port osPort) {
checkNotNull(osPort);
// assumes all vtn port has single IP address
IP ipAddr = osPort.getFixedIps().stream().findFirst().orElse(null);
if (ipAddr == null) {
log.warn("Failed to get IP address for {}", osPort);
return null;
}
Map<IpAddress, MacAddress> addressPairs = Maps.newHashMap();
osPort.getAllowedAddressPairs().stream().forEach(
pair -> addressPairs.put(IpAddress.valueOf(pair.getIpAddress()),
MacAddress.valueOf(pair.getMacAddress())));
return new VtnPort(VtnPortId.of(osPort.getId()),
osPort.getName(),
VtnServiceId.of(osPort.getNetworkId()),
MacAddress.valueOf(osPort.getMacAddress()),
IpAddress.valueOf(ipAddr.getIpAddress()),
addressPairs);
}
// TODO remove this when XOS provides this information
private OSClient getOpenStackClient(OpenStackAccess osAccess) {
checkNotNull(osAccess);
// creating a client every time must be inefficient, but this method
// will be removed once XOS provides equivalent APIs
return OSFactory.builder()
.endpoint(osAccess.endpoint())
.credentials(osAccess.user(), osAccess.password())
.tenantName(osAccess.tenant())
.authenticate();
}
}
......
......@@ -17,12 +17,22 @@ package org.onosproject.xosclient.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onosproject.xosclient.api.OpenStackAccess;
import org.onosproject.xosclient.api.VtnService.NetworkType;
import org.onosproject.xosclient.api.VtnService.ServiceType;
import org.onosproject.xosclient.api.VtnServiceApi;
import org.onosproject.xosclient.api.XosAccess;
import org.onosproject.xosclient.api.VtnService;
import org.onosproject.xosclient.api.VtnServiceId;
import org.openstack4j.api.OSClient;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Subnet;
import org.openstack4j.openstack.OSFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -30,6 +40,9 @@ import java.io.IOException;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static org.onosproject.xosclient.api.VtnService.NetworkType.*;
import static org.onosproject.xosclient.api.VtnService.ServiceType.*;
/**
* Provides CORD VTN service and service dependency APIs.
......@@ -113,4 +126,76 @@ public final class DefaultVtnServiceApi extends XosApi implements VtnServiceApi
}
return tServices;
}
@Override
// TODO remove this when XOS provides this information
public VtnService service(VtnServiceId serviceId, OpenStackAccess osAccess) {
checkNotNull(osAccess);
OSClient osClient = getOpenStackClient(osAccess);
Network osNet = osClient.networking().network().get(serviceId.id());
if (osNet == null) {
log.warn("Failed to get OpenStack network {}", serviceId);
return null;
}
// assumes all cord service networks has single subnet
Subnet osSubnet = osNet.getNeutronSubnets().stream()
.findFirst().orElse(null);
if (osSubnet == null) {
log.warn("Failed to get OpenStack subnet of network {}", serviceId);
return null;
}
return new VtnService(serviceId,
osNet.getName(),
serviceType(osNet.getName()),
networkType(osNet.getName()),
Long.parseLong(osNet.getProviderSegID()),
IpPrefix.valueOf(osSubnet.getCidr()),
IpAddress.valueOf(osSubnet.getGateway()),
providerServices(serviceId),
tenantServices(serviceId));
}
// TODO remove this when XOS provides this information
private OSClient getOpenStackClient(OpenStackAccess osAccess) {
checkNotNull(osAccess);
// creating a client every time must be inefficient, but this method
// will be removed once XOS provides equivalent APIs
return OSFactory.builder()
.endpoint(osAccess.endpoint())
.credentials(osAccess.user(), osAccess.password())
.tenantName(osAccess.tenant())
.authenticate();
}
// TODO remove this when XOS provides this information
private NetworkType networkType(String netName) {
checkArgument(!Strings.isNullOrEmpty(netName));
String name = netName.toUpperCase();
if (name.contains(PUBLIC.toString())) {
return PUBLIC;
} else if (name.contains(MANAGEMENT.toString())) {
return MANAGEMENT;
} else {
return PRIVATE;
}
}
// TODO remove this when XOS provides this information
private ServiceType serviceType(String netName) {
checkArgument(!Strings.isNullOrEmpty(netName));
String name = netName.toUpperCase();
if (name.contains(VSG.toString())) {
return VSG;
} else if (name.contains(OLT_AGENT.toString())) {
return OLT_AGENT;
} else {
return DUMMY;
}
}
}
......
......@@ -81,13 +81,15 @@ public class XosApi {
public String restGet(String path) {
WebTarget wt = client.target(access.endpoint() + baseUrl).path(path);
Invocation.Builder builder = wt.request(JSON_UTF_8.toString());
try {
Response response = builder.get();
if (response.getStatus() != HTTP_OK) {
log.warn("Failed to get resource {}", access.endpoint() + baseUrl + path);
return EMPTY_JSON_STRING;
}
} catch (javax.ws.rs.ProcessingException e) {
return EMPTY_JSON_STRING;
}
return builder.get(String.class);
}
}
......