Add remote logging server

Remote logging enables the user to configure a remote
server to stream out local logs. This feature will be
available on the Event Log page. The user can add a
remote server, edit/change an existing server
configuration and remove/disable the remote server.

Resolves openbmc/phosphor-webui#68

Signed-off-by: Yoshie Muranaka <yoshiemuranaka@gmail.com>
Change-Id: I8284cbdbdaaf85f5c95f237efc72290c66904b40
diff --git a/app/common/services/api-utils.js b/app/common/services/api-utils.js
index 6e46c9c..e796f43 100644
--- a/app/common/services/api-utils.js
+++ b/app/common/services/api-utils.js
@@ -1557,6 +1557,65 @@
           });
           return $q.all(promises);
         },
+        setRemoteLoggingServer: (data) => {
+          const ip = data.hostname;
+          const port = data.port;
+          const setIPRequest = $http({
+            method: 'PUT',
+            url: DataService.getHost() +
+                '/xyz/openbmc_project/logging/config/remote/attr/Address',
+            withCredentials: true,
+            data: {'data': ip}
+          });
+          const setPortRequest = $http({
+            method: 'PUT',
+            url: DataService.getHost() +
+                '/xyz/openbmc_project/logging/config/remote/attr/Port',
+            withCredentials: true,
+            data: {'data': port}
+          });
+          const promises = [setIPRequest, setPortRequest];
+          return $q.all(promises);
+        },
+        getRemoteLoggingServer: () => {
+          return $http({
+                   method: 'GET',
+                   url: DataService.getHost() +
+                       '/xyz/openbmc_project/logging/config/remote',
+                   withCredentials: true
+                 })
+              .then((response) => {
+                const remoteServer = response.data.data;
+                if (remoteServer === undefined) {
+                  return undefined;
+                }
+                const hostname = remoteServer.Address;
+                const port = remoteServer.Port;
+                if (hostname === '') {
+                  return undefined;
+                } else {
+                  return {
+                    hostname, port
+                  }
+                }
+              });
+        },
+        disableRemoteLoggingServer: () => {
+          return SERVICE.setRemoteLoggingServer({hostname: '', port: 0});
+        },
+        updateRemoteLoggingServer: (data) => {
+          // Recommended to disable existing configuration
+          // before updating config to new server
+          // https://github.com/openbmc/phosphor-logging#changing-the-rsyslog-server
+          return SERVICE.disableRemoteLoggingServer()
+              .then(() => {
+                return SERVICE.setRemoteLoggingServer(data);
+              })
+              .catch(() => {
+                // try updating server even if initial disable attempt fails
+                return SERVICE.setRemoteLoggingServer(data);
+              });
+        },
         getPowerConsumption: function() {
           return $http({
                    method: 'GET',
diff --git a/app/common/styles/base/forms.scss b/app/common/styles/base/forms.scss
index 21253e7..5e75bcc 100644
--- a/app/common/styles/base/forms.scss
+++ b/app/common/styles/base/forms.scss
@@ -10,6 +10,13 @@
   }
 }
 
+.label__helper-text {
+  color: $darkgrey;
+  line-height: 1.2;
+  font-size: 0.9em;
+  margin-bottom: 0.4em;
+}
+
 input[type='email'],
 input[type='number'],
 input[type='password'],
@@ -113,3 +120,7 @@
     max-height: none;
   }
 }
+.form__validation-message {
+  color: $error-color;
+  font-size: 0.9em;
+}
diff --git a/app/common/styles/base/icons.scss b/app/common/styles/base/icons.scss
index 43d3669..557c857 100644
--- a/app/common/styles/base/icons.scss
+++ b/app/common/styles/base/icons.scss
@@ -126,3 +126,18 @@
   @extend .icon__up-arrow;
   transform: rotate(180deg);
 }
+
+.icon__edit {
+  @include status-icon;
+  background-image: url(../assets/images/icon-edit-blue.svg);
+}
+
+.icon__delete {
+  @include status-icon;
+  background-image: url(../assets/images/icon-trashcan-blue.svg);
+}
+
+.icon__close {
+  @include status-icon;
+  background-image: url(../assets/images/crit-x-black.svg);
+}
diff --git a/app/common/styles/elements/modals.scss b/app/common/styles/elements/modals.scss
index 1a8b71f..0bb81d5 100644
--- a/app/common/styles/elements/modals.scss
+++ b/app/common/styles/elements/modals.scss
@@ -18,7 +18,7 @@
   @include fastTransition-all;
 }
 
-.modal {
+.modal:not(.uib-modal) {
   width: auto;
   height: auto;
   left: 50%;
@@ -79,3 +79,26 @@
     }
   }
 }
+
+.uib-modal.fade.in {
+  opacity: 1;
+}
+.uib-modal.in .modal-dialog {
+  transform: translate(0, 10vh);
+  margin-top: 50px;
+  .icon__close {
+    margin: 0;
+    padding: 0;
+  }
+  .modal-content {
+    border-radius: 0;
+    border-color: $black;
+  }
+}
+
+.modal-backdrop.in {
+  opacity: 0.5;
+}
+.uib-modal__content {
+  padding: 1em;
+}