ONOS-6742 Refactored OpenstackNode
- Removed gateway node uplink interface configuration steps
- Added checking group states
- Refactored interface, store, manager and handler
Change-Id: I9149edbec6481b15377848c8f24bdc5c6c73adc4
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/ConnectionHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/ConnectionHandler.java
deleted file mode 100644
index b355fc6..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/ConnectionHandler.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.openstacknode;
-
-/**
- * Entity capable of handling a subject connected and disconnected situation.
- */
-public interface ConnectionHandler<T> {
- /**
- * Processes the connected subject.
- *
- * @param subject subject
- */
- void connected(T subject);
-
- /**
- * Processes the disconnected subject.
- *
- * @param subject subject.
- */
- void disconnected(T subject);
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
deleted file mode 100644
index 0ff91a9..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * 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.openstacknode;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.DeviceId;
-import org.onosproject.openstacknode.OpenstackNodeEvent.NodeState;
-import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
-
-import java.util.Comparator;
-import java.util.Objects;
-import java.util.Optional;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE;
-import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.INIT;
-
-/**
- * Representation of a compute/gateway node for OpenstackSwitching/Routing service.
- */
-public final class OpenstackNode {
-
- private final String hostname;
- private final NodeType type;
- private final IpAddress managementIp;
- private final Optional<IpAddress> dataIp;
- private final DeviceId integrationBridge;
- private final Optional<DeviceId> routerBridge;
- private final Optional<String> uplink;
- // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
- private final Optional<IpAddress> routerController;
- private final Optional<String> vlanPort;
- private final NodeState state;
-
- public static final Comparator<OpenstackNode> OPENSTACK_NODE_COMPARATOR =
- (node1, node2) -> node1.hostname().compareTo(node2.hostname());
-
- private OpenstackNode(String hostname,
- NodeType type,
- IpAddress managementIp,
- Optional<IpAddress> dataIp,
- DeviceId integrationBridge,
- Optional<DeviceId> routerBridge,
- Optional<String> uplink,
- Optional<IpAddress> routerController,
- Optional<String> vlanPort,
- NodeState state) {
- this.hostname = hostname;
- this.type = type;
- this.managementIp = managementIp;
- this.dataIp = dataIp;
- this.integrationBridge = integrationBridge;
- this.routerBridge = routerBridge;
- this.uplink = uplink;
- this.routerController = routerController;
- this.vlanPort = vlanPort;
- this.state = state;
- }
-
- /**
- * Returns OpenStack node with new state.
- *
- * @param node openstack node
- * @param state openstack node init state
- * @return openstack node
- */
- public static OpenstackNode getUpdatedNode(OpenstackNode node, NodeState state) {
- return new OpenstackNode(node.hostname,
- node.type,
- node.managementIp,
- node.dataIp,
- node.integrationBridge,
- node.routerBridge,
- node.uplink,
- node.routerController,
- node.vlanPort,
- state);
- }
-
- /**
- * Returns hostname of the node.
- *
- * @return hostname
- */
- public String hostname() {
- return hostname;
- }
-
- /**
- * Returns the type of the node.
- *
- * @return node type
- */
- public NodeType type() {
- return type;
- }
-
- /**
- * Returns the management network IP address of the node.
- *
- * @return management network ip address
- */
- public IpAddress managementIp() {
- return managementIp;
- }
-
- /**
- * Returns the data network IP address of the node.
- *
- * @return data network ip address; or empty value
- */
- public Optional<IpAddress> dataIp() {
- return dataIp;
- }
-
- /**
- * Returns the integration bridge device ID.
- *
- * @return device id
- */
- public DeviceId intBridge() {
- return integrationBridge;
- }
-
- /**
- * Returns the router bridge device ID.
- * It returns valid value only if the node type is GATEWAY.
- *
- * @return device id; or empty device id
- */
- public Optional<DeviceId> routerBridge() {
- return routerBridge;
- }
-
- /**
- * Returns the router bridge controller.
- * It returns valid value only if the node type is GATEWAY.
- *
- * @return device id; or empty value
- */
- // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
- public Optional<IpAddress> routerController() {
- return routerController;
- }
-
- /**
- * Returns the uplink interface name.
- * It returns valid value only if the node type is GATEWAY.
- *
- * @return uplink interface name; or empty value
- */
- public Optional<String> uplink() {
- return uplink;
- }
-
- /**
- * Returns the vlan interface name.
- *
- * @return vlan interface name; or empty value
- */
- public Optional<String> vlanPort() {
- return vlanPort;
- }
-
- /**
- * Returns the init state of the node.
- *
- * @return init state
- */
- public NodeState state() {
- return state;
- }
-
- /**
- * Returns the device ID of the OVSDB session of the node.
- *
- * @return device id
- */
- public DeviceId ovsdbId() {
- return DeviceId.deviceId("ovsdb:" + managementIp.toString());
- }
-
- /**
- * Returns the name of the port connected to the external network.
- * It returns valid value only if the node is gateway node.
- *
- * @return external port name
- */
- public Optional<String> externalPortName() {
- if (type == NodeType.GATEWAY) {
- return Optional.of(PATCH_INTG_BRIDGE);
- } else {
- return Optional.empty();
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj instanceof OpenstackNode) {
- OpenstackNode that = (OpenstackNode) obj;
- if (Objects.equals(hostname, that.hostname) &&
- Objects.equals(type, that.type) &&
- Objects.equals(managementIp, that.managementIp) &&
- Objects.equals(dataIp, that.dataIp) &&
- Objects.equals(integrationBridge, that.integrationBridge) &&
- Objects.equals(routerBridge, that.routerBridge) &&
- Objects.equals(uplink, that.uplink) &&
- Objects.equals(routerController, that.routerController) &&
- Objects.equals(vlanPort, that.vlanPort)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(hostname,
- type,
- managementIp,
- dataIp,
- integrationBridge,
- routerBridge,
- uplink,
- routerController,
- vlanPort);
- }
-
- @Override
- public String toString() {
- return MoreObjects.toStringHelper(getClass())
- .add("hostname", hostname)
- .add("type", type)
- .add("managementIp", managementIp)
- .add("dataIp", dataIp)
- .add("integrationBridge", integrationBridge)
- .add("routerBridge", routerBridge)
- .add("uplink", uplink)
- .add("routerController", routerController)
- .add("vlanport", vlanPort)
- .add("state", state)
- .toString();
- }
-
- /**
- * Returns a new builder instance.
- *
- * @return openstack node builder
- */
- public static Builder builder() {
- return new Builder();
- }
-
- /**
- * Builder of OpenStack node entities.
- */
- public static final class Builder {
- private String hostname;
- private NodeType type;
- private IpAddress managementIp;
- private Optional<IpAddress> dataIp = Optional.empty();
- private DeviceId integrationBridge;
- private Optional<DeviceId> routerBridge = Optional.empty();
- private Optional<String> uplink = Optional.empty();
- private Optional<String> vlanPort = Optional.empty();
- // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
- private Optional<IpAddress> routerController = Optional.empty();
- private NodeState state = INIT;
-
- private Builder() {
- }
-
- public OpenstackNode build() {
- checkArgument(!Strings.isNullOrEmpty(hostname));
- checkNotNull(type);
- checkNotNull(managementIp);
- checkNotNull(dataIp);
- checkNotNull(integrationBridge);
- checkNotNull(routerBridge);
- checkNotNull(uplink);
- checkNotNull(routerController);
- checkNotNull(vlanPort);
-
- if (type == NodeType.GATEWAY) {
- checkArgument(routerBridge.isPresent());
- checkArgument(uplink.isPresent());
- checkArgument(routerController.isPresent());
- }
-
- return new OpenstackNode(hostname,
- type,
- managementIp,
- dataIp,
- integrationBridge,
- routerBridge,
- uplink,
- routerController,
- vlanPort,
- state);
- }
-
- /**
- * Returns node builder with the hostname.
- *
- * @param hostname hostname
- * @return openstack node builder
- */
- public Builder hostname(String hostname) {
- this.hostname = hostname;
- return this;
- }
-
- /**
- * Returns node builder with the node type.
- *
- * @param type openstack node type
- * @return openstack node builder
- */
- public Builder type(NodeType type) {
- this.type = type;
- return this;
- }
-
- /**
- * Returns node builder with the management network IP address.
- *
- * @param managementIp management ip address
- * @return openstack node builder
- */
- public Builder managementIp(IpAddress managementIp) {
- this.managementIp = managementIp;
- return this;
- }
-
- /**
- * Returns node builder with the data network IP address.
- *
- * @param dataIp data network ip address
- * @return openstack node builder
- */
- public Builder dataIp(IpAddress dataIp) {
- this.dataIp = Optional.ofNullable(dataIp);
- return this;
- }
-
- /**
- * Returns node builder with the integration bridge ID.
- *
- * @param integrationBridge integration bridge device id
- * @return openstack node builder
- */
- public Builder integrationBridge(DeviceId integrationBridge) {
- this.integrationBridge = integrationBridge;
- return this;
- }
-
- /**
- * Returns node builder with the router bridge ID.
- *
- * @param routerBridge router bridge device ID
- * @return openstack node builder
- */
- public Builder routerBridge(DeviceId routerBridge) {
- this.routerBridge = Optional.ofNullable(routerBridge);
- return this;
- }
-
- /**
- * Returns node builder with the uplink interface name.
- *
- * @param uplink uplink interface name
- * @return openstack node builder
- */
- public Builder uplink(String uplink) {
- this.uplink = Optional.ofNullable(uplink);
- return this;
- }
-
- /**
- * Returns node builder with the router controller.
- *
- * @param routerController router contoller
- * @return openstack node builder
- */
- // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
- public Builder routerController(IpAddress routerController) {
- this.routerController = Optional.ofNullable(routerController);
- return this;
- }
-
- /**
- * Returns node builder with the vlan interface name.
- *
- * @param vlanPort vlan interface name
- * @return openstack node builder
- */
- public Builder vlanPort(String vlanPort) {
- this.vlanPort = Optional.ofNullable(vlanPort);
- return this;
- }
-
- /**
- * Returns node builder with the init state.
- *
- * @param state node init state
- * @return openstack node builder
- */
- public Builder state(NodeState state) {
- this.state = state;
- return this;
- }
- }
-}
-
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeEvent.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeEvent.java
deleted file mode 100644
index 7e0fb78..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeEvent.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.openstacknode;
-
-import org.joda.time.LocalDateTime;
-import org.onosproject.event.AbstractEvent;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Describes OpenStack node init state event.
- */
-public class OpenstackNodeEvent extends AbstractEvent<OpenstackNodeEvent.NodeState, OpenstackNode> {
-
- public enum NodeState {
- /**
- * Indicates the node is newly added.
- */
- INIT {
- @Override
- public void process(OpenstackNodeService nodeService, OpenstackNode node) {
- nodeService.processInitState(node);
- }
- },
- /**
- * Indicates bridge devices are added according to the node state.
- */
- DEVICE_CREATED {
- @Override
- public void process(OpenstackNodeService nodeService, OpenstackNode node) {
- nodeService.processDeviceCreatedState(node);
- }
- },
- /**
- * Indicates all node initialization is done.
- */
- COMPLETE {
- @Override
- public void process(OpenstackNodeService nodeService, OpenstackNode node) {
- nodeService.processCompleteState(node);
- }
- },
- /**
- * Indicates node initialization is not done but unable to proceed to
- * the next step for some reason.
- */
- INCOMPLETE {
- @Override
- public void process(OpenstackNodeService nodeService, OpenstackNode node) {
- nodeService.processIncompleteState(node);
- }
- };
-
- public abstract void process(OpenstackNodeService nodeService, OpenstackNode node);
- }
-
- public OpenstackNodeEvent(NodeState state, OpenstackNode node) {
- super(state, node);
- }
-
- @Override
- public String toString() {
- return toStringHelper(this)
- .add("time", new LocalDateTime(time()))
- .add("state", type())
- .add("node", subject())
- .toString();
- }
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
deleted file mode 100644
index 3eb8095..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
+++ /dev/null
@@ -1,872 +0,0 @@
-/*
- * 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.openstacknode;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-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.IpAddress;
-import org.onlab.packet.TpPort;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.ControllerNode;
-import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.core.GroupId;
-import org.onosproject.event.ListenerRegistry;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.BridgeConfig;
-import org.onosproject.net.behaviour.BridgeDescription;
-import org.onosproject.net.behaviour.BridgeName;
-import org.onosproject.net.behaviour.ControllerInfo;
-import org.onosproject.net.behaviour.DefaultBridgeDescription;
-import org.onosproject.net.behaviour.DefaultPatchDescription;
-import org.onosproject.net.behaviour.DefaultTunnelDescription;
-import org.onosproject.net.behaviour.InterfaceConfig;
-import org.onosproject.net.behaviour.PatchDescription;
-import org.onosproject.net.behaviour.TunnelDescription;
-import org.onosproject.net.behaviour.TunnelEndPoints;
-import org.onosproject.net.behaviour.TunnelKeys;
-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.basics.SubjectFactories;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupService;
-import org.onosproject.openstacknode.OpenstackNodeEvent.NodeState;
-import org.onosproject.ovsdb.controller.OvsdbClientService;
-import org.onosproject.ovsdb.controller.OvsdbController;
-import org.onosproject.ovsdb.controller.OvsdbNodeId;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.MapEventListener;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.Versioned;
-import org.osgi.service.component.ComponentContext;
-import org.slf4j.Logger;
-
-import java.util.Dictionary;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.net.Device.Type.SWITCH;
-import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
-import static org.onosproject.openstacknode.Constants.*;
-import static org.onosproject.openstacknode.OpenstackNode.getUpdatedNode;
-import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Initializes devices in compute/gateway nodes according to there type.
- */
-@Component(immediate = true)
-@Service
-public final class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
- implements OpenstackNodeService {
- private final Logger log = getLogger(getClass());
-
- private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
- .register(KryoNamespaces.API)
- .register(OpenstackNode.class)
- .register(NodeType.class)
- .register(NodeState.class);
-
- private static final String OVSDB_PORT = "ovsdbPort";
- private static final int DPID_BEGIN = 3;
-
- private static final String APP_ID = "org.onosproject.openstacknode";
-
- private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected OvsdbController ovsdbController;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClusterService clusterService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected StorageService storageService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ComponentConfigService componentConfigService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigRegistry configRegistry;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected LeadershipService leadershipService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DriverService driverService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected GroupService groupService;
-
- @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
- label = "OVSDB server listen port")
- private int ovsdbPort = DEFAULT_OVSDB_PORT;
-
- private final ExecutorService eventExecutor =
- newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler", log));
-
- private final ConfigFactory configFactory =
- new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
- SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
- @Override
- public OpenstackNodeConfig createConfig() {
- return new OpenstackNodeConfig();
- }
- };
-
- private final NetworkConfigListener configListener = new InternalConfigListener();
- private final DeviceListener deviceListener = new InternalDeviceListener();
- private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
-
- private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
- private final BridgeHandler bridgeHandler = new BridgeHandler();
-
- private ConsistentMap<String, OpenstackNode> nodeStore;
-
- private SelectGroupHandler selectGroupHandler;
- private ApplicationId appId;
- private NodeId localNodeId;
-
- @Activate
- protected void activate() {
- appId = coreService.getAppId(APP_ID);
-
- localNodeId = clusterService.getLocalNode().id();
- leadershipService.runForLeadership(appId.name());
-
- nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
- .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
- .withName("openstack-nodestore")
- .withApplicationId(appId)
- .build();
-
- nodeStore.addListener(nodeStoreListener);
- deviceService.addListener(deviceListener);
-
- configRegistry.registerConfigFactory(configFactory);
- configRegistry.addListener(configListener);
- componentConfigService.registerProperties(getClass());
-
- selectGroupHandler = new SelectGroupHandler(groupService, deviceService, driverService, appId);
-
- readConfiguration();
- log.info("Started");
- }
-
- @Deactivate
- protected void deactivate() {
- configRegistry.removeListener(configListener);
- deviceService.removeListener(deviceListener);
- nodeStore.removeListener(nodeStoreListener);
-
- componentConfigService.unregisterProperties(getClass(), false);
- configRegistry.unregisterConfigFactory(configFactory);
-
- leadershipService.withdraw(appId.name());
- eventExecutor.shutdown();
-
- log.info("Stopped");
- }
-
- @Modified
- protected void modified(ComponentContext context) {
- Dictionary<?, ?> properties = context.getProperties();
- int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
- if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
- ovsdbPort = updatedOvsdbPort;
- }
-
- log.info("Modified");
- }
-
- @Override
- public void addOrUpdateNode(OpenstackNode node) {
- nodeStore.computeIf(node.hostname(),
- v -> v == null || (!v.equals(node) || v.state() != COMPLETE),
- (k, v) -> getUpdatedNode(node, nodeState(node))
- );
- }
-
- @Override
- public void deleteNode(OpenstackNode node) {
- nodeStore.remove(node.hostname());
- process(new OpenstackNodeEvent(INCOMPLETE, node));
- }
-
- @Override
- public void processInitState(OpenstackNode node) {
- // make sure there is OVSDB connection
- if (!isOvsdbConnected(node)) {
- connectOvsdb(node);
- return;
- }
- process(new OpenstackNodeEvent(INIT, node));
-
- createBridge(node, INTEGRATION_BRIDGE, node.intBridge());
- if (node.type().equals(NodeType.GATEWAY)) {
- createBridge(node, ROUTER_BRIDGE, node.routerBridge().get());
- // TODO remove this when OVSDB provides port event
- setNodeState(node, nodeState(node));
- }
- }
-
- @Override
- public void processDeviceCreatedState(OpenstackNode node) {
- // make sure there is OVSDB connection
- if (!isOvsdbConnected(node)) {
- connectOvsdb(node);
- return;
- }
-
- process(new OpenstackNodeEvent(DEVICE_CREATED, node));
-
- if (node.dataIp().isPresent()) {
- createTunnelInterface(node);
- }
-
- if (node.vlanPort().isPresent()) {
- addVlanPort(node);
- }
-
- if (node.type().equals(NodeType.GATEWAY)) {
- createPatchInterface(node);
- addUplink(node);
- // TODO remove this when OVSDB provides port event
- setNodeState(node, nodeState(node));
- }
- }
-
- @Override
- public void processCompleteState(OpenstackNode node) {
- process(new OpenstackNodeEvent(COMPLETE, node));
- switch (node.type()) {
- case COMPUTE:
- selectGroupHandler.createGatewayGroup(node, gatewayNodes());
- break;
- case GATEWAY:
- updateGatewayGroup(node, true);
- break;
- default:
- break;
- }
- log.info("Finished init {}", node.hostname());
- }
-
- @Override
- public void processIncompleteState(OpenstackNode node) {
- process(new OpenstackNodeEvent(INCOMPLETE, node));
- if (node.type().equals(NodeType.GATEWAY)) {
- updateGatewayGroup(node, false);
- }
- }
-
- @Override
- public List<OpenstackNode> nodes() {
- return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
- }
-
- @Override
- public Set<OpenstackNode> completeNodes() {
- return nodeStore.values().stream().map(Versioned::value)
- .filter(node -> node.state().equals(COMPLETE))
- .collect(Collectors.toSet());
- }
-
- @Override
- public Optional<IpAddress> dataIp(DeviceId deviceId) {
- OpenstackNode node = nodeByDeviceId(deviceId);
- if (node == null) {
- log.warn("Failed to get node for {}", deviceId);
- return Optional.empty();
- }
- return node.dataIp();
- }
-
- @Override
- public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
- return deviceService.getPorts(deviceId).stream()
- .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
- p.isEnabled())
- .map(Port::number).findFirst();
- }
-
- @Override
- public Optional<PortNumber> vlanPort(DeviceId intBridgeId) {
- Optional<String> vlanPortName = nodeByDeviceId(intBridgeId).vlanPort();
-
- return deviceService.getPorts(intBridgeId).stream()
- .filter(p -> p.annotations().value(PORT_NAME).equals(vlanPortName.get()) &&
- p.isEnabled())
- .map(Port::number).findFirst();
-
- }
-
- @Override
- public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
- OpenstackNode node = nodeByDeviceId(intBridgeId);
- if (node == null || node.type().equals(NodeType.COMPUTE)) {
- log.warn("Failed to find router bridge connected to {}", intBridgeId);
- return Optional.empty();
- }
- return node.routerBridge();
- }
-
- @Override
- public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
- return deviceService.getPorts(intBridgeId).stream()
- .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
- p.isEnabled())
- .map(Port::number).findFirst();
- }
-
- @Override
- public OpenstackNode gatewayNode(DeviceId deviceId) {
- OpenstackNode gatewayNode = nodeByDeviceId(deviceId);
- if (gatewayNode == null || gatewayNode.type() != NodeType.GATEWAY) {
- log.warn("Gateway with device ID {} does not exist");
- return null;
- }
- return gatewayNode;
- }
-
- @Override
- public synchronized GroupId gatewayGroupId(DeviceId srcDeviceId, NetworkMode networkMode) {
- GroupKey groupKey = selectGroupHandler.groupKey(srcDeviceId, networkMode);
- Group group = groupService.getGroup(srcDeviceId, groupKey);
-
- if (group == null) {
- log.info("Created gateway group for {}", srcDeviceId);
- selectGroupHandler.createGatewayGroup(nodeByDeviceId(srcDeviceId), gatewayNodes());
-
- return groupService.getGroup(srcDeviceId, selectGroupHandler.groupKey(srcDeviceId, networkMode)).id();
- } else {
- return group.id();
- }
- }
-
- @Override
- public List<OpenstackNode> gatewayNodes() {
- return nodeStore.values()
- .stream()
- .map(Versioned::value)
- .filter(node -> node.type().equals(NodeType.GATEWAY))
- .filter(node -> node.state().equals(COMPLETE))
- .collect(Collectors.toList());
- }
-
- @Override
- public List<DeviceId> gatewayDeviceIds() {
- return gatewayNodes().stream().map(OpenstackNode::intBridge)
- .collect(Collectors.toList());
- }
-
- private void updateGatewayGroup(OpenstackNode gatewayNode, boolean isInsert) {
- nodeStore.values()
- .stream()
- .map(Versioned::value)
- .filter(node -> node.type().equals(NodeType.COMPUTE))
- .filter(node -> node.dataIp().isPresent())
- .filter(node -> node.state().equals(COMPLETE))
- .forEach(computeNode -> {
- selectGroupHandler.updateGatewayGroupBuckets(computeNode,
- ImmutableList.of(gatewayNode), NetworkMode.VXLAN, isInsert);
- log.trace("Updated gateway group on {} for vxlan mode", computeNode.intBridge());
- });
-
- nodeStore.values()
- .stream()
- .map(Versioned::value)
- .filter(node -> node.type().equals(NodeType.COMPUTE))
- .filter(node -> node.vlanPort().isPresent())
- .filter(node -> node.state().equals(COMPLETE))
- .forEach(computeNode -> {
- selectGroupHandler.updateGatewayGroupBuckets(computeNode,
- ImmutableList.of(gatewayNode), NetworkMode.VLAN, isInsert);
- log.trace("Updated gateway group on {} for vlan mode", computeNode.intBridge());
- });
-
- }
-
- private void initNode(OpenstackNode node) {
- NodeState state = node.state();
- state.process(this, node);
- log.debug("Processing node: {} state: {}", node.hostname(), state);
- }
-
- private void setNodeState(OpenstackNode node, NodeState newState) {
- nodeStore.put(node.hostname(), getUpdatedNode(node, newState));
- }
-
- private NodeState nodeState(OpenstackNode node) {
- if (!isOvsdbConnected(node) || !deviceService.isAvailable(node.intBridge()) ||
- !isBridgeCreated(node.ovsdbId(), INTEGRATION_BRIDGE)) {
- return INIT;
- }
-
- // TODO use device service when we use single ONOS cluster for both openstackNode and vRouter
- if (node.type().equals(NodeType.GATEWAY) &&
- !isBridgeCreated(node.ovsdbId(), ROUTER_BRIDGE)) {
- return INIT;
- }
-
- if (node.dataIp().isPresent() && !isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
- return DEVICE_CREATED;
- }
-
- if (node.vlanPort().isPresent() && !isIfaceCreated(node.ovsdbId(), node.vlanPort().get())) {
- return DEVICE_CREATED;
- }
-
- if (node.type().equals(NodeType.GATEWAY) && (
- !isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE) ||
- !isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) ||
- !isIfaceCreated(node.ovsdbId(), node.uplink().get()))) {
- return DEVICE_CREATED;
- }
- return COMPLETE;
- }
-
- private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
- Device device = deviceService.getDevice(deviceId);
- if (device == null || !device.is(BridgeConfig.class)) {
- return false;
- }
-
- BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
- return bridgeConfig.getPorts().stream()
- .anyMatch(port -> port.annotations().value(PORT_NAME).equals(ifaceName));
- }
-
- private boolean isBridgeCreated(DeviceId deviceId, String bridgeName) {
- Device device = deviceService.getDevice(deviceId);
- if (device == null || !device.is(BridgeConfig.class)) {
- return false;
- }
-
- BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
- return bridgeConfig.getBridges().stream()
- .anyMatch(bridge -> bridge.name().equals(bridgeName));
- }
-
- private void createBridge(OpenstackNode node, String bridgeName, DeviceId deviceId) {
- Device device = deviceService.getDevice(node.ovsdbId());
- if (device == null || !device.is(BridgeConfig.class)) {
- log.error("Failed to create integration bridge on {}", node.ovsdbId());
- return;
- }
-
- // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
- Set<IpAddress> controllerIps;
- if (bridgeName.equals(ROUTER_BRIDGE)) {
- controllerIps = Sets.newHashSet(node.routerController().get());
- } else {
- controllerIps = clusterService.getNodes().stream()
- .map(ControllerNode::ip)
- .collect(Collectors.toSet());
- }
-
- List<ControllerInfo> controllers = controllerIps.stream()
- .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
- .collect(Collectors.toList());
-
- String dpid = deviceId.toString().substring(DPID_BEGIN);
- BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
- .name(bridgeName)
- .failMode(BridgeDescription.FailMode.SECURE)
- .datapathId(dpid)
- .disableInBand()
- .controllers(controllers)
- .build();
-
- BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
- bridgeConfig.addBridge(bridgeDesc);
- }
-
- private void createTunnelInterface(OpenstackNode node) {
- if (isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
- return;
- }
-
- Device device = deviceService.getDevice(node.ovsdbId());
- if (device == null || !device.is(InterfaceConfig.class)) {
- log.error("Failed to create tunnel interface on {}", node.ovsdbId());
- return;
- }
-
- TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
- .deviceId(INTEGRATION_BRIDGE)
- .ifaceName(DEFAULT_TUNNEL)
- .type(VXLAN)
- .remote(TunnelEndPoints.flowTunnelEndpoint())
- .key(TunnelKeys.flowTunnelKey())
- .build();
-
- InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
- ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
- }
-
- private void createPatchInterface(OpenstackNode node) {
- checkArgument(node.type().equals(NodeType.GATEWAY));
- if (isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) &&
- isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE)) {
- return;
- }
-
- Device device = deviceService.getDevice(node.ovsdbId());
- if (device == null || !device.is(InterfaceConfig.class)) {
- log.error("Failed to create patch interfaces on {}", node.hostname());
- return;
- }
-
- PatchDescription patchIntg = DefaultPatchDescription.builder()
- .deviceId(INTEGRATION_BRIDGE)
- .ifaceName(PATCH_INTG_BRIDGE)
- .peer(PATCH_ROUT_BRIDGE)
- .build();
-
- PatchDescription patchRout = DefaultPatchDescription.builder()
- .deviceId(ROUTER_BRIDGE)
- .ifaceName(PATCH_ROUT_BRIDGE)
- .peer(PATCH_INTG_BRIDGE)
- .build();
-
- InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
- ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
- ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
- }
-
- private void addUplink(OpenstackNode node) {
- checkArgument(node.type().equals(NodeType.GATEWAY));
- if (isIfaceCreated(node.ovsdbId(), node.uplink().get())) {
- return;
- }
-
- Device device = deviceService.getDevice(node.ovsdbId());
- if (device == null || !device.is(BridgeConfig.class)) {
- log.error("Failed to add port {} on {}", node.uplink().get(), node.ovsdbId());
- return;
- }
-
- BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
- bridgeConfig.addPort(BridgeName.bridgeName(ROUTER_BRIDGE),
- node.uplink().get());
- }
-
- private void addVlanPort(OpenstackNode node) {
- if (isIfaceCreated(node.ovsdbId(), node.vlanPort().get())) {
- return;
- }
-
- Device device = deviceService.getDevice(node.ovsdbId());
- if (device == null || !device.is(BridgeConfig.class)) {
- log.error("Failed to add port {} on {}", node.vlanPort().get(), node.ovsdbId());
- return;
- }
-
- BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
- bridgeConfig.addPort(BridgeName.bridgeName(INTEGRATION_BRIDGE),
- node.vlanPort().get());
- }
-
- private boolean isOvsdbConnected(OpenstackNode node) {
- OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
- OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
- return deviceService.isAvailable(node.ovsdbId()) &&
- client != null &&
- client.isConnected();
- }
-
- private void connectOvsdb(OpenstackNode node) {
- ovsdbController.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
- }
-
- private Set<String> systemIfaces(OpenstackNode node) {
- Set<String> ifaces = Sets.newHashSet();
- node.dataIp().ifPresent(ip -> ifaces.add(DEFAULT_TUNNEL));
- node.vlanPort().ifPresent(ifaces::add);
- if (node.type().equals(NodeType.GATEWAY)) {
- ifaces.add(PATCH_INTG_BRIDGE);
- ifaces.add(PATCH_ROUT_BRIDGE);
- ifaces.add(node.uplink().get());
- }
- return ifaces;
- }
-
- private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
- OpenstackNode node = nodes().stream()
- .filter(n -> n.intBridge().equals(deviceId))
- .findFirst().orElseGet(() -> nodes().stream()
- .filter(n -> n.routerBridge().isPresent())
- .filter(n -> n.routerBridge().get().equals(deviceId))
- .findFirst().orElseGet(() -> nodes().stream()
- .filter(n -> n.ovsdbId().equals(deviceId))
- .findFirst().orElse(null)));
- return node;
- }
-
- private class OvsdbHandler implements ConnectionHandler<Device> {
-
- @Override
- public void connected(Device device) {
- OpenstackNode node = nodes().stream()
- .filter(n -> n.ovsdbId().equals(device.id()))
- .findFirst()
- .orElse(null);
- if (node != null) {
- setNodeState(node, nodeState(node));
- } else {
- log.debug("{} is detected on unregistered node, ignore it.", device.id());
- }
- }
-
- @Override
- public void disconnected(Device device) {
- OpenstackNode node = nodeByDeviceId(device.id());
- if (node != null) {
- log.warn("Device {} is disconnected", device.id());
- setNodeState(node, NodeState.INCOMPLETE);
- }
- }
- }
-
- private class BridgeHandler implements ConnectionHandler<Device> {
-
- @Override
- public void connected(Device device) {
- OpenstackNode node = nodeByDeviceId(device.id());
- if (node != null) {
- setNodeState(node, nodeState(node));
- } else {
- log.debug("{} is detected on unregistered node, ignore it.", device.id());
- }
- }
-
- @Override
- public void disconnected(Device device) {
- OpenstackNode node = nodeByDeviceId(device.id());
- if (node != null) {
- log.warn("Device {} is disconnected", device.id());
- setNodeState(node, NodeState.INCOMPLETE);
- }
- }
-
- /**
- * Handles port added situation.
- * If the added port is tunnel or data plane interface, proceed to the remaining
- * node initialization. Otherwise, do nothing.
- *
- * @param port port
- */
- public void portAdded(Port port) {
- OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
- String portName = port.annotations().value(PORT_NAME);
- if (node == null) {
- log.debug("{} is added to unregistered node, ignore it.", portName);
- return;
- }
-
- log.info("Port {} is added to {}", portName, node.hostname());
- if (systemIfaces(node).contains(portName)) {
- setNodeState(node, nodeState(node));
- }
- }
-
- /**
- * Handles port removed situation.
- * If the removed port is tunnel or data plane interface, proceed to the remaining
- * node initialization.Others, do nothing.
- *
- * @param port port
- */
- public void portRemoved(Port port) {
- OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
- String portName = port.annotations().value(PORT_NAME);
-
- if (node == null) {
- return;
- }
-
- log.info("Port {} is removed from {}", portName, node.hostname());
- if (systemIfaces(node).contains(portName)) {
- setNodeState(node, NodeState.INCOMPLETE);
- }
- }
- }
-
- private class InternalDeviceListener implements DeviceListener {
-
- @Override
- public void event(DeviceEvent event) {
-
- NodeId leaderNodeId = leadershipService.getLeader(appId.name());
- if (!Objects.equals(localNodeId, leaderNodeId)) {
- // do not allow to proceed without leadership
- return;
- }
-
- Device device = event.subject();
- ConnectionHandler<Device> handler =
- (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
-
- switch (event.type()) {
- // TODO implement OVSDB port event so that we can handle updates on the OVSDB
- case PORT_ADDED:
- eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
- break;
- case PORT_UPDATED:
- if (!event.port().isEnabled()) {
- eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
- }
- break;
- case DEVICE_ADDED:
- case DEVICE_AVAILABILITY_CHANGED:
- if (deviceService.isAvailable(device.id())) {
- eventExecutor.execute(() -> handler.connected(device));
- } else {
- eventExecutor.execute(() -> handler.disconnected(device));
- log.warn("OpenstackNode with device ID {} is disconnected", device.id());
- }
- break;
- default:
- break;
- }
- }
- }
-
- private void readConfiguration() {
- OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
- if (config == null) {
- log.debug("No configuration found");
- return;
- }
-
- Map<String, OpenstackNode> prevNodeMap = Maps.newHashMap(nodeStore.asJavaMap());
- config.openstackNodes().forEach(node -> {
- prevNodeMap.remove(node.hostname());
- addOrUpdateNode(node);
- });
- prevNodeMap.values().forEach(this::deleteNode);
- }
-
- private class InternalConfigListener implements NetworkConfigListener {
-
- @Override
- public void event(NetworkConfigEvent event) {
- NodeId leaderNodeId = leadershipService.getLeader(appId.name());
- if (!Objects.equals(localNodeId, leaderNodeId)) {
- // do not allow to proceed without leadership
- return;
- }
-
- if (!event.configClass().equals(CONFIG_CLASS)) {
- return;
- }
-
- switch (event.type()) {
- case CONFIG_ADDED:
- case CONFIG_UPDATED:
- eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
- break;
- default:
- break;
- }
- }
- }
-
- private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
-
- @Override
- public void event(MapEvent<String, OpenstackNode> event) {
- NodeId leaderNodeId = leadershipService.getLeader(appId.name());
- if (!Objects.equals(localNodeId, leaderNodeId)) {
- // do not allow to proceed without leadership
- return;
- }
-
- OpenstackNode oldNode;
- OpenstackNode newNode;
-
- switch (event.type()) {
- case UPDATE:
- oldNode = event.oldValue().value();
- newNode = event.newValue().value();
-
- log.info("Reloaded {}", newNode.hostname());
- if (!newNode.equals(oldNode)) {
- log.debug("New node: {}", newNode);
- }
- // performs init procedure even if the node is not changed
- // for robustness since it's no harm to run init procedure
- // multiple times
- eventExecutor.execute(() -> initNode(newNode));
- break;
- case INSERT:
- newNode = event.newValue().value();
- log.info("Added {}", newNode.hostname());
- eventExecutor.execute(() -> initNode(newNode));
- break;
- case REMOVE:
- oldNode = event.oldValue().value();
- log.info("Removed {}", oldNode.hostname());
- break;
- default:
- break;
- }
- }
- }
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
deleted file mode 100644
index 9600280..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * 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.openstacknode;
-
-import org.onlab.packet.IpAddress;
-import org.onosproject.core.GroupId;
-import org.onosproject.event.ListenerService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * Handles the bootstrap request for compute/gateway node.
- */
-public interface OpenstackNodeService
- extends ListenerService<OpenstackNodeEvent, OpenstackNodeListener> {
-
- enum NodeType {
- /**
- * Compute or Gateway Node.
- */
- COMPUTE,
- GATEWAY
- }
-
- enum NetworkMode {
- /**
- * VxLAN or VLAN mode.
- */
- VXLAN,
- VLAN
- }
-
- /**
- * Adds or updates a new node to the service.
- *
- * @param node openstack node
- */
- void addOrUpdateNode(OpenstackNode node);
-
- /**
- * Bootstraps node with INIT state.
- *
- * @param node openstack node
- */
- void processInitState(OpenstackNode node);
-
- /**
- * Bootstraps node with DEVICE_CREATED state.
- *
- * @param node openstack node
- */
- void processDeviceCreatedState(OpenstackNode node);
-
- /**
- * Bootstraps node with COMPLETE state.
- *
- * @param node openstack node
- */
- void processCompleteState(OpenstackNode node);
-
- /**
- * Bootstraps node with INCOMPLETE state.
- *
- * @param node openstack node
- */
- void processIncompleteState(OpenstackNode node);
-
- /**
- * Deletes a node from the service.
- *
- * @param node openstack node
- */
- void deleteNode(OpenstackNode node);
-
- /**
- * Returns all nodes known to the service.
- *
- * @return list of nodes
- */
- List<OpenstackNode> nodes();
-
- /**
- * Returns all nodes in complete state.
- *
- * @return set of nodes
- */
- Set<OpenstackNode> completeNodes();
-
- /**
- * Returns data network IP address of a given integration bridge device.
- *
- * @param intBridgeId integration bridge device id
- * @return ip address; empty value otherwise
- */
- Optional<IpAddress> dataIp(DeviceId intBridgeId);
-
- /**
- * Returns tunnel port number of a given integration bridge device.
- *
- * @param intBridgeId integration bridge device id
- * @return port number; or empty value
- */
- Optional<PortNumber> tunnelPort(DeviceId intBridgeId);
-
- /**
- * Returns vlan port number of a given integration bridge device.
- *
- * @param intBridgeId integration bridge device id
- * @return port number; or empty value
- */
- Optional<PortNumber> vlanPort(DeviceId intBridgeId);
-
- /**
- * Returns router bridge device ID connected to a given integration bridge.
- * It returns valid value only if the node type is GATEWAY.
- *
- * @param intBridgeId device id of the integration bridge
- * @return device id of a router bridge; or empty value
- */
- Optional<DeviceId> routerBridge(DeviceId intBridgeId);
-
- /**
- * Returns port number connected to the router bridge.
- * It returns valid value only if the node type is GATEWAY.
- *
- * @param intBridgeId integration bridge device id
- * @return port number; or empty value
- */
- Optional<PortNumber> externalPort(DeviceId intBridgeId);
-
- /**
- * Returns gateway node with the given device identifier.
- *
- * @param deviceId The gateway node deviceId
- * @return The gateway node information
- */
- OpenstackNode gatewayNode(DeviceId deviceId);
-
- /**
- * Returns group id for gateway load balance.
- * If the group does not exist in the supplied source device, creates one.
- *
- * @param srcDeviceId source device id
- * @param networkMode network mode
- * @return group id
- */
- GroupId gatewayGroupId(DeviceId srcDeviceId, NetworkMode networkMode);
-
- /**
- * Returns the list of gateway node information with the given device identifier.
- *
- * @return The list of gateway node information
- */
- List<OpenstackNode> gatewayNodes();
-
- /**
- * Returns the list of gateway`s device identifiers.
- *
- * @return The list of device identifier]
- */
- List<DeviceId> gatewayDeviceIds();
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
deleted file mode 100644
index 5f8e065..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright 2017-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.openstacknode;
-
-import com.google.common.collect.Lists;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.MacAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.GroupId;
-import org.onosproject.openstacknode.OpenstackNodeService.NetworkMode;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DefaultDriverData;
-import org.onosproject.net.driver.DefaultDriverHandler;
-import org.onosproject.net.driver.Driver;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.ExtensionPropertyException;
-import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
-import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.GroupBucket;
-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.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
-import static org.onosproject.net.AnnotationKeys.PORT_MAC;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknode.Constants.*;
-import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
-
-/**
- * Handles group generation request from OpenstackNode.
- */
-public class SelectGroupHandler {
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- private static final String TUNNEL_DESTINATION = "tunnelDst";
- private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
-
- private final GroupService groupService;
- private final DeviceService deviceService;
- private final DriverService driverService;
- private final ApplicationId appId;
-
- /**
- * Default constructor.
- *
- * @param targetGroupService group service
- * @param targetDeviceService device service
- * @param targetDriverService driver service
- * @param appId application id for group service
- */
- public SelectGroupHandler(GroupService targetGroupService, DeviceService targetDeviceService,
- DriverService targetDriverService, ApplicationId appId) {
- groupService = targetGroupService;
- deviceService = targetDeviceService;
- driverService = targetDriverService;
- this.appId = appId;
- }
-
- /**
- * Creates select type group description according to given deviceId.
- *
- * @param computeNode target device id for group description
- * @param gatewayNodeList gateway node list for bucket action
- */
- public void createGatewayGroup(OpenstackNode computeNode, List<OpenstackNode> gatewayNodeList) {
- List<GroupBucket> bucketList;
- GroupId groupId;
-
- if (computeNode.dataIp().isPresent()) {
- bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, NetworkMode.VXLAN);
- groupId = groupId(computeNode.intBridge(), NetworkMode.VXLAN);
-
- GroupDescription groupDescription = new DefaultGroupDescription(
- computeNode.intBridge(),
- GroupDescription.Type.SELECT,
- new GroupBuckets(bucketList),
- groupKey(computeNode.intBridge(), NetworkMode.VXLAN),
- groupId.id(),
- appId);
-
- groupService.addGroup(groupDescription);
- }
-
- if (computeNode.vlanPort().isPresent()) {
- bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, NetworkMode.VLAN);
- groupId = groupId(computeNode.intBridge(), NetworkMode.VLAN);
-
- GroupDescription groupDescription = new DefaultGroupDescription(
- computeNode.intBridge(),
- GroupDescription.Type.SELECT,
- new GroupBuckets(bucketList),
- groupKey(computeNode.intBridge(), NetworkMode.VLAN),
- groupId.id(),
- appId);
-
- groupService.addGroup(groupDescription);
- }
- }
-
- /**
- * Returns unique group key with supplied source device ID and network mode as a hash.
- * @param srcDeviceId source device id
- * @param networkMode network mode
- * @return group key
- */
- public GroupKey groupKey(DeviceId srcDeviceId, NetworkMode networkMode) {
- if (networkMode.equals(NetworkMode.VXLAN)) {
- return new DefaultGroupKey(srcDeviceId.toString().concat(DEFAULT_TUNNEL).getBytes());
- } else {
- return new DefaultGroupKey(srcDeviceId.toString().concat(VLAN).getBytes());
- }
- }
-
- private GroupId groupId(DeviceId srcDeviceId, NetworkMode networkMode) {
- if (networkMode.equals(NetworkMode.VXLAN)) {
- return new GroupId(srcDeviceId.toString().concat(DEFAULT_TUNNEL).hashCode());
- } else {
- return new GroupId(srcDeviceId.toString().concat(VLAN).hashCode());
- }
- }
-
-
- /**
- * Updates groupBuckets in select type group.
- *
- * @param computeNode compute node
- * @param gatewayNodeList updated gateway node list for bucket action
- * @param networkMode network mode
- * @param isInsert update type(add or remove)
- */
- public void updateGatewayGroupBuckets(OpenstackNode computeNode,
- List<OpenstackNode> gatewayNodeList,
- NetworkMode networkMode,
- boolean isInsert) {
- List<GroupBucket> bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, networkMode);
- GroupKey groupKey = groupKey(computeNode.intBridge(), networkMode);
- if (groupService.getGroup(computeNode.intBridge(), groupKey) == null) {
- log.error("There's no group in compute node {}", computeNode.intBridge());
- return;
- }
-
- if (isInsert) {
- groupService.addBucketsToGroup(
- computeNode.intBridge(),
- groupKey,
- new GroupBuckets(bucketList),
- groupKey, appId);
- } else {
- groupService.removeBucketsFromGroup(
- computeNode.intBridge(),
- groupKey,
- new GroupBuckets(bucketList),
- groupKey, appId);
- }
- }
-
-
-
- private List<GroupBucket> generateBucketsForSelectGroup(OpenstackNode computeNode,
- List<OpenstackNode> gatewayNodeList,
- NetworkMode networkMode) {
- List<GroupBucket> bucketList = Lists.newArrayList();
-
- switch (networkMode) {
- case VXLAN:
- gatewayNodeList.stream()
- .filter(node -> node.dataIp().isPresent())
- .forEach(node -> {
- TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
- .extension(buildNiciraExtenstion(computeNode.intBridge(),
- node.dataIp().get().getIp4Address()),
- computeNode.intBridge())
- .setOutput(getTunnelPort(computeNode.intBridge()))
- .build();
- bucketList.add(createSelectGroupBucket(tBuilder));
- });
- return bucketList;
- case VLAN:
- gatewayNodeList.stream()
- .filter(node -> node.vlanPort().isPresent())
- .forEach(node -> {
- TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
- .setEthDst(MacAddress.valueOf(vlanPortMac(node)))
- .setOutput(vlanPortNum(computeNode))
- .build();
- bucketList.add(createSelectGroupBucket(tBuilder));
- });
- return bucketList;
- default:
- final String error = String.format(
- ERR_UNSUPPORTED_NET_TYPE + "%s",
- networkMode.toString());
- throw new IllegalStateException(error);
- }
- }
-
- /**
- * Builds Nicira extension for tagging remoteIp of vxlan.
- *
- * @param id device id of vxlan source device
- * @param hostIp remote ip of vxlan destination device
- * @return NiciraExtension Treatment
- */
- private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
- Driver driver = driverService.getDriver(id);
- DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
- ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
-
- ExtensionTreatment extensionInstruction =
- resolver.getExtensionInstruction(
- ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type());
-
- try {
- extensionInstruction.setPropertyValue(TUNNEL_DESTINATION, hostIp);
- } catch (ExtensionPropertyException e) {
- log.error("Error setting Nicira extension setting {}", e);
- }
-
- return extensionInstruction;
- }
-
- /**
- * Returns port number of vxlan tunnel.
- *
- * @param deviceId target Device Id
- * @return portNumber
- */
- private PortNumber getTunnelPort(DeviceId deviceId) {
- Port port = deviceService.getPorts(deviceId).stream()
- .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL))
- .findAny().orElse(null);
-
- if (port == null) {
- log.error("No TunnelPort was created.");
- return null;
- }
- return port.number();
- }
-
- private PortNumber vlanPortNum(OpenstackNode node) {
- return deviceService.getPorts(node.intBridge()).stream()
- .filter(p -> p.annotations().value(PORT_NAME).equals(node.vlanPort().get()) &&
- p.isEnabled())
- .map(Port::number).findFirst().get();
-
- }
- private String vlanPortMac(OpenstackNode node) {
- return deviceService.getPorts(node.intBridge()).stream()
- .filter(p -> p.annotations().value(PORT_NAME).equals(node.vlanPort().get()) && p.isEnabled())
- .findFirst().get().annotations().value(PORT_MAC);
- }
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/Constants.java
similarity index 80%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/Constants.java
index 8bb75a1..9f812db 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/Constants.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
/**
* Provides constants used in OpenStack node services.
@@ -26,11 +26,6 @@
public static final String INTEGRATION_BRIDGE = "br-int";
public static final String ROUTER_BRIDGE = "br-router";
public static final String DEFAULT_TUNNEL = "vxlan";
- public static final String VLAN = "vlan";
public static final String PATCH_INTG_BRIDGE = "patch-intg";
public static final String PATCH_ROUT_BRIDGE = "patch-rout";
-
- public static final int DEFAULT_OVSDB_PORT = 6640;
- public static final int DEFAULT_OFPORT = 6653;
- public static final String DEFAULT_OF_PROTO = "tcp";
}
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/NodeState.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/NodeState.java
new file mode 100644
index 0000000..6d3a825
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/NodeState.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017-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.openstacknode.api;
+
+/**
+ * Defines the initialization states of OpenStack node.
+ */
+public enum NodeState {
+
+ /**
+ * Indicates the node is newly added.
+ */
+ INIT {
+ @Override
+ public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+ handler.processInitState(osNode);
+ }
+
+ @Override
+ public NodeState nextState() {
+ return DEVICE_CREATED;
+ }
+ },
+ /**
+ * Indicates bridge devices are added according to the node state.
+ */
+ DEVICE_CREATED {
+ @Override
+ public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+ handler.processDeviceCreatedState(osNode);
+ }
+
+ @Override
+ public NodeState nextState() {
+ return PORT_CREATED;
+ }
+ },
+ /**
+ * Indicates required ports are added.
+ */
+ PORT_CREATED {
+ @Override
+ public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+ handler.processPortCreatedState(osNode);
+ }
+
+ @Override
+ public NodeState nextState() {
+ return COMPLETE;
+ }
+ },
+ /**
+ * Indicates node initialization is done.
+ */
+ COMPLETE {
+ @Override
+ public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+ handler.processCompleteState(osNode);
+ }
+
+ @Override
+ public NodeState nextState() {
+ return COMPLETE;
+ }
+
+ },
+ /**
+ * Indicates node is broken.
+ */
+ INCOMPLETE {
+ @Override
+ public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+ handler.processIncompleteState(osNode);
+ }
+
+ @Override
+ public NodeState nextState() {
+ return INIT;
+ }
+ };
+
+ public abstract void process(OpenstackNodeHandler handler, OpenstackNode osNode);
+ public abstract NodeState nextState();
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
new file mode 100644
index 0000000..d380ec5
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2017-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.openstacknode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.group.GroupKey;
+
+/**
+ * Representation of a node used in OpenstackNetworking service.
+ */
+public interface OpenstackNode {
+
+ /**
+ * List of valid virtual network modes.
+ */
+ enum NetworkMode {
+ VXLAN,
+ VLAN
+ }
+
+ /**
+ * List of valid node types.
+ */
+ enum NodeType {
+ COMPUTE,
+ GATEWAY
+ }
+
+ /**
+ * Returns hostname of the node.
+ *
+ * @return hostname
+ */
+ String hostname();
+
+ /**
+ * Returns the type of the node.
+ *
+ * @return node type
+ */
+ NodeType type();
+
+ /**
+ * Returns the OVSDB device ID of the node.
+ *
+ * @return ovsdb device id
+ */
+ DeviceId ovsdb();
+
+ /**
+ * Returns the device ID of the integration bridge at the node.
+ *
+ * @return device id
+ */
+ DeviceId intgBridge();
+
+ /**
+ * Returns the router bridge device ID.
+ *
+ * @return device id; null if the node type is compute
+ */
+ DeviceId routerBridge();
+
+ /**
+ * Returns the management network IP address of the node.
+ *
+ * @return ip address
+ */
+ IpAddress managementIp();
+
+ /**
+ * Returns the data network IP address used for tunneling.
+ *
+ * @return ip address; null if vxlan mode is not enabled
+ */
+ IpAddress dataIp();
+
+ /**
+ * Returns the name of the vlan interface.
+ *
+ * @return vlan interface name; null if vlan mode is not enabled
+ */
+ String vlanIntf();
+
+ /**
+ * Returns the initialization state of the node.
+ *
+ * @return node state
+ */
+ NodeState state();
+
+ /**
+ * Returns the gateway group ID of this node.
+ *
+ * @param mode network mode of the group
+ * @return gateway group identifier
+ */
+ GroupId gatewayGroupId(NetworkMode mode);
+
+ /**
+ * Returns the group key of this node.
+ *
+ * @param mode network mode of the group
+ * @return gateway group key
+ */
+ GroupKey gatewayGroupKey(NetworkMode mode);
+
+ /**
+ * Returns the tunnel port number.
+ *
+ * @return port number; null if tunnel port does not exist
+ */
+ PortNumber tunnelPortNum();
+
+ /**
+ * Returns the vlan port nubmer.
+ *
+ * @return port number; null if vlan port does not exist
+ */
+ PortNumber vlanPortNum();
+
+ /**
+ * Returns the patch port number of the integration bridge.
+ *
+ * @return port number; null if the node type is compute
+ */
+ PortNumber patchPortNum();
+
+ /**
+ * Returns the vlan port MAC address.
+ *
+ * @return mac address; null if vlan port does not exist
+ */
+ MacAddress vlanPortMac();
+
+ /**
+ * Returns new openstack node instance with given state.
+ *
+ * @param newState updated state
+ * @return updated openstack node
+ */
+ OpenstackNode updateState(NodeState newState);
+
+ /**
+ * Builder of new node entities.
+ */
+ interface Builder {
+
+ /**
+ * Builds an immutable openstack node instance.
+ *
+ * @return openstack node instance
+ */
+ OpenstackNode build();
+
+ /**
+ * Returns openstack node builder with supplied hostname.
+ *
+ * @param hostname hostname of the node
+ * @return opesntack node builder
+ */
+ Builder hostname(String hostname);
+
+ /**
+ * Returns openstack node builder with supplied type.
+ *
+ * @param type openstack node type
+ * @return openstack node builder
+ */
+ Builder type(NodeType type);
+
+ /**
+ * Returns openstack node builder with supplied integration bridge ID.
+ *
+ * @param intgBridge integration bridge device id
+ * @return openstack node builder
+ */
+ Builder intgBridge(DeviceId intgBridge);
+
+ /**
+ * Returns openstack node builder with supplied router bridge ID.
+ *
+ * @param routerBridge router bridge id
+ * @return openstack node builder
+ */
+ Builder routerBridge(DeviceId routerBridge);
+
+ /**
+ * Returns openstack node builder with supplied management IP address.
+ *
+ * @param managementIp management ip address
+ * @return openstack node builder
+ */
+ Builder managementIp(IpAddress managementIp);
+
+ /**
+ * Returns openstack node builder with supplied data network IP address.
+ *
+ * @param dataIp data network ip address
+ * @return openstack node builder
+ */
+ Builder dataIp(IpAddress dataIp);
+
+ /**
+ * Returns openstack node builder with supplied vlan interface.
+ *
+ * @param vlanIntf vlan interface name
+ * @return openstack node builder
+ */
+ Builder vlanIntf(String vlanIntf);
+
+ /**
+ * Returns openstack node builder with supplied node state.
+ *
+ * @param state node state
+ * @return openstack node builder
+ */
+ Builder state(NodeState state);
+ }
+}
+
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeAdminService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeAdminService.java
new file mode 100644
index 0000000..cfef1aa
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeAdminService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-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.openstacknode.api;
+
+/**
+ * Service for administering inventory of opestackNode.
+ */
+public interface OpenstackNodeAdminService {
+
+ /**
+ * Creates a new node.
+ *
+ * @param osNode openstack node
+ */
+ void createNode(OpenstackNode osNode);
+
+ /**
+ * Updates the node.
+ *
+ * @param osNode openstack node
+ */
+ void updateNode(OpenstackNode osNode);
+
+ /**
+ * Removes the node.
+ *
+ * @param hostname openstack node hostname
+ * @return removed node; null if the node does not exist
+ */
+ OpenstackNode removeNode(String hostname);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeConfig.java
similarity index 71%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeConfig.java
index 0f264e9..774342a 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
import com.fasterxml.jackson.databind.JsonNode;
@@ -22,13 +22,14 @@
import org.onlab.packet.IpAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.DeviceId;
-import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
-import java.util.Set;
import org.onosproject.net.config.Config;
+import org.onosproject.openstacknode.impl.DefaultOpenstackNode;
+
+import java.util.Set;
import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
/**
* Configuration object for OpensatckNode service.
@@ -44,22 +45,17 @@
// GATEWAY node specific fields
private static final String ROUTER_BRIDGE = "routerBridge";
- private static final String UPLINK_PORT_NAME = "uplinkPort";
- // TODO remove this when vRouter supports multiple switches
- private static final String ROUTER_CONTROLLER = "routerController";
- private static final String VLAN_PORT_NAME = "vlanPort";
+ private static final String VLAN_INTF_NAME = "vlanPort";
@Override
public boolean isValid() {
boolean result = hasOnlyFields(NODES);
-
if (object.get(NODES) == null || object.get(NODES).size() < 1) {
- final String msg = "No node is present";
- throw new IllegalArgumentException(msg);
+ return true;
}
for (JsonNode node : object.get(NODES)) {
- if (get(node, DATA_IP) == null && get(node, VLAN_PORT_NAME) == null) {
+ if (get(node, DATA_IP) == null && get(node, VLAN_INTF_NAME) == null) {
final String msg = "There is neither tunnel interface nor vlan port";
throw new IllegalArgumentException(msg);
}
@@ -71,26 +67,22 @@
DATA_IP,
INTEGRATION_BRIDGE,
ROUTER_BRIDGE,
- UPLINK_PORT_NAME,
- ROUTER_CONTROLLER,
- VLAN_PORT_NAME
+ VLAN_INTF_NAME
);
result &= isString(osNode, HOST_NAME, MANDATORY);
result &= isString(osNode, TYPE, MANDATORY);
result &= isIpAddress(osNode, MANAGEMENT_IP, MANDATORY);
result &= isString(osNode, INTEGRATION_BRIDGE, MANDATORY);
- result &= isString(osNode, VLAN_PORT_NAME, OPTIONAL);
+ result &= isString(osNode, VLAN_INTF_NAME, OPTIONAL);
result &= isIpAddress(osNode, DATA_IP, OPTIONAL);
DeviceId.deviceId(osNode.get(INTEGRATION_BRIDGE).asText());
- NodeType.valueOf(osNode.get(TYPE).asText());
+ OpenstackNode.NodeType.valueOf(osNode.get(TYPE).asText());
if (osNode.get(TYPE).asText().equals(GATEWAY.name())) {
result &= isString(osNode, ROUTER_BRIDGE, MANDATORY);
DeviceId.deviceId(osNode.get(ROUTER_BRIDGE).asText());
- result &= isString(osNode, UPLINK_PORT_NAME, MANDATORY);
- result &= isIpAddress(osNode, ROUTER_CONTROLLER, MANDATORY);
}
}
return result;
@@ -104,25 +96,22 @@
public Set<OpenstackNode> openstackNodes() {
Set<OpenstackNode> nodes = Sets.newHashSet();
for (JsonNode node : object.get(NODES)) {
- NodeType type = NodeType.valueOf(get(node, TYPE));
- OpenstackNode.Builder nodeBuilder = OpenstackNode.builder()
- .integrationBridge(DeviceId.deviceId(get(node, INTEGRATION_BRIDGE)))
+ OpenstackNode.NodeType type = OpenstackNode.NodeType.valueOf(get(node, TYPE));
+ OpenstackNode.Builder nodeBuilder = DefaultOpenstackNode.builder()
+ .intgBridge(DeviceId.deviceId(get(node, INTEGRATION_BRIDGE)))
.managementIp(IpAddress.valueOf(get(node, MANAGEMENT_IP)))
.type(type)
- .hostname(get(node, HOST_NAME));
+ .hostname(get(node, HOST_NAME))
+ .state(NodeState.INIT);
if (get(node, DATA_IP) != null) {
nodeBuilder.dataIp(IpAddress.valueOf(get(node, DATA_IP)));
}
-
- if (get(node, VLAN_PORT_NAME) != null) {
- nodeBuilder.vlanPort(get(node, VLAN_PORT_NAME));
+ if (get(node, VLAN_INTF_NAME) != null) {
+ nodeBuilder.vlanIntf(get(node, VLAN_INTF_NAME));
}
-
if (type.equals(GATEWAY)) {
- nodeBuilder.routerBridge(DeviceId.deviceId(get(node, ROUTER_BRIDGE)))
- .uplink(get(node, UPLINK_PORT_NAME))
- .routerController(IpAddress.valueOf(get(node, ROUTER_CONTROLLER)));
+ nodeBuilder.routerBridge(DeviceId.deviceId(get(node, ROUTER_BRIDGE)));
}
nodes.add(nodeBuilder.build());
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeEvent.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeEvent.java
new file mode 100644
index 0000000..68ac50e
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeEvent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.openstacknode.api;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes OpenStack node init state event.
+ */
+public class OpenstackNodeEvent extends AbstractEvent<OpenstackNodeEvent.Type, OpenstackNode> {
+
+ public enum Type {
+
+ /**
+ * Signifies that new node is created.
+ */
+ OPENSTACK_NODE_CREATED,
+
+ /**
+ * Signifies that the node state is updated.
+ */
+ OPENSTACK_NODE_UPDATED,
+
+ /**
+ * Signifies that the node state is complete.
+ */
+ OPENSTACK_NODE_COMPLETE,
+
+ /**
+ * Signifies that the node state is removed.
+ */
+ OPENSTACK_NODE_REMOVED,
+
+ /**
+ * Signifies that the node state is changed to incomplete.
+ */
+ OPENSTACK_NODE_INCOMPLETE
+ }
+
+ /**
+ * Creates an event with the given type and node.
+ *
+ * @param type event type
+ * @param node openstack node
+ */
+ public OpenstackNodeEvent(Type type, OpenstackNode node) {
+ super(type, node);
+ }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeHandler.java
new file mode 100644
index 0000000..fa0bb33
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeHandler.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017-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.openstacknode.api;
+
+/**
+ * Service handling openstack node state.
+ */
+public interface OpenstackNodeHandler {
+
+ /**
+ * Processes the given node for init state.
+ * It creates required bridges on OVS based on the node type.
+ *
+ * @param osNode openstack node
+ */
+ void processInitState(OpenstackNode osNode);
+
+ /**
+ * Processes the given node for device created state.
+ * It creates required ports on the bridges based on the node type.
+ *
+ * @param osNode openstack node
+ */
+ void processDeviceCreatedState(OpenstackNode osNode);
+
+ /**
+ * Processes the given node for port created state.
+ * It creates gateway groups on compute node.
+ *
+ * @param osNode openstack node
+ */
+ void processPortCreatedState(OpenstackNode osNode);
+
+ /**
+ * Processes the given node for complete state.
+ * It performs post-init jobs for the complete node.
+ *
+ * @param osNode openstack node
+ */
+ void processCompleteState(OpenstackNode osNode);
+
+ /**
+ * Processes the given node for incomplete state.
+ *
+ * @param osNode openstack node
+ */
+ void processIncompleteState(OpenstackNode osNode);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeListener.java
similarity index 83%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeListener.java
index df8cf0d..b462624 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeListener.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-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.
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
import org.onosproject.event.EventListener;
/**
- * Listener for OpenStack node events.
+ * Listener for OpenstackNode event.
*/
public interface OpenstackNodeListener extends EventListener<OpenstackNodeEvent> {
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
new file mode 100644
index 0000000..592593a
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2017-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.openstacknode.api;
+
+import org.onosproject.event.ListenerService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.openstacknode.api.OpenstackNode.NodeType;
+
+import java.util.Set;
+
+/**
+ * Service for interfacing with the inventory of {@link OpenstackNode}.
+ */
+public interface OpenstackNodeService extends ListenerService<OpenstackNodeEvent, OpenstackNodeListener> {
+
+ String APP_ID = "org.onosproject.openstacknode";
+
+ /**
+ * Returns all registered nodes.
+ *
+ * @return set of openstack nodes
+ */
+ Set<OpenstackNode> nodes();
+
+ /**
+ * Returns all nodes with the specified type.
+ *
+ * @param type node type
+ * @return set of openstack nodes
+ */
+ Set<OpenstackNode> nodes(NodeType type);
+
+ /**
+ * Returns all nodes with complete state.
+ *
+ * @return set of openstack nodes
+ */
+ Set<OpenstackNode> completeNodes();
+
+ /**
+ * Returns all nodes with complete state and the specified type.
+ *
+ * @param type node type
+ * @return set of openstack nodes
+ */
+ Set<OpenstackNode> completeNodes(NodeType type);
+
+ /**
+ * Returns the node with the specified hostname.
+ *
+ * @param hostname hostname
+ * @return openstack node
+ */
+ OpenstackNode node(String hostname);
+
+ /**
+ * Returns the node with the specified device ID.
+ * The device ID can be any one of integration bridge, router bridge,
+ * or ovsdb device.
+ *
+ * @param deviceId device id
+ * @return openstack node
+ */
+ OpenstackNode node(DeviceId deviceId);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStore.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStore.java
new file mode 100644
index 0000000..3a0a1ca
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStore.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-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.openstacknode.api;
+
+import org.onosproject.store.Store;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of OpenstackNode; not intended for direct use.
+ */
+public interface OpenstackNodeStore extends Store<OpenstackNodeEvent, OpenstackNodeStoreDelegate> {
+
+ /**
+ * Creates a new node.
+ *
+ * @param osNode openstack node
+ */
+ void createNode(OpenstackNode osNode);
+
+ /**
+ * Updates the node.
+ *
+ * @param osNode openstack node
+ */
+ void updateNode(OpenstackNode osNode);
+
+ /**
+ * Removes the node.
+ *
+ * @param hostname openstack node hostname
+ * @return removed openstack node; null if no node mapped for the hostname
+ */
+ OpenstackNode removeNode(String hostname);
+
+ /**
+ * Returns all registered nodes.
+ *
+ * @return set of openstack nodes
+ */
+ Set<OpenstackNode> nodes();
+
+ /**
+ * Returns the node with the specified hostname.
+ *
+ * @param hostname hostname
+ * @return openstack node
+ */
+ OpenstackNode node(String hostname);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStoreDelegate.java
similarity index 68%
copy from apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
copy to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStoreDelegate.java
index df8cf0d..34de865 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStoreDelegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-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.
@@ -13,12 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
-import org.onosproject.event.EventListener;
+import org.onosproject.store.StoreDelegate;
/**
- * Listener for OpenStack node events.
+ * OpenstackNode store delegate.
*/
-public interface OpenstackNodeListener extends EventListener<OpenstackNodeEvent> {
+public interface OpenstackNodeStoreDelegate extends StoreDelegate<OpenstackNodeEvent> {
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/package-info.java
similarity index 86%
copy from apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
copy to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/package-info.java
index c280f54..3bf6709 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-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.
@@ -17,4 +17,4 @@
/**
* Application for bootstrapping Compute/Gateway Node in OpenStack.
*/
-package org.onosproject.openstacknode;
\ No newline at end of file
+package org.onosproject.openstacknode.api;
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
index 4d54abd..a35ab1b 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
@@ -25,14 +25,14 @@
import org.onosproject.net.behaviour.BridgeConfig;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortDescription;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
import java.util.Optional;
import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknode.Constants.*;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.Constants.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
/**
* Checks detailed node init state.
@@ -50,22 +50,17 @@
@Override
protected void execute() {
- OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+ OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
- OpenstackNode node = nodeService.nodes()
- .stream()
- .filter(n -> n.hostname().equals(hostname))
- .findFirst()
- .orElse(null);
-
- if (node == null) {
- print("Cannot find %s from registered nodes", hostname);
+ OpenstackNode osNode = osNodeService.node(hostname);
+ if (osNode == null) {
+ error("Cannot find %s from registered nodes", hostname);
return;
}
print("[Integration Bridge Status]");
- Device device = deviceService.getDevice(node.intBridge());
+ Device device = deviceService.getDevice(osNode.intgBridge());
if (device != null) {
print("%s %s=%s available=%s %s",
deviceService.isAvailable(device.id()) ? MSG_OK : MSG_NO,
@@ -73,41 +68,39 @@
device.id(),
deviceService.isAvailable(device.id()),
device.annotations());
-
- node.dataIp().ifPresent(ip -> print(getPortState(deviceService, node.intBridge(), DEFAULT_TUNNEL)));
- node.vlanPort().ifPresent(p -> print(getPortState(deviceService, node.intBridge(), p)));
+ if (osNode.dataIp() != null) {
+ print(getPortState(deviceService, osNode.intgBridge(), DEFAULT_TUNNEL));
+ }
+ if (osNode.vlanIntf() != null) {
+ print(getPortState(deviceService, osNode.intgBridge(), osNode.vlanIntf()));
+ }
} else {
print("%s %s=%s is not available",
MSG_NO,
INTEGRATION_BRIDGE,
- node.intBridge());
+ osNode.intgBridge());
}
- if (node.type().equals(GATEWAY)) {
- print(getPortState(deviceService, node.intBridge(), PATCH_INTG_BRIDGE));
+ if (osNode.type() == GATEWAY) {
+ print(getPortState(deviceService, osNode.intgBridge(), PATCH_INTG_BRIDGE));
print("%n[Router Bridge Status]");
- device = deviceService.getDevice(node.ovsdbId());
+ device = deviceService.getDevice(osNode.ovsdb());
if (device == null || !device.is(BridgeConfig.class)) {
print("%s %s=%s is not available(unable to connect OVSDB)",
MSG_NO,
ROUTER_BRIDGE,
- node.intBridge());
+ osNode.intgBridge());
} else {
BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
boolean available = bridgeConfig.getBridges().stream()
- .filter(bridge -> bridge.name().equals(ROUTER_BRIDGE))
- .findAny()
- .isPresent();
-
+ .anyMatch(bridge -> bridge.name().equals(ROUTER_BRIDGE));
print("%s %s=%s available=%s",
available ? MSG_OK : MSG_NO,
ROUTER_BRIDGE,
- node.routerBridge().get(),
+ osNode.routerBridge(),
available);
-
- print(getPortStateOvsdb(deviceService, node.ovsdbId(), PATCH_ROUT_BRIDGE));
- print(getPortStateOvsdb(deviceService, node.ovsdbId(), node.uplink().get()));
+ print(getPortStateOvsdb(deviceService, osNode.ovsdb(), PATCH_ROUT_BRIDGE));
}
}
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
index 8f4dbb1..85da391 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
@@ -19,10 +19,10 @@
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeService;
-
-import java.util.NoSuchElementException;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
/**
* Initializes nodes for OpenStack node service.
@@ -37,21 +37,19 @@
@Override
protected void execute() {
- OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+ OpenstackNodeService osNodeService =
+ AbstractShellCommand.get(OpenstackNodeService.class);
+ OpenstackNodeAdminService osNodeAdminService =
+ AbstractShellCommand.get(OpenstackNodeAdminService.class);
for (String hostname : hostnames) {
- OpenstackNode node;
- try {
- node = nodeService.nodes()
- .stream()
- .filter(n -> n.hostname().equals(hostname))
- .findFirst().get();
- } catch (NoSuchElementException e) {
+ OpenstackNode osNode = osNodeService.node(hostname);
+ if (osNode == null) {
print("Unable to find %s", hostname);
continue;
}
-
- nodeService.addOrUpdateNode(node);
+ OpenstackNode updated = osNode.updateState(NodeState.INIT);
+ osNodeAdminService.updateNode(updated);
}
}
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
index 65a77e6..ceed2a7 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
@@ -19,11 +19,13 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.Lists;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import java.util.Comparator;
import java.util.List;
/**
@@ -37,43 +39,45 @@
@Override
protected void execute() {
- OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
- List<OpenstackNode> nodes = nodeService.nodes();
- nodes.sort(OpenstackNode.OPENSTACK_NODE_COMPARATOR);
+ OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+ List<OpenstackNode> osNodes = Lists.newArrayList(osNodeService.nodes());
+ osNodes.sort(Comparator.comparing(OpenstackNode::hostname));
if (outputJson()) {
- print("%s", json(nodes));
+ print("%s", json(osNodes));
} else {
print(FORMAT, "Hostname", "Type", "Integration Bridge", "Router Bridge",
"Management IP", "Data IP", "VLAN Intf", "State");
- for (OpenstackNode node : nodes) {
+ for (OpenstackNode osNode : osNodes) {
print(FORMAT,
- node.hostname(),
- node.type(),
- node.intBridge(),
- node.routerBridge().isPresent() ? node.routerBridge().get() : "",
- node.managementIp(),
- node.dataIp().isPresent() ? node.dataIp().get() : "",
- node.vlanPort().isPresent() ? node.vlanPort().get() : "",
- node.state());
+ osNode.hostname(),
+ osNode.type(),
+ osNode.intgBridge(),
+ osNode.routerBridge() != null ? osNode.routerBridge() : "",
+ osNode.managementIp(),
+ osNode.dataIp() != null ? osNode.dataIp() : "",
+ osNode.vlanIntf() != null ? osNode.vlanIntf() : "",
+ osNode.state());
}
- print("Total %s nodes", nodeService.nodes().size());
+ print("Total %s nodes", osNodeService.nodes().size());
}
}
- private JsonNode json(List<OpenstackNode> nodes) {
+ private JsonNode json(List<OpenstackNode> osNodes) {
ObjectMapper mapper = new ObjectMapper();
ArrayNode result = mapper.createArrayNode();
- for (OpenstackNode node : nodes) {
+ for (OpenstackNode osNode : osNodes) {
result.add(mapper.createObjectNode()
- .put("hostname", node.hostname())
- .put("type", node.type().name())
- .put("intBridge", node.intBridge().toString())
- .put("routerBridge", node.routerBridge().toString())
- .put("managementIp", node.managementIp().toString())
- .put("dataIp", node.dataIp().toString())
- .put("vlanPort", node.vlanPort().toString())
- .put("state", node.state().name()));
+ .put("hostname", osNode.hostname())
+ .put("type", osNode.type().name())
+ .put("integrationBridge", osNode.intgBridge().toString())
+ .put("routerBridge", osNode.routerBridge().toString())
+ .put("managementIp", osNode.managementIp().toString())
+ .put("dataIp", osNode.dataIp().toString())
+ .put("vlanIntf", osNode.vlanIntf())
+ .put("tunnelPortNum", osNode.tunnelPortNum().toString())
+ .put("vlanPortNum", osNode.vlanPortNum().toString())
+ .put("state", osNode.state().name()));
}
return result;
}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
new file mode 100644
index 0000000..670d4e8
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2017-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.openstacknode.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.net.AnnotationKeys.PORT_MAC;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
+import static org.onosproject.openstacknode.api.Constants.PATCH_INTG_BRIDGE;
+
+/**
+ * Representation of a openstack node.
+ */
+public final class DefaultOpenstackNode implements OpenstackNode {
+
+ private final String hostname;
+ private final NodeType type;
+ private final DeviceId intgBridge;
+ private final DeviceId routerBridge;
+ private final IpAddress managementIp;
+ private final IpAddress dataIp;
+ private final String vlanIntf;
+ private final NodeState state;
+
+ private DefaultOpenstackNode(String hostname,
+ NodeType type,
+ DeviceId intgBridge,
+ DeviceId routerBridge,
+ IpAddress managementIp,
+ IpAddress dataIp,
+ String vlanIntf,
+ NodeState state) {
+ this.hostname = hostname;
+ this.type = type;
+ this.intgBridge = intgBridge;
+ this.routerBridge = routerBridge;
+ this.managementIp = managementIp;
+ this.dataIp = dataIp;
+ this.vlanIntf = vlanIntf;
+ this.state = state;
+ }
+
+ @Override
+ public String hostname() {
+ return hostname;
+ }
+
+ @Override
+ public NodeType type() {
+ return type;
+ }
+
+ @Override
+ public DeviceId ovsdb() {
+ return DeviceId.deviceId("ovsdb:" + managementIp().toString());
+ }
+
+ @Override
+ public DeviceId intgBridge() {
+ return intgBridge;
+ }
+
+ @Override
+ public DeviceId routerBridge() {
+ return routerBridge;
+ }
+
+ @Override
+ public IpAddress managementIp() {
+ return managementIp;
+ }
+
+ @Override
+ public IpAddress dataIp() {
+ return dataIp;
+ }
+
+ @Override
+ public String vlanIntf() {
+ return vlanIntf;
+ }
+
+ @Override
+ public NodeState state() {
+ return state;
+ }
+
+ @Override
+ public GroupKey gatewayGroupKey(NetworkMode mode) {
+ return new DefaultGroupKey(intgBridge.toString().concat(mode.name()).getBytes());
+ }
+
+ @Override
+ public PortNumber tunnelPortNum() {
+ if (dataIp == null) {
+ return null;
+ }
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+ Port port = deviceService.getPorts(intgBridge).stream()
+ .filter(p -> p.isEnabled() &&
+ Objects.equals(p.annotations().value(PORT_NAME), DEFAULT_TUNNEL))
+ .findAny().orElse(null);
+ return port != null ? port.number() : null;
+ }
+
+ @Override
+ public PortNumber vlanPortNum() {
+ if (vlanIntf == null) {
+ return null;
+ }
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+ Port port = deviceService.getPorts(intgBridge).stream()
+ .filter(p -> p.isEnabled() &&
+ Objects.equals(p.annotations().value(PORT_NAME), vlanIntf))
+ .findAny().orElse(null);
+ return port != null ? port.number() : null;
+ }
+
+ @Override
+ public PortNumber patchPortNum() {
+ if (type == NodeType.COMPUTE) {
+ return null;
+ }
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+ Port port = deviceService.getPorts(intgBridge).stream()
+ .filter(p -> p.isEnabled() &&
+ Objects.equals(p.annotations().value(PORT_NAME), PATCH_INTG_BRIDGE))
+ .findAny().orElse(null);
+ return port != null ? port.number() : null;
+ }
+
+ @Override
+ public MacAddress vlanPortMac() {
+ if (vlanIntf == null) {
+ return null;
+ }
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+ Port port = deviceService.getPorts(intgBridge).stream()
+ .filter(p -> p.annotations().value(PORT_NAME).equals(vlanIntf))
+ .findAny().orElse(null);
+ return port != null ? MacAddress.valueOf(port.annotations().value(PORT_MAC)) : null;
+ }
+
+ @Override
+ public GroupId gatewayGroupId(NetworkMode mode) {
+ return new GroupId(intgBridge.toString().concat(mode.name()).hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof DefaultOpenstackNode) {
+ DefaultOpenstackNode that = (DefaultOpenstackNode) obj;
+ if (Objects.equals(hostname, that.hostname) &&
+ Objects.equals(type, that.type) &&
+ Objects.equals(intgBridge, that.intgBridge) &&
+ Objects.equals(routerBridge, that.routerBridge) &&
+ Objects.equals(managementIp, that.managementIp) &&
+ Objects.equals(dataIp, that.dataIp) &&
+ Objects.equals(vlanIntf, that.vlanIntf)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(hostname,
+ type,
+ intgBridge,
+ routerBridge,
+ managementIp,
+ dataIp,
+ vlanIntf);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("hostname", hostname)
+ .add("type", type)
+ .add("integrationBridge", intgBridge)
+ .add("routerBridge", routerBridge)
+ .add("managementIp", managementIp)
+ .add("dataIp", dataIp)
+ .add("vlanIntf", vlanIntf)
+ .add("state", state)
+ .toString();
+ }
+
+ @Override
+ public OpenstackNode updateState(NodeState newState) {
+ return new Builder()
+ .type(type)
+ .hostname(hostname)
+ .intgBridge(intgBridge)
+ .routerBridge(routerBridge)
+ .managementIp(managementIp)
+ .dataIp(dataIp)
+ .vlanIntf(vlanIntf)
+ .state(newState)
+ .build();
+ }
+
+ /**
+ * Returns new builder instance.
+ *
+ * @return openstack node builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Returns new builder instance with the given node as a default value.
+ *
+ * @param osNode openstack node
+ * @return openstack node builder
+ */
+ public static Builder from(OpenstackNode osNode) {
+ return new Builder()
+ .hostname(osNode.hostname())
+ .intgBridge(osNode.intgBridge())
+ .routerBridge(osNode.routerBridge())
+ .managementIp(osNode.managementIp())
+ .dataIp(osNode.dataIp())
+ .vlanIntf(osNode.vlanIntf())
+ .state(osNode.state());
+ }
+
+ public static final class Builder implements OpenstackNode.Builder {
+
+ private String hostname;
+ private NodeType type;
+ private DeviceId intgBridge;
+ private DeviceId routerBridge;
+ private IpAddress managementIp;
+ private IpAddress dataIp;
+ private String vlanIntf;
+ private NodeState state;
+
+ private Builder() {
+ }
+
+ @Override
+ public DefaultOpenstackNode build() {
+ checkArgument(hostname != null, "Node hostname cannot be null");
+ checkArgument(type != null, "Node type cannot be null");
+ checkArgument(intgBridge != null, "Node integration bridge cannot be null");
+ checkArgument(managementIp != null, "Node management IP cannot be null");
+ checkArgument(state != null, "Node state cannot be null");
+
+ if (type == NodeType.GATEWAY && routerBridge == null) {
+ throw new IllegalArgumentException("Router bridge is required for gateway node");
+ }
+ if (dataIp == null && Strings.isNullOrEmpty(vlanIntf)) {
+ throw new IllegalArgumentException("Either data IP or VLAN interface is required");
+ }
+
+ return new DefaultOpenstackNode(hostname,
+ type,
+ intgBridge,
+ routerBridge,
+ managementIp,
+ dataIp,
+ vlanIntf,
+ state);
+ }
+
+ @Override
+ public Builder hostname(String hostname) {
+ if (!Strings.isNullOrEmpty(hostname)) {
+ this.hostname = hostname;
+ }
+ return this;
+ }
+
+ @Override
+ public Builder type(NodeType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public Builder intgBridge(DeviceId intgBridge) {
+ this.intgBridge = intgBridge;
+ return this;
+ }
+
+ @Override
+ public Builder routerBridge(DeviceId routerBridge) {
+ this.routerBridge = routerBridge;
+ return this;
+ }
+
+ @Override
+ public Builder managementIp(IpAddress managementIp) {
+ this.managementIp = managementIp;
+ return this;
+ }
+
+ @Override
+ public Builder dataIp(IpAddress dataIp) {
+ this.dataIp = dataIp;
+ return this;
+ }
+
+ @Override
+ public Builder vlanIntf(String vlanIntf) {
+ this.vlanIntf = vlanIntf;
+ return this;
+ }
+
+ @Override
+ public Builder state(NodeState state) {
+ this.state = state;
+ return this;
+ }
+ }
+}
+
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
new file mode 100644
index 0000000..c0d2c3c
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
@@ -0,0 +1,805 @@
+/*
+ * Copyright 2017-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.openstacknode.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+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.onlab.packet.IpAddress;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.behaviour.DefaultBridgeDescription;
+import org.onosproject.net.behaviour.DefaultPatchDescription;
+import org.onosproject.net.behaviour.DefaultTunnelDescription;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.PatchDescription;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoints;
+import org.onosproject.net.behaviour.TunnelKeys;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionPropertyException;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNode.NetworkMode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeHandler;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.packet.TpPort.tpPort;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
+import static org.onosproject.openstacknode.api.Constants.*;
+import static org.onosproject.openstacknode.api.Constants.PATCH_INTG_BRIDGE;
+import static org.onosproject.openstacknode.api.NodeState.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NetworkMode.VLAN;
+import static org.onosproject.openstacknode.api.OpenstackNode.NetworkMode.VXLAN;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Service bootstraps openstack node based on its type.
+ */
+@Component(immediate = true)
+public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String OVSDB_PORT = "ovsdbPortNum";
+ private static final int DEFAULT_OVSDB_PORT = 6640;
+ private static final String DEFAULT_OF_PROTO = "tcp";
+ private static final int DEFAULT_OFPORT = 6653;
+ private static final int DPID_BEGIN = 3;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceAdminService deviceAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OvsdbController ovsdbController;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeAdminService osNodeAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService componentConfigService;
+
+ @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
+ label = "OVSDB server listen port")
+ private int ovsdbPort = DEFAULT_OVSDB_PORT;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+ private final DeviceListener ovsdbListener = new InternalOvsdbListener();
+ private final DeviceListener bridgeListener = new InternalBridgeListener();
+ private final GroupListener groupListener = new InternalGroupListener();
+ private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
+
+ private ApplicationId appId;
+ private NodeId localNode;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.getAppId(APP_ID);
+ localNode = clusterService.getLocalNode().id();
+
+ componentConfigService.registerProperties(getClass());
+ leadershipService.runForLeadership(appId.name());
+ groupService.addListener(groupListener);
+ deviceService.addListener(ovsdbListener);
+ deviceService.addListener(bridgeListener);
+ osNodeService.addListener(osNodeListener);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osNodeService.removeListener(osNodeListener);
+ deviceService.removeListener(bridgeListener);
+ deviceService.removeListener(ovsdbListener);
+ groupService.removeListener(groupListener);
+ componentConfigService.unregisterProperties(getClass(), false);
+ leadershipService.withdraw(appId.name());
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ protected void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
+ if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
+ ovsdbPort = updatedOvsdbPort;
+ }
+
+ log.info("Modified");
+ }
+
+ @Override
+ public void processInitState(OpenstackNode osNode) {
+ if (!isOvsdbConnected(osNode)) {
+ ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
+ return;
+ }
+ if (!deviceService.isAvailable(osNode.intgBridge())) {
+ createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
+ }
+ if (osNode.type() == GATEWAY &&
+ !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
+ createBridge(osNode, ROUTER_BRIDGE, osNode.routerBridge());
+ }
+ }
+
+ @Override
+ public void processDeviceCreatedState(OpenstackNode osNode) {
+ if (!isOvsdbConnected(osNode)) {
+ ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
+ return;
+ }
+ if (osNode.type() == GATEWAY && (
+ !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
+ !isIntfCreated(osNode, PATCH_ROUT_BRIDGE)
+ )) {
+ createPatchInterface(osNode);
+ }
+ if (osNode.dataIp() != null &&
+ !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
+ createTunnelInterface(osNode);
+ }
+ if (osNode.vlanIntf() != null &&
+ !isIntfEnabled(osNode, osNode.vlanIntf())) {
+ addSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.vlanIntf());
+ }
+ }
+
+ @Override
+ public void processPortCreatedState(OpenstackNode osNode) {
+ switch (osNode.type()) {
+ case COMPUTE:
+ if (osNode.dataIp() != null) {
+ addOrUpdateGatewayGroup(osNode,
+ osNodeService.completeNodes(GATEWAY),
+ VXLAN);
+ }
+ if (osNode.vlanIntf() != null) {
+ addOrUpdateGatewayGroup(osNode,
+ osNodeService.completeNodes(GATEWAY),
+ VLAN);
+ }
+ break;
+ case GATEWAY:
+ Set<OpenstackNode> gateways =
+ Sets.newHashSet(osNodeService.completeNodes(GATEWAY));
+ gateways.add(osNode);
+ osNodeService.completeNodes(COMPUTE).forEach(n -> {
+ if (n.dataIp() != null) {
+ addOrUpdateGatewayGroup(n, gateways, VXLAN);
+ }
+ if (n.vlanIntf() != null) {
+ addOrUpdateGatewayGroup(n, gateways, VLAN);
+ }
+ });
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void processCompleteState(OpenstackNode osNode) {
+ OvsdbClientService ovsdbClient = ovsdbController.getOvsdbClient(
+ new OvsdbNodeId(osNode.managementIp(), DEFAULT_OVSDB_PORT));
+ if (ovsdbClient != null && ovsdbClient.isConnected()) {
+ ovsdbClient.disconnect();
+ }
+ }
+
+ @Override
+ public void processIncompleteState(OpenstackNode osNode) {
+ if (osNode.type() == COMPUTE) {
+ if (osNode.dataIp() != null) {
+ groupService.removeGroup(osNode.intgBridge(), osNode.gatewayGroupKey(VXLAN), appId);
+ }
+ if (osNode.vlanIntf() != null) {
+ groupService.removeGroup(osNode.intgBridge(), osNode.gatewayGroupKey(VLAN), appId);
+ }
+ }
+ if (osNode.type() == GATEWAY) {
+ osNodeService.completeNodes(COMPUTE).forEach(n -> {
+ if (n.dataIp() != null) {
+ addOrUpdateGatewayGroup(n,
+ osNodeService.completeNodes(GATEWAY),
+ VXLAN);
+ }
+ if (n.vlanIntf() != null) {
+ addOrUpdateGatewayGroup(n,
+ osNodeService.completeNodes(GATEWAY),
+ VLAN);
+ }
+ });
+ }
+ }
+
+ private boolean isOvsdbConnected(OpenstackNode osNode) {
+ OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
+ OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
+ return deviceService.isAvailable(osNode.ovsdb()) &&
+ client != null &&
+ client.isConnected();
+ }
+
+ private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
+ Device device = deviceService.getDevice(osNode.ovsdb());
+ if (device == null || !device.is(BridgeConfig.class)) {
+ log.error("Failed to create integration bridge on {}", osNode.ovsdb());
+ return;
+ }
+
+ // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
+ Set<IpAddress> controllerIps;
+ if (bridgeName.equals(ROUTER_BRIDGE)) {
+ // TODO checks if empty controller does not break anything
+ controllerIps = ImmutableSet.of();
+ } else {
+ controllerIps = clusterService.getNodes().stream()
+ .map(ControllerNode::ip)
+ .collect(Collectors.toSet());
+ }
+
+ List<ControllerInfo> controllers = controllerIps.stream()
+ .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
+ .collect(Collectors.toList());
+
+ String dpid = deviceId.toString().substring(DPID_BEGIN);
+ BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
+ .name(bridgeName)
+ .failMode(BridgeDescription.FailMode.SECURE)
+ .datapathId(dpid)
+ .disableInBand()
+ .controllers(controllers)
+ .build();
+
+ BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+ bridgeConfig.addBridge(bridgeDesc);
+ }
+
+ private void addSystemInterface(OpenstackNode osNode, String bridgeName, String intfName) {
+ Device device = deviceService.getDevice(osNode.ovsdb());
+ if (device == null || !device.is(BridgeConfig.class)) {
+ return;
+ }
+ BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+ bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
+ }
+
+ private void createTunnelInterface(OpenstackNode osNode) {
+ if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
+ return;
+ }
+
+ Device device = deviceService.getDevice(osNode.ovsdb());
+ if (device == null || !device.is(InterfaceConfig.class)) {
+ log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
+ return;
+ }
+
+ TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
+ .deviceId(INTEGRATION_BRIDGE)
+ .ifaceName(DEFAULT_TUNNEL)
+ .type(TunnelDescription.Type.VXLAN)
+ .remote(TunnelEndPoints.flowTunnelEndpoint())
+ .key(TunnelKeys.flowTunnelKey())
+ .build();
+
+ InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+ ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
+ }
+
+ private void createPatchInterface(OpenstackNode osNode) {
+ checkArgument(osNode.type().equals(OpenstackNode.NodeType.GATEWAY));
+ if (isIntfEnabled(osNode, PATCH_INTG_BRIDGE) &&
+ isIntfCreated(osNode, PATCH_ROUT_BRIDGE)) {
+ return;
+ }
+
+ Device device = deviceService.getDevice(osNode.ovsdb());
+ if (device == null || !device.is(InterfaceConfig.class)) {
+ log.error("Failed to create patch interfaces on {}", osNode.hostname());
+ return;
+ }
+
+ PatchDescription patchIntg = DefaultPatchDescription.builder()
+ .deviceId(INTEGRATION_BRIDGE)
+ .ifaceName(PATCH_INTG_BRIDGE)
+ .peer(PATCH_ROUT_BRIDGE)
+ .build();
+
+ PatchDescription patchRout = DefaultPatchDescription.builder()
+ .deviceId(ROUTER_BRIDGE)
+ .ifaceName(PATCH_ROUT_BRIDGE)
+ .peer(PATCH_INTG_BRIDGE)
+ .build();
+
+ InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+ ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
+ ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
+ }
+
+ private void addOrUpdateGatewayGroup(OpenstackNode osNode,
+ Set<OpenstackNode> gatewayNodes,
+ NetworkMode mode) {
+ GroupBuckets buckets = gatewayGroupBuckets(osNode, gatewayNodes, mode);
+ if (groupService.getGroup(osNode.intgBridge(), osNode.gatewayGroupKey(mode)) == null) {
+ GroupDescription groupDescription = new DefaultGroupDescription(
+ osNode.intgBridge(),
+ GroupDescription.Type.SELECT,
+ buckets,
+ osNode.gatewayGroupKey(mode),
+ osNode.gatewayGroupId(mode).id(),
+ appId);
+ groupService.addGroup(groupDescription);
+ log.debug("Created gateway group for {}", osNode.hostname());
+ } else {
+ groupService.setBucketsForGroup(
+ osNode.intgBridge(),
+ osNode.gatewayGroupKey(mode),
+ buckets,
+ osNode.gatewayGroupKey(mode),
+ appId);
+ log.debug("Updated gateway group for {}", osNode.hostname());
+ }
+ }
+
+ private GroupBuckets gatewayGroupBuckets(OpenstackNode osNode,
+ Set<OpenstackNode> gatewayNodes,
+ NetworkMode mode) {
+ List<GroupBucket> bucketList = Lists.newArrayList();
+ switch (mode) {
+ case VXLAN:
+ gatewayNodes.stream().filter(n -> n.dataIp() != null).forEach(n -> {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .extension(tunnelDstTreatment(osNode.intgBridge(),
+ n.dataIp()),
+ osNode.intgBridge())
+ .setOutput(osNode.tunnelPortNum())
+ .build();
+ bucketList.add(createSelectGroupBucket(treatment));
+ });
+ return new GroupBuckets(bucketList);
+ case VLAN:
+ gatewayNodes.stream().filter(n -> n.vlanIntf() != null).forEach(n -> {
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(n.vlanPortMac())
+ .setOutput(osNode.vlanPortNum())
+ .build();
+ bucketList.add(createSelectGroupBucket(treatment));
+ });
+ return new GroupBuckets(bucketList);
+ default:
+ return null;
+ }
+ }
+
+ private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
+ Device device = deviceService.getDevice(deviceId);
+ if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
+ log.error("The extension treatment is not supported");
+ return null;
+ }
+
+ ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+ ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+ try {
+ treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
+ return treatment;
+ } catch (ExtensionPropertyException e) {
+ log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
+ return null;
+ }
+ }
+
+ private boolean isBridgeCreated(DeviceId ovsdbId, String bridgeName) {
+ Device device = deviceService.getDevice(ovsdbId);
+ if (device == null || !deviceService.isAvailable(device.id()) ||
+ !device.is(BridgeConfig.class)) {
+ return false;
+ }
+ BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+ return bridgeConfig.getBridges().stream()
+ .anyMatch(bridge -> bridge.name().equals(bridgeName));
+ }
+
+ private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
+ if (!deviceService.isAvailable(osNode.intgBridge())) {
+ return false;
+ }
+ return deviceService.getPorts(osNode.intgBridge()).stream()
+ .anyMatch(port -> Objects.equals(
+ port.annotations().value(PORT_NAME), intf) &&
+ port.isEnabled());
+ }
+
+ private boolean isIntfCreated(OpenstackNode osNode, String intf) {
+ Device device = deviceService.getDevice(osNode.ovsdb());
+ if (device == null || !deviceService.isAvailable(osNode.ovsdb()) ||
+ !device.is(BridgeConfig.class)) {
+ return false;
+ }
+
+ BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+ return bridgeConfig.getPorts().stream()
+ .anyMatch(port -> port.annotations().value(PORT_NAME).equals(intf));
+ }
+
+ private boolean isGroupCreated(OpenstackNode osNode) {
+ for (OpenstackNode gNode : osNodeService.completeNodes(GATEWAY)) {
+ if (!isGatewayBucketAdded(osNode, gNode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isGatewayBucketAdded(OpenstackNode cNode, OpenstackNode gNode) {
+ if (cNode.dataIp() != null) {
+ Group osGroup = groupService.getGroup(cNode.intgBridge(),
+ cNode.gatewayGroupKey(VXLAN));
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .extension(tunnelDstTreatment(gNode.intgBridge(),
+ gNode.dataIp()),
+ cNode.intgBridge())
+ .setOutput(cNode.tunnelPortNum())
+ .build();
+ GroupBucket bucket = createSelectGroupBucket(treatment);
+ if (osGroup == null || osGroup.state() != Group.GroupState.ADDED ||
+ !osGroup.buckets().buckets().contains(bucket)) {
+ return false;
+ }
+ }
+ if (cNode.vlanIntf() != null) {
+ Group osGroup = groupService.getGroup(cNode.intgBridge(),
+ cNode.gatewayGroupKey(VLAN));
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setEthDst(gNode.vlanPortMac())
+ .setOutput(cNode.vlanPortNum())
+ .build();
+ GroupBucket bucket = createSelectGroupBucket(treatment);
+ if (osGroup == null || osGroup.state() != Group.GroupState.ADDED ||
+ !osGroup.buckets().buckets().contains(bucket)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isCurrentStateDone(OpenstackNode osNode) {
+ switch (osNode.state()) {
+ case INIT:
+ if (!deviceService.isAvailable(osNode.intgBridge())) {
+ return false;
+ }
+ if (osNode.type() == GATEWAY &&
+ !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
+ return false;
+ }
+ return true;
+ case DEVICE_CREATED:
+ if (osNode.dataIp() != null &&
+ !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
+ return false;
+ }
+ if (osNode.vlanIntf() != null &&
+ !isIntfEnabled(osNode, osNode.vlanIntf())) {
+ return false;
+ }
+ if (osNode.type() == GATEWAY && (
+ !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
+ !isIntfCreated(osNode, PATCH_ROUT_BRIDGE))) {
+ return false;
+ }
+ return true;
+ case PORT_CREATED:
+ if (osNode.type() == COMPUTE) {
+ return isGroupCreated(osNode);
+ } else {
+ for (OpenstackNode cNode : osNodeService.completeNodes(COMPUTE)) {
+ if (!isGatewayBucketAdded(cNode, osNode)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ case COMPLETE:
+ return false;
+ case INCOMPLETE:
+ // always return false
+ // run init CLI to re-trigger node bootstrap
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ private void setState(OpenstackNode osNode, NodeState newState) {
+ if (osNode.state() == newState) {
+ return;
+ }
+ OpenstackNode updated = osNode.updateState(newState);
+ osNodeAdminService.updateNode(updated);
+ log.info("Changed {} state: {}", osNode.hostname(), newState);
+ }
+
+ private void bootstrapNode(OpenstackNode osNode) {
+ if (isCurrentStateDone(osNode)) {
+ setState(osNode, osNode.state().nextState());
+ } else {
+ log.trace("Processing {} state for {}", osNode.state(), osNode.hostname());
+ osNode.state().process(this, osNode);
+ }
+ }
+
+ private class InternalOvsdbListener implements DeviceListener {
+
+ @Override
+ public boolean isRelevant(DeviceEvent event) {
+ NodeId leader = leadershipService.getLeader(appId.name());
+ return Objects.equals(localNode, leader) &&
+ event.subject().type() == Device.Type.CONTROLLER &&
+ osNodeService.node(event.subject().id()) != null;
+ }
+
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ OpenstackNode osNode = osNodeService.node(device.id());
+
+ switch (event.type()) {
+ case DEVICE_AVAILABILITY_CHANGED:
+ case DEVICE_ADDED:
+ eventExecutor.execute(() -> {
+ if (deviceService.isAvailable(device.id())) {
+ log.debug("OVSDB {} detected", device.id());
+ bootstrapNode(osNode);
+ } else if (osNode.state() == COMPLETE) {
+ log.debug("Removing OVSDB {}", device.id());
+ deviceAdminService.removeDevice(device.id());
+ }
+ });
+ break;
+ case PORT_ADDED:
+ case PORT_REMOVED:
+ case DEVICE_REMOVED:
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ private class InternalBridgeListener implements DeviceListener {
+
+ @Override
+ public boolean isRelevant(DeviceEvent event) {
+ NodeId leader = leadershipService.getLeader(appId.name());
+ return Objects.equals(localNode, leader) &&
+ event.subject().type() == Device.Type.SWITCH &&
+ osNodeService.node(event.subject().id()) != null;
+ }
+
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ OpenstackNode osNode = osNodeService.node(device.id());
+
+ switch (event.type()) {
+ case DEVICE_AVAILABILITY_CHANGED:
+ case DEVICE_ADDED:
+ eventExecutor.execute(() -> {
+ if (deviceService.isAvailable(device.id())) {
+ log.debug("Integration bridge created on {}", osNode.hostname());
+ bootstrapNode(osNode);
+ } else if (osNode.state() == COMPLETE) {
+ log.warn("Device {} disconnected", device.id());
+ setState(osNode, INCOMPLETE);
+ }
+ });
+ break;
+ case PORT_ADDED:
+ eventExecutor.execute(() -> {
+ Port port = event.port();
+ String portName = port.annotations().value(PORT_NAME);
+ if (osNode.state() == DEVICE_CREATED && (
+ Objects.equals(portName, DEFAULT_TUNNEL) ||
+ Objects.equals(portName, osNode.vlanIntf()) ||
+ Objects.equals(portName, PATCH_INTG_BRIDGE) ||
+ Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
+ // FIXME we never gets PATCH_ROUTE_BRIDGE port added events as of now
+ log.debug("Interface {} added to {}", portName, event.subject().id());
+ bootstrapNode(osNode);
+ }
+ });
+ break;
+ case PORT_REMOVED:
+ eventExecutor.execute(() -> {
+ Port port = event.port();
+ String portName = port.annotations().value(PORT_NAME);
+ if (osNode.state() == COMPLETE && (
+ Objects.equals(portName, DEFAULT_TUNNEL) ||
+ Objects.equals(portName, osNode.vlanIntf()) ||
+ Objects.equals(portName, PATCH_INTG_BRIDGE) ||
+ Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
+ log.warn("Interface {} removed from {}", portName, event.subject().id());
+ setState(osNode, INCOMPLETE);
+ }
+ });
+ break;
+ case PORT_UPDATED:
+ case DEVICE_REMOVED:
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ private class InternalGroupListener implements GroupListener {
+
+ @Override
+ public boolean isRelevant(GroupEvent event) {
+ NodeId leader = leadershipService.getLeader(appId.name());
+ return Objects.equals(localNode, leader);
+ }
+
+ @Override
+ public void event(GroupEvent event) {
+ switch (event.type()) {
+ case GROUP_ADDED:
+ log.trace("Group added, ID:{} state:{}", event.subject().id(),
+ event.subject().state());
+ eventExecutor.execute(() -> {
+ OpenstackNode osNode = osNodeByGroupId(event.subject().id());
+ if (osNode != null && osNode.state() == PORT_CREATED) {
+ setState(osNode, COMPLETE);
+ }
+ });
+ break;
+ case GROUP_UPDATED:
+ log.trace("Group updated, ID:{} state:{}", event.subject().id(),
+ event.subject().state());
+ eventExecutor.execute(() -> {
+ osNodeService.nodes(GATEWAY).stream()
+ .filter(osNode -> osNode.state() == PORT_CREATED)
+ .forEach(osNode -> bootstrapNode(osNode));
+ });
+ break;
+ case GROUP_REMOVED:
+ // TODO handle group removed
+ break;
+ default:
+ break;
+ }
+ }
+
+ private OpenstackNode osNodeByGroupId(GroupId groupId) {
+ return osNodeService.nodes().stream()
+ .filter(n -> n.gatewayGroupId(VXLAN).equals(groupId) ||
+ n.gatewayGroupId(VLAN).equals(groupId))
+ .findAny().orElse(null);
+ }
+ }
+
+ private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+ @Override
+ public boolean isRelevant(OpenstackNodeEvent event) {
+ NodeId leader = leadershipService.getLeader(appId.name());
+ return Objects.equals(localNode, leader);
+ }
+
+ @Override
+ public void event(OpenstackNodeEvent event) {
+ switch (event.type()) {
+ case OPENSTACK_NODE_CREATED:
+ case OPENSTACK_NODE_UPDATED:
+ eventExecutor.execute(() -> {
+ bootstrapNode(event.subject());
+ });
+ break;
+ case OPENSTACK_NODE_COMPLETE:
+ break;
+ case OPENSTACK_NODE_REMOVED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
new file mode 100644
index 0000000..f248367
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2017-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.openstacknode.impl;
+
+import com.google.common.collect.ImmutableSet;
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeStore;
+import org.onosproject.openstacknode.api.OpenstackNodeStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
+import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
+import static org.onosproject.openstacknode.api.OpenstackNodeEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of openstack node store using consistent map.
+ */
+@Service
+@Component(immediate = true)
+public class DistributedOpenstackNodeStore
+ extends AbstractStore<OpenstackNodeEvent, OpenstackNodeStoreDelegate>
+ implements OpenstackNodeStore {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String ERR_NOT_FOUND = " does not exist";
+ private static final String ERR_DUPLICATE = " already exists";
+
+ private static final KryoNamespace SERIALIZER_OPENSTACK_NODE = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(OpenstackNode.class)
+ .register(DefaultOpenstackNode.class)
+ .register(OpenstackNode.NodeType.class)
+ .register(NodeState.class)
+ .build();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+ private final MapEventListener<String, OpenstackNode> osNodeMapListener =
+ new OpenstackNodeMapListener();
+ private ConsistentMap<String, OpenstackNode> osNodeStore;
+
+ @Activate
+ protected void activate() {
+ ApplicationId appId = coreService.registerApplication("org.onosproject.openstacknode");
+ osNodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_OPENSTACK_NODE))
+ .withName("openstack-nodestore")
+ .withApplicationId(appId)
+ .build();
+ osNodeStore.addListener(osNodeMapListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osNodeStore.removeListener(osNodeMapListener);
+ eventExecutor.shutdown();
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createNode(OpenstackNode osNode) {
+ osNodeStore.compute(osNode.hostname(), (hostname, existing) -> {
+ final String error = osNode.hostname() + ERR_DUPLICATE;
+ checkArgument(existing == null, error);
+ return osNode;
+ });
+ }
+
+ @Override
+ public void updateNode(OpenstackNode osNode) {
+ osNodeStore.compute(osNode.hostname(), (hostname, existing) -> {
+ final String error = osNode.hostname() + ERR_NOT_FOUND;
+ checkArgument(existing != null, error);
+ return osNode;
+ });
+ }
+
+ @Override
+ public OpenstackNode removeNode(String hostname) {
+ Versioned<OpenstackNode> osNode = osNodeStore.remove(hostname);
+ if (osNode == null) {
+ final String error = hostname + ERR_NOT_FOUND;
+ throw new IllegalArgumentException(error);
+ }
+ return osNode.value();
+ }
+
+ @Override
+ public Set<OpenstackNode> nodes() {
+ Set<OpenstackNode> osNodes = osNodeStore.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osNodes);
+ }
+
+ @Override
+ public OpenstackNode node(String hostname) {
+ Versioned<OpenstackNode> osNode = osNodeStore.get(hostname);
+ return osNode == null ? null : osNode.value();
+ }
+
+ private class OpenstackNodeMapListener implements MapEventListener<String, OpenstackNode> {
+
+ @Override
+ public void event(MapEvent<String, OpenstackNode> event) {
+ switch (event.type()) {
+ case INSERT:
+ log.debug("OpenStack node created {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNodeEvent(
+ OPENSTACK_NODE_CREATED,
+ event.newValue().value()
+ ));
+ });
+ break;
+ case UPDATE:
+ log.debug("OpenStack node updated {}", event.newValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNodeEvent(
+ OPENSTACK_NODE_UPDATED,
+ event.newValue().value()
+ ));
+ if (event.newValue().value().state() == COMPLETE) {
+ notifyDelegate(new OpenstackNodeEvent(
+ OPENSTACK_NODE_COMPLETE,
+ event.newValue().value()
+ ));
+ } else if (event.newValue().value().state() == INCOMPLETE) {
+ notifyDelegate(new OpenstackNodeEvent(
+ OPENSTACK_NODE_INCOMPLETE,
+ event.newValue().value()
+ ));
+ }
+ });
+ break;
+ case REMOVE:
+ log.debug("OpenStack node removed {}", event.oldValue());
+ eventExecutor.execute(() -> {
+ notifyDelegate(new OpenstackNodeEvent(
+ OPENSTACK_NODE_REMOVED,
+ event.oldValue().value()
+ ));
+ });
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
new file mode 100644
index 0000000..65d565a
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2017-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.openstacknode.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.DeviceId;
+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.basics.SubjectFactories;
+import org.onosproject.openstacknode.api.OpenstackNodeConfig;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNodeStore;
+import org.onosproject.openstacknode.api.OpenstackNodeStoreDelegate;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Service administering the inventory of openstack nodes.
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
+ implements OpenstackNodeService, OpenstackNodeAdminService {
+
+ protected final Logger log = getLogger(getClass());
+
+ private static final String MSG_NODE = "OpenStack node %s %s";
+ private static final String MSG_CREATED = "created";
+ private static final String MSG_UPDATED = "updated";
+ private static final String MSG_REMOVED = "removed";
+
+ private static final String ERR_NULL_NODE = "OpenStack node cannot be null";
+ private static final String ERR_NULL_HOSTNAME = "OpenStack node hostname cannot be null";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeStore osNodeStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry configRegistry;
+
+ private final ExecutorService eventExecutor = newSingleThreadExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+ private final ConfigFactory configFactory =
+ new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
+ SubjectFactories.APP_SUBJECT_FACTORY, OpenstackNodeConfig.class, "openstacknode") {
+ @Override
+ public OpenstackNodeConfig createConfig() {
+ return new OpenstackNodeConfig();
+ }
+ };
+
+ private final NetworkConfigListener configListener = new InternalConfigListener();
+ private final OpenstackNodeStoreDelegate delegate = new InternalNodeStoreDelegate();
+
+ private ApplicationId appId;
+ private NodeId localNode;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(APP_ID);
+ osNodeStore.setDelegate(delegate);
+
+ localNode = clusterService.getLocalNode().id();
+ leadershipService.runForLeadership(appId.name());
+
+ configRegistry.registerConfigFactory(configFactory);
+ configRegistry.addListener(configListener);
+
+ readConfiguration();
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ osNodeStore.unsetDelegate(delegate);
+ configRegistry.removeListener(configListener);
+ configRegistry.unregisterConfigFactory(configFactory);
+
+ leadershipService.withdraw(appId.name());
+ eventExecutor.shutdown();
+
+ log.info("Stopped");
+ }
+
+ @Override
+ public void createNode(OpenstackNode osNode) {
+ checkNotNull(osNode, ERR_NULL_NODE);
+ osNodeStore.createNode(osNode);
+ log.info(String.format(MSG_NODE, osNode.hostname(), MSG_CREATED));
+ }
+
+ @Override
+ public void updateNode(OpenstackNode osNode) {
+ checkNotNull(osNode, ERR_NULL_NODE);
+ osNodeStore.updateNode(osNode);
+ log.info(String.format(MSG_NODE, osNode.hostname(), MSG_UPDATED));
+ }
+
+ @Override
+ public OpenstackNode removeNode(String hostname) {
+ checkArgument(!Strings.isNullOrEmpty(hostname), ERR_NULL_HOSTNAME);
+ OpenstackNode osNode = osNodeStore.removeNode(hostname);
+ log.info(String.format(MSG_NODE, hostname, MSG_REMOVED));
+ return osNode;
+ }
+
+ @Override
+ public Set<OpenstackNode> nodes() {
+ return osNodeStore.nodes();
+ }
+
+ @Override
+ public Set<OpenstackNode> nodes(OpenstackNode.NodeType type) {
+ Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
+ .filter(osNode -> Objects.equals(osNode.type(), type))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osNodes);
+ }
+
+ @Override
+ public Set<OpenstackNode> completeNodes() {
+ Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
+ .filter(osNode -> Objects.equals(osNode.state(), COMPLETE))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osNodes);
+ }
+
+ @Override
+ public Set<OpenstackNode> completeNodes(OpenstackNode.NodeType type) {
+ Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
+ .filter(osNode -> osNode.type() == type &&
+ Objects.equals(osNode.state(), COMPLETE))
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(osNodes);
+ }
+
+ @Override
+ public OpenstackNode node(String hostname) {
+ return osNodeStore.node(hostname);
+ }
+
+ @Override
+ public OpenstackNode node(DeviceId deviceId) {
+ OpenstackNode result = osNodeStore.nodes().stream()
+ .filter(osNode -> Objects.equals(osNode.intgBridge(), deviceId) ||
+ Objects.equals(osNode.ovsdb(), deviceId) ||
+ Objects.equals(osNode.routerBridge(), deviceId))
+ .findFirst().orElse(null);
+ return result;
+ }
+
+ private class InternalNodeStoreDelegate implements OpenstackNodeStoreDelegate {
+
+ @Override
+ public void notify(OpenstackNodeEvent event) {
+ if (event != null) {
+ log.trace("send openstack node event {}", event);
+ process(event);
+ }
+ }
+ }
+
+ private void readConfiguration() {
+ OpenstackNodeConfig config = configRegistry.getConfig(appId, OpenstackNodeConfig.class);
+ if (config == null) {
+ log.debug("No configuration found");
+ return;
+ }
+
+ log.info("Read openstack node configurations...");
+ Set<String> hostnames = config.openstackNodes().stream()
+ .map(OpenstackNode::hostname)
+ .collect(Collectors.toSet());
+ nodes().stream().filter(osNode -> !hostnames.contains(osNode.hostname()))
+ .forEach(osNode -> removeNode(osNode.hostname()));
+
+ config.openstackNodes().forEach(osNode -> {
+ OpenstackNode existing = node(osNode.hostname());
+ if (existing == null) {
+ createNode(osNode);
+ } else if (!existing.equals(osNode)) {
+ updateNode(osNode);
+ }
+ });
+ }
+
+ private class InternalConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ NodeId leaderNodeId = leadershipService.getLeader(appId.name());
+ if (!Objects.equals(localNode, leaderNodeId)) {
+ // do not allow to proceed without leadership
+ return;
+ }
+
+ if (!event.configClass().equals(OpenstackNodeConfig.class)) {
+ return;
+ }
+
+ switch (event.type()) {
+ case CONFIG_ADDED:
+ case CONFIG_UPDATED:
+ eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/package-info.java
similarity index 86%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/package-info.java
index c280f54..4982c44 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-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.
@@ -17,4 +17,4 @@
/**
* Application for bootstrapping Compute/Gateway Node in OpenStack.
*/
-package org.onosproject.openstacknode;
\ No newline at end of file
+package org.onosproject.openstacknode.impl;
\ No newline at end of file