Adds ssh authentication information in OpenstackNode class.

Change-Id: I46552bc580d1ac82d4236b03589c005622170ffe
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java
index fd8d3b6..78e261f 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/DefaultOpenstackNode.java
@@ -57,6 +57,7 @@
     private final Collection<ControllerInfo> controllers;
     private final OpenstackAuth auth;
     private final String endPoint;
+    private final OpenstackSshAuth sshAuth;
 
     private static final String NOT_NULL_MSG = "Node % cannot be null";
 
@@ -77,6 +78,7 @@
      * @param controllers   customized controllers
      * @param auth          keystone authentication info
      * @param endPoint      openstack endpoint URL
+     * @param sshAuth       ssh authentication info
      */
     protected DefaultOpenstackNode(String hostname, NodeType type,
                                    DeviceId intgBridge,
@@ -88,7 +90,8 @@
                                    Collection<OpenstackPhyInterface> phyIntfs,
                                    Collection<ControllerInfo> controllers,
                                    OpenstackAuth auth,
-                                   String endPoint) {
+                                   String endPoint,
+                                   OpenstackSshAuth sshAuth) {
         this.hostname = hostname;
         this.type = type;
         this.intgBridge = intgBridge;
@@ -101,6 +104,7 @@
         this.controllers = controllers;
         this.auth = auth;
         this.endPoint = endPoint;
+        this.sshAuth = sshAuth;
     }
 
     @Override
@@ -242,7 +246,8 @@
                     Objects.equals(phyIntfs, that.phyIntfs) &&
                     Objects.equals(controllers, that.controllers) &&
                     Objects.equals(auth, that.auth) &&
-                    Objects.equals(endPoint, that.endPoint);
+                    Objects.equals(endPoint, that.endPoint) &&
+                    Objects.equals(sshAuth, that.sshAuth);
         }
         return false;
     }
@@ -259,7 +264,8 @@
                 phyIntfs,
                 controllers,
                 auth,
-                endPoint);
+                endPoint,
+                sshAuth);
     }
 
     @Override
@@ -277,6 +283,7 @@
                 .add("controllers", controllers)
                 .add("auth", auth)
                 .add("endpoint", endPoint)
+                .add("sshAuth", sshAuth)
                 .toString();
     }
 
@@ -295,6 +302,7 @@
                 .controllers(controllers)
                 .authentication(auth)
                 .endPoint(endPoint)
+                .sshAuthInfo(sshAuth)
                 .build();
     }
 
@@ -319,6 +327,11 @@
     }
 
     @Override
+    public OpenstackSshAuth sshAuthInfo() {
+        return sshAuth;
+    }
+
+    @Override
     public PortNumber phyIntfPortNum(String providerPhysnet) {
         Optional<OpenstackPhyInterface> openstackPhyInterface =
                 phyIntfs.stream().filter(p -> p.network().equals(providerPhysnet)).findAny();
@@ -375,7 +388,8 @@
                 .phyIntfs(osNode.phyIntfs())
                 .controllers(osNode.controllers())
                 .authentication(osNode.authentication())
-                .endPoint(osNode.endPoint());
+                .endPoint(osNode.endPoint())
+                .sshAuthInfo(osNode.sshAuthInfo());
     }
 
     /**
@@ -395,6 +409,7 @@
         private Collection<ControllerInfo> controllers;
         private OpenstackAuth auth;
         private String endPoint;
+        private OpenstackSshAuth sshAuth;
 
         // private constructor not intended to use from external
         private Builder() {
@@ -432,7 +447,8 @@
                     phyIntfs,
                     controllers,
                     auth,
-                    endPoint);
+                    endPoint,
+                    sshAuth);
         }
 
         @Override
@@ -508,6 +524,12 @@
             this.endPoint = endPoint;
             return this;
         }
+
+        @Override
+        public Builder sshAuthInfo(OpenstackSshAuth sshAuth) {
+            this.sshAuth = sshAuth;
+            return this;
+        }
     }
 }
 
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
index 35e84fe..5789deb 100644
--- a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
@@ -208,6 +208,13 @@
     Collection<ControllerInfo> controllers();
 
     /**
+     * Returns the ssh authentication info.
+     *
+     * @return ssh authentication info
+     */
+    OpenstackSshAuth sshAuthInfo();
+
+    /**
      * Builder of new node entities.
      */
     interface Builder {
@@ -314,6 +321,14 @@
          * @return openstack node builder
          */
         Builder endPoint(String endPoint);
+
+        /**
+         * Returns openstack node builder with supplied ssh authentication info.
+         *
+         * @param sshAuth ssh authentication info
+         * @return openstack node builder
+         */
+        Builder sshAuthInfo(OpenstackSshAuth sshAuth);
     }
 }
 
diff --git a/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackSshAuth.java b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackSshAuth.java
new file mode 100644
index 0000000..46e9f77
--- /dev/null
+++ b/apps/openstacknode/api/src/main/java/org/onosproject/openstacknode/api/OpenstackSshAuth.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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;
+
+/**
+ * Representation of ssh authentication information for node.
+ */
+public interface OpenstackSshAuth {
+    /**
+     * Returns the ID for ssh authentication.
+     *
+     * @return id
+     */
+    String id();
+
+    /**
+     * Returns the password for ssh authentication.
+     *
+     * @return password
+     */
+    String password();
+
+    /**
+     * Builder of OpenstackSshAuth instance.
+     */
+    interface Builder {
+        /**
+         * Builds an immutable OpenstackSshAuth instance.
+         *
+         * @return OpenstackSsshAuth instance
+         */
+        OpenstackSshAuth build();
+
+        /**
+         * Returns OpenstackSshAuth builder with supplied ID.
+         *
+         * @param id id
+         * @return OpenstackSsshAuth builder
+         */
+        Builder id(String id);
+
+        /**
+         * Returns OpenstackSshAuth builder with supplied password.
+         *
+         * @param password password
+         * @return OpenstackSsshAuth builder
+         */
+        Builder password(String password);
+    }
+}
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java
index 282560d..a94aa10 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackNodeCodec.java
@@ -28,6 +28,7 @@
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackPhyInterface;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
 import org.slf4j.Logger;
 
 import java.util.ArrayList;
@@ -59,6 +60,7 @@
     private static final String CONTROLLERS = "controllers";
     private static final String AUTHENTICATION = "authentication";
     private static final String END_POINT = "endPoint";
+    private static final String SSH_AUTH = "sshAuth";
 
     private static final String MISSING_MESSAGE = " is required in OpenstackNode";
 
@@ -111,7 +113,13 @@
         if (node.authentication() != null) {
             ObjectNode authJson = context.codec(OpenstackAuth.class)
                     .encode(node.authentication(), context);
-            result.put(AUTHENTICATION, authJson);
+            result.set(AUTHENTICATION, authJson);
+        }
+
+        if (node.sshAuthInfo() != null) {
+            ObjectNode sshAuthJson = context.codec(OpenstackSshAuth.class)
+                    .encode(node.sshAuthInfo(), context);
+            result.set(SSH_AUTH, sshAuthJson);
         }
 
         return result;
@@ -196,6 +204,15 @@
             nodeBuilder.authentication(auth);
         }
 
+        // parse ssh authentication
+        JsonNode sshAuthJson = json.get(SSH_AUTH);
+        if (json.get(SSH_AUTH) != null) {
+            final JsonCodec<OpenstackSshAuth> sshAuthJsonCodec = context.codec(OpenstackSshAuth.class);
+
+            OpenstackSshAuth sshAuth = sshAuthJsonCodec.decode((ObjectNode) sshAuthJson.deepCopy(), context);
+            nodeBuilder.sshAuthInfo(sshAuth);
+        }
+
         log.trace("node is {}", nodeBuilder.build().toString());
 
         return nodeBuilder.build();
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackSshAuthCodec.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackSshAuthCodec.java
new file mode 100644
index 0000000..4dbe1d6
--- /dev/null
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/codec/OpenstackSshAuthCodec.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.codec;
+
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
+import org.onosproject.openstacknode.impl.DefaultOpenstackSshAuth;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Node ssh authentication info codec used for serializing and
+ * de-serializing JSON string.
+ */
+public class OpenstackSshAuthCodec extends JsonCodec<OpenstackSshAuth> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ID = "id";
+    private static final String PASSWORD = "password";
+
+    private static final String MISSING_MESSAGE = " is required in OpenstackSshAuth";
+    private static final String ERROR_MSG_NOT_NULL = "Openstack SSH auth cannot be null";
+
+    @Override
+    public ObjectNode encode(OpenstackSshAuth sshAuth, CodecContext context) {
+        checkNotNull(sshAuth, ERROR_MSG_NOT_NULL);
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(ID, sshAuth.id())
+                .put(PASSWORD, sshAuth.password());
+
+        return result;
+    }
+
+    @Override
+    public OpenstackSshAuth decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String id = nullIsIllegal(json.get(ID).asText(),
+                ID + MISSING_MESSAGE);
+
+        String password = nullIsIllegal(json.get(PASSWORD).asText(),
+                PASSWORD + MISSING_MESSAGE);
+
+        return DefaultOpenstackSshAuth.builder()
+                .id(id)
+                .password(password)
+                .build();
+    }
+}
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackAuth.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackAuth.java
index 5a3b30d..b073f5f 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackAuth.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackAuth.java
@@ -25,7 +25,7 @@
 /**
  * Implementation class of openstack authentication.
  */
-public class DefaultOpenstackAuth implements OpenstackAuth {
+public final class DefaultOpenstackAuth implements OpenstackAuth {
 
     private final String version;
     private final Integer port;
@@ -48,7 +48,7 @@
      * @param project       project name
      * @param perspective   user perspective
      */
-    protected DefaultOpenstackAuth(String version, Integer port, Protocol protocol,
+    private DefaultOpenstackAuth(String version, Integer port, Protocol protocol,
                                    String username, String password, String project,
                                    Perspective perspective) {
 
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackSshAuth.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackSshAuth.java
new file mode 100644
index 0000000..882e028
--- /dev/null
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackSshAuth.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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 org.onosproject.openstacknode.api.OpenstackSshAuth;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+/**
+ * Implementation class of ssh authentication.
+ */
+public final class DefaultOpenstackSshAuth implements OpenstackSshAuth {
+    private final String id;
+    private final String password;
+
+    private static final String NOT_NULL_MSG = "% cannot be null";
+
+    /**
+     * A default constructor of ssh authentication instance.
+     *
+     * @param id ssh auth ID
+     * @param password ssh auth password
+     */
+    private DefaultOpenstackSshAuth(String id, String password) {
+        this.id = id;
+        this.password = password;
+    }
+
+    @Override
+    public String id() {
+        return id;
+    }
+
+    @Override
+    public String password() {
+        return password;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof DefaultOpenstackSshAuth) {
+            DefaultOpenstackSshAuth that = (DefaultOpenstackSshAuth) obj;
+            return Objects.equals(id, that.id) &&
+                    Objects.equals(password, that.password);
+        }
+
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, password);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id)
+                .add("password", password)
+                .toString();
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return ssh authentication instance builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of OpenstackSshAuth instance.
+     */
+    public static final class Builder implements OpenstackSshAuth.Builder {
+        private String id;
+        private String password;
+
+        private Builder() {
+
+        }
+
+        @Override
+        public OpenstackSshAuth build() {
+            checkArgument(id != null, NOT_NULL_MSG, "id");
+            checkArgument(password != null, NOT_NULL_MSG, "password");
+
+            return new DefaultOpenstackSshAuth(id, password);
+        }
+
+        @Override
+        public Builder id(String id) {
+            this.id = id;
+            return this;
+        }
+
+        @Override
+        public Builder password(String password) {
+            this.password = password;
+            return this;
+        }
+    }
+}
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
index 0383b97..6b77efb 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
@@ -88,6 +88,7 @@
             .register(DefaultOpenstackAuth.class)
             .register(DefaultOpenstackAuth.Perspective.class)
             .register(DefaultOpenstackAuth.Protocol.class)
+            .register(DefaultOpenstackSshAuth.class)
             .register(Collection.class)
             .build();
 
diff --git a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/web/OpenstackNodeCodecRegister.java b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/web/OpenstackNodeCodecRegister.java
index 9002237..3c8954c 100644
--- a/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/web/OpenstackNodeCodecRegister.java
+++ b/apps/openstacknode/app/src/main/java/org/onosproject/openstacknode/web/OpenstackNodeCodecRegister.java
@@ -25,10 +25,12 @@
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackPhyInterface;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
 import org.onosproject.openstacknode.codec.OpenstackAuthCodec;
 import org.onosproject.openstacknode.codec.OpenstackControllerCodec;
 import org.onosproject.openstacknode.codec.OpenstackNodeCodec;
 import org.onosproject.openstacknode.codec.OpenstackPhyInterfaceCodec;
+import org.onosproject.openstacknode.codec.OpenstackSshAuthCodec;
 
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -49,6 +51,7 @@
         codecService.registerCodec(OpenstackAuth.class, new OpenstackAuthCodec());
         codecService.registerCodec(OpenstackPhyInterface.class, new OpenstackPhyInterfaceCodec());
         codecService.registerCodec(ControllerInfo.class, new OpenstackControllerCodec());
+        codecService.registerCodec(OpenstackSshAuth.class, new OpenstackSshAuthCodec());
 
         log.info("Started");
     }
@@ -59,6 +62,7 @@
         codecService.unregisterCodec(OpenstackAuth.class);
         codecService.unregisterCodec(OpenstackPhyInterface.class);
         codecService.unregisterCodec(ControllerInfo.class);
+        codecService.unregisterCodec(OpenstackSshAuth.class);
 
         log.info("Stopped");
     }
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackAuthJsonMatcher.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackAuthJsonMatcher.java
index 055fbe6..2f165f9 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackAuthJsonMatcher.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackAuthJsonMatcher.java
@@ -21,7 +21,7 @@
 import org.onosproject.openstacknode.api.OpenstackAuth;
 
 /**
- * Hamcrest matcher for opensatck auth.
+ * Hamcrest matcher for openstack auth.
  */
 public final class OpenstackAuthJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
 
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java
index 7d504d3..a42bfcd 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeCodecTest.java
@@ -33,9 +33,11 @@
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackPhyInterface;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
 import org.onosproject.openstacknode.impl.DefaultOpenstackAuth;
 import org.onosproject.openstacknode.api.DefaultOpenstackNode;
 import org.onosproject.openstacknode.impl.DefaultOpenstackPhyInterface;
+import org.onosproject.openstacknode.impl.DefaultOpenstackSshAuth;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -60,6 +62,8 @@
     JsonCodec<OpenstackPhyInterface> openstackPhyIntfJsonCodec;
     JsonCodec<ControllerInfo> openstackControllerJsonCodec;
     JsonCodec<OpenstackAuth> openstackAuthJsonCodec;
+    JsonCodec<OpenstackSshAuth> openstackSshAuthJsonCodec;
+
     final CoreService mockCoreService = createMock(CoreService.class);
     private static final String REST_APP_ID = "org.onosproject.rest";
 
@@ -70,11 +74,13 @@
         openstackPhyIntfJsonCodec = new OpenstackPhyInterfaceCodec();
         openstackControllerJsonCodec = new OpenstackControllerCodec();
         openstackAuthJsonCodec = new OpenstackAuthCodec();
+        openstackSshAuthJsonCodec = new OpenstackSshAuthCodec();
 
         assertThat(openstackNodeCodec, notNullValue());
         assertThat(openstackPhyIntfJsonCodec, notNullValue());
         assertThat(openstackControllerJsonCodec, notNullValue());
         assertThat(openstackAuthJsonCodec, notNullValue());
+        assertThat(openstackSshAuthJsonCodec, notNullValue());
 
         expect(mockCoreService.registerApplication(REST_APP_ID))
                 .andReturn(APP_ID).anyTimes();
@@ -97,6 +103,11 @@
                                 .intf("eth4")
                                 .build();
 
+        OpenstackSshAuth sshAuth = DefaultOpenstackSshAuth.builder()
+                .id("sdn")
+                .password("sdn")
+                .build();
+
         ControllerInfo controller1 =
                 new ControllerInfo(IpAddress.valueOf("10.10.10.2"), 6653, "tcp");
         ControllerInfo controller2 =
@@ -112,6 +123,7 @@
                                 .dataIp(IpAddress.valueOf("20.20.20.2"))
                                 .phyIntfs(ImmutableList.of(phyIntf1, phyIntf2))
                                 .controllers(ImmutableList.of(controller1, controller2))
+                                .sshAuthInfo(sshAuth)
                                 .build();
 
         ObjectNode nodeJson = openstackNodeCodec.encode(node, context);
@@ -135,6 +147,8 @@
         assertThat(node.vlanIntf(), is("eth2"));
         assertThat(node.phyIntfs().size(), is(2));
         assertThat(node.controllers().size(), is(2));
+        assertThat(node.sshAuthInfo().id(), is("sdn"));
+        assertThat(node.sshAuthInfo().password(), is("sdn"));
 
         node.phyIntfs().forEach(intf -> {
             if (intf.network().equals("mgmtnetwork")) {
@@ -254,6 +268,9 @@
             if (entityClass == OpenstackAuth.class) {
                 return (JsonCodec<T>) openstackAuthJsonCodec;
             }
+            if (entityClass == OpenstackSshAuth.class) {
+                return (JsonCodec<T>) openstackSshAuthJsonCodec;
+            }
             return manager.getCodec(entityClass);
         }
 
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java
index d715865..e8b6d07 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackNodeJsonMatcher.java
@@ -23,6 +23,7 @@
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackPhyInterface;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
 
 import static org.onosproject.openstacknode.api.Constants.DATA_IP;
 import static org.onosproject.openstacknode.api.Constants.MANAGEMENT_IP;
@@ -40,6 +41,7 @@
     private static final String CONTROLLERS = "controllers";
     private static final String AUTHENTICATION = "authentication";
     private static final String END_POINT = "endPoint";
+    private static final String SSH_AUTH = "sshAuth";
 
     private OpenstackNodeJsonMatcher(OpenstackNode node) {
         this.node = node;
@@ -120,6 +122,17 @@
             }
         }
 
+        // check openstack ssh auth
+        JsonNode jsonSshAuth = jsonNode.get(SSH_AUTH);
+        if (jsonSshAuth != null) {
+            OpenstackSshAuth sshAuth = node.sshAuthInfo();
+            OpenstackSshAuthJsonMatcher sshAuthJsonMatcher =
+                    OpenstackSshAuthJsonMatcher.matchOpenstackSshAuth(sshAuth);
+            if (!sshAuthJsonMatcher.matches(jsonSshAuth)) {
+                return false;
+            }
+        }
+
         // check endpoint URL
         JsonNode jsonEndPoint = jsonNode.get(END_POINT);
         if (jsonEndPoint != null) {
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackSshAuthJsonMatcher.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackSshAuthJsonMatcher.java
new file mode 100644
index 0000000..9108885
--- /dev/null
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/codec/OpenstackSshAuthJsonMatcher.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
+
+/**
+ * Hamcrest matcher for openstack SSH auth.
+ */
+public final class OpenstackSshAuthJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final OpenstackSshAuth sshAuth;
+
+    private static final String ID = "id";
+    private static final String PASSWORD = "password";
+
+    private OpenstackSshAuthJsonMatcher(OpenstackSshAuth sshAuth) {
+        this.sshAuth = sshAuth;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+        // check id
+        String jsonId = jsonNode.get(ID).asText();
+        String id = sshAuth.id();
+        if (!jsonId.equals(id)) {
+            description.appendText("id was " + jsonId);
+            return false;
+        }
+
+        // check password
+        String jsonPassword = jsonNode.get(PASSWORD).asText();
+        String password = sshAuth.id();
+        if (!jsonPassword.equals(password)) {
+            description.appendText("password was " + jsonId);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(sshAuth.toString());
+    }
+
+    /**
+     * Factory to allocate an openstack SSH auth matcher.
+     *
+     * @param sshAuth openstack SSH auth object we are looking for
+     * @return matcher
+     */
+    public static OpenstackSshAuthJsonMatcher matchOpenstackSshAuth(OpenstackSshAuth sshAuth) {
+        return new OpenstackSshAuthJsonMatcher(sshAuth);
+    }
+}
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
index 92c7615..ee5e478 100644
--- a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandlerTest.java
@@ -74,6 +74,7 @@
 import org.onosproject.openstacknode.api.OpenstackAuth;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackPhyInterface;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
 import org.onosproject.ovsdb.controller.OvsdbClientService;
 import org.onosproject.ovsdb.controller.OvsdbController;
 
@@ -422,7 +423,7 @@
                 intgBridge.id(),
                 ipAddr,
                 ipAddr,
-                null, null, state, phyIntfs, controllers, null, null);
+                null, null, state, phyIntfs, controllers, null, null, null);
     }
 
     private static OpenstackNode createGatewayNode(String hostname,
@@ -437,7 +438,8 @@
                 intgBridge.id(),
                 ipAddr,
                 ipAddr,
-                null, uplinkPort, state, null, null, null, null);
+                null, uplinkPort, state,
+                null, null, null, null, null);
     }
 
     private static final class TestDevice extends DefaultDevice {
@@ -498,7 +500,8 @@
                                   Set<OpenstackPhyInterface> phyIntfs,
                                   Set<ControllerInfo> controllers,
                                   OpenstackAuth auth,
-                                  String endPoint) {
+                                  String endPoint,
+                                  OpenstackSshAuth sshAuth) {
             super(hostname,
                     type,
                     intgBridge,
@@ -510,7 +513,8 @@
                     phyIntfs,
                     controllers,
                     auth,
-                    endPoint);
+                    endPoint,
+                    sshAuth);
         }
 
         @Override
diff --git a/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackSshAuthTest.java b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackSshAuthTest.java
new file mode 100644
index 0000000..bdba45c
--- /dev/null
+++ b/apps/openstacknode/app/src/test/java/org/onosproject/openstacknode/impl/DefaultOpenstackSshAuthTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * 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.testing.EqualsTester;
+import org.junit.Test;
+import org.onosproject.openstacknode.api.OpenstackSshAuth;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for DefaultOpenstackSshAuth.
+ */
+public class DefaultOpenstackSshAuthTest {
+
+    private static final String ID_1 = "sdn1";
+    private static final String ID_2 = "sdn2";
+    private static final String PASSWORD_1 = "password1";
+    private static final String PASSWORD_2 = "password2";
+
+    private static final OpenstackSshAuth OS_SSH_AUTH_1 =
+            createOpenstackSshAuth(ID_1, PASSWORD_1);
+    private static final OpenstackSshAuth OS_SSH_AUTH_2 =
+            createOpenstackSshAuth(ID_2, PASSWORD_2);
+    private static final OpenstackSshAuth OS_SSH_AUTH_3 =
+            createOpenstackSshAuth(ID_1, PASSWORD_1);
+
+
+    private static OpenstackSshAuth createOpenstackSshAuth(String id, String password) {
+        return DefaultOpenstackSshAuth.builder()
+                .id(id)
+                .password(password)
+                .build();
+    }
+
+    @Test
+    public void testEquality() {
+        new EqualsTester().addEqualityGroup(OS_SSH_AUTH_1, OS_SSH_AUTH_3)
+                .addEqualityGroup(OS_SSH_AUTH_2)
+                .testEquals();
+    }
+
+    @Test
+    public void testConstruction() {
+        OpenstackSshAuth auth = OS_SSH_AUTH_1;
+
+        assertThat(auth.id(), is("sdn1"));
+        assertThat(auth.password(), is("password1"));
+    }
+}
diff --git a/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackComputeNode.json b/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackComputeNode.json
index c630c43..0c07798 100644
--- a/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackComputeNode.json
+++ b/apps/openstacknode/app/src/test/resources/org/onosproject/openstacknode/codec/OpenstackComputeNode.json
@@ -15,6 +15,10 @@
       "intf": "eth4"
     }
   ],
+  "sshAuth": {
+    "id": "sdn",
+    "password": "sdn"
+  },
   "controllers": [
     {
       "ip": "10.10.10.2",