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/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;
+        }
+    }
+}
+