[botanist] Add support for rebooting over AMT

Bug: IN-353 #comment
Change-Id: If16f88344fd0341693932492b486c7f3c2c6e653
diff --git a/Gopkg.lock b/Gopkg.lock
index 02aaedc..e175ab2 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -2,24 +2,15 @@
 
 
 [[projects]]
+  digest = "1:4e800c0d846ed856a032380c87b22577ef03c146ccd26203b62ac90ef78e94b1"
   name = "cloud.google.com/go"
   packages = ["compute/metadata"]
+  pruneopts = ""
   revision = "0fd7230b2a7505833d5f69b75cbd6c9582401479"
   version = "v0.23.0"
 
 [[projects]]
-  name = "github.com/Microsoft/go-winio"
-  packages = ["."]
-  revision = "7da180ee92d8bd8bb8c37fc560e673e6557c392f"
-  version = "v0.4.7"
-
-[[projects]]
-  name = "github.com/davecgh/go-spew"
-  packages = ["spew"]
-  revision = "346938d642f2ec3594ed81d874461961cd0faa76"
-  version = "v1.1.0"
-
-[[projects]]
+  digest = "1:f958a1c137db276e52f0b50efee41a1a389dcdded59a69711f3e872757dab34b"
   name = "github.com/golang/protobuf"
   packages = [
     "jsonpb",
@@ -29,73 +20,73 @@
     "ptypes/any",
     "ptypes/duration",
     "ptypes/struct",
-    "ptypes/timestamp"
+    "ptypes/timestamp",
   ]
+  pruneopts = ""
   revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
   version = "v1.1.0"
 
 [[projects]]
   branch = "master"
+  digest = "1:b264547c40314ec7619d2cf264e2621953843be7242c140efe1e3119f93877f4"
   name = "github.com/google/subcommands"
   packages = ["."]
+  pruneopts = ""
   revision = "5bae204cdfb2d92dcc333d56014bae6a2f6c58b1"
 
 [[projects]]
+  branch = "master"
+  digest = "1:5247b135b5492aa232a731acdcb52b08f32b874cb398f21ab460396eadbe866b"
+  name = "github.com/google/uuid"
+  packages = ["."]
+  pruneopts = ""
+  revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494"
+  source = "https://fuchsia.googlesource.com/third_party/github.com/google/uuid.git"
+
+[[projects]]
+  digest = "1:3c818dada3e41bdb0f509f78e6775610f1bb179449ec8c4c86a45fae35460f3f"
   name = "github.com/julienschmidt/httprouter"
   packages = ["."]
+  pruneopts = ""
   revision = "8c199fb6259ffc1af525cc3ad52ee60ba8359669"
   version = "v1.1"
 
 [[projects]]
-  name = "github.com/kr/pretty"
-  packages = ["."]
-  revision = "73f6ac0b30a98e433b289500d779f50c1a6f0712"
-  version = "v0.1.0"
-
-[[projects]]
-  name = "github.com/kr/text"
-  packages = ["."]
-  revision = "e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f"
-  version = "v0.1.0"
-
-[[projects]]
   branch = "master"
+  digest = "1:c1a5639af00686cffcaefbc1deafb460f42523bf1810ad13e81a6c956be9da1c"
   name = "github.com/maruel/subcommands"
   packages = ["."]
+  pruneopts = ""
   revision = "8c2c452e1460e1c2d42a8e3a3a527fb09bb68ee2"
 
 [[projects]]
   branch = "master"
+  digest = "1:99651e95333755cbe5c9768c1b80031300acca64a80870b40309202b32585a5a"
   name = "github.com/mitchellh/go-homedir"
   packages = ["."]
+  pruneopts = ""
   revision = "3864e76763d94a6df2f9960b16a20a33da9f9a66"
 
 [[projects]]
-  name = "github.com/pmezard/go-difflib"
-  packages = ["difflib"]
-  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
-  version = "v1.0.0"
-
-[[projects]]
-  name = "github.com/satori/go.uuid"
-  packages = ["."]
-  revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
-  version = "v1.2.0"
-
-[[projects]]
-  name = "github.com/stretchr/testify"
-  packages = ["assert"]
-  revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686"
-  version = "v1.2.2"
-
-[[projects]]
   branch = "master"
+  digest = "1:ff0671f12ff98386398469e4b44453369fe164563f48ddb6c6b8ce6963379f97"
   name = "github.com/texttheater/golang-levenshtein"
   packages = ["levenshtein"]
+  pruneopts = ""
   revision = "d188e65d659ef53fcdb0691c12f1bba64928b649"
 
 [[projects]]
   branch = "master"
+  digest = "1:08f54f35f83370bac9bf1043df718d05ca270f48f5faa5bd8e233b5e69a550f0"
+  name = "github.com/xinsnake/go-http-digest-auth-client"
+  packages = ["."]
+  pruneopts = ""
+  revision = "9da83de55d71220f00cbffb736dba373e6e683b1"
+  source = "https://fuchsia.googlesource.com/third_party/github.com/xinsnake/go-http-digest-auth-client.git"
+
+[[projects]]
+  branch = "master"
+  digest = "1:fbf838a2b217690d144649163c51fa70185c751c6850c38a5698e11d9a91fc4f"
   name = "go.chromium.org/luci"
   packages = [
     "auth",
@@ -107,16 +98,12 @@
     "buildbucket/proto",
     "common/cli",
     "common/clock",
-    "common/clock/clockflag",
     "common/data/rand/cryptorand",
     "common/data/rand/mathrand",
-    "common/data/recordio",
     "common/data/stringset",
     "common/data/strpair",
     "common/data/text/indented",
     "common/errors",
-    "common/flag/flagenum",
-    "common/flag/stringmapflag",
     "common/gcloud/googleoauth",
     "common/gcloud/iam",
     "common/iotools",
@@ -136,21 +123,19 @@
     "hardcoded/chromeinfra",
     "logdog/api/endpoints/coordinator/logs/v1",
     "logdog/api/logpb",
-    "logdog/client/butlerlib/bootstrap",
-    "logdog/client/butlerlib/streamclient",
-    "logdog/client/butlerlib/streamproto",
     "logdog/client/coordinator",
     "logdog/common/fetcher",
     "logdog/common/renderer",
     "logdog/common/types",
-    "logdog/common/viewer",
     "lucictx",
-    "server/router"
+    "server/router",
   ]
+  pruneopts = ""
   revision = "8acab14f36eb0e48a8521720d30f77deae4a9339"
 
 [[projects]]
   branch = "master"
+  digest = "1:5dc6753986b9eeba4abdf05dedc5ba06bb52dad43cc8aad35ffb42bb7adfa68f"
   name = "golang.org/x/net"
   packages = [
     "context",
@@ -160,35 +145,38 @@
     "http2/hpack",
     "idna",
     "internal/timeseries",
-    "trace"
+    "trace",
   ]
+  pruneopts = ""
   revision = "db08ff08e8622530d9ed3a0e8ac279f6d4c02196"
   source = "https://go.googlesource.com/net.git"
 
 [[projects]]
   branch = "master"
+  digest = "1:872a8244645e68541452705451d7183a27781920fd171df927716ed8ce74dbf5"
   name = "golang.org/x/oauth2"
   packages = [
     ".",
     "google",
     "internal",
     "jws",
-    "jwt"
+    "jwt",
   ]
+  pruneopts = ""
   revision = "113ce6928c4638e14fd5eba69b9e6ec899d5dd83"
   source = "https://go.googlesource.com/oauth2.git"
 
 [[projects]]
   branch = "master"
+  digest = "1:a8bbf66046a1af68dd81d9d7b12cc9da2d28491fb5632b7757751cada4664444"
   name = "golang.org/x/sys"
-  packages = [
-    "unix",
-    "windows"
-  ]
+  packages = ["unix"]
+  pruneopts = ""
   revision = "fc8bd948cf46f9c7af0f07d34151ce25fe90e477"
   source = "https://go.googlesource.com/sys.git"
 
 [[projects]]
+  digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4"
   name = "golang.org/x/text"
   packages = [
     "collate",
@@ -204,23 +192,27 @@
     "unicode/bidi",
     "unicode/cldr",
     "unicode/norm",
-    "unicode/rangetable"
+    "unicode/rangetable",
   ]
+  pruneopts = ""
   revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
   version = "v0.3.0"
 
 [[projects]]
   branch = "master"
+  digest = "1:7d15746ff4df12481c89fd953a28122fa75368fb1fb1bb1fed918a78647b3c3a"
   name = "google.golang.org/api"
   packages = [
     "googleapi",
-    "googleapi/internal/uritemplates"
+    "googleapi/internal/uritemplates",
   ]
+  pruneopts = ""
   revision = "2eea9ba0a3d94f6ab46508083e299a00bbbc65f6"
   source = "https://code.googlesource.com/google-api-go-client.git"
 
 [[projects]]
   branch = "master"
+  digest = "1:b53c1a5bf2a2d0ccf2b3ff9d6563f84f1e70ca04c0db230d19c160f21c481de5"
   name = "google.golang.org/appengine"
   packages = [
     ".",
@@ -232,21 +224,25 @@
     "internal/modules",
     "internal/remote_api",
     "internal/urlfetch",
-    "urlfetch"
+    "urlfetch",
   ]
+  pruneopts = ""
   revision = "d9a072cfa7b9736e44311ef77b3e09d804bfa599"
   source = "https://fuchsia.googlesource.com/third_party/github.com/golang/appengine.git"
 
 [[projects]]
   branch = "master"
+  digest = "1:d88c2eb6750028f5707971ed1340a02407d5f0967af85bf6ac70c031533f6c15"
   name = "google.golang.org/genproto"
   packages = [
     "googleapis/rpc/status",
-    "protobuf/field_mask"
+    "protobuf/field_mask",
   ]
+  pruneopts = ""
   revision = "32ee49c4dd805befd833990acba36cb75042378c"
 
 [[projects]]
+  digest = "1:dda79c5192c1c59f3f60bbc109a6c710f37e3ebef4d1e7527f586e11ec9ba2af"
   name = "google.golang.org/grpc"
   packages = [
     ".",
@@ -272,14 +268,30 @@
     "stats",
     "status",
     "tap",
-    "transport"
+    "transport",
   ]
+  pruneopts = ""
   revision = "7a6a684ca69eb4cae85ad0a484f2e531598c047b"
   version = "v1.12.2"
 
 [solve-meta]
   analyzer-name = "dep"
   analyzer-version = 1
-  inputs-digest = "0cce7557b1319b77873a7f01642ecc5f57bd2bdf77640e02b24efc42f2c6e9a3"
+  input-imports = [
+    "github.com/google/subcommands",
+    "github.com/google/uuid",
+    "github.com/xinsnake/go-http-digest-auth-client",
+    "go.chromium.org/luci/auth",
+    "go.chromium.org/luci/auth/client/authcli",
+    "go.chromium.org/luci/buildbucket/proto",
+    "go.chromium.org/luci/grpc/prpc",
+    "go.chromium.org/luci/hardcoded/chromeinfra",
+    "go.chromium.org/luci/logdog/client/coordinator",
+    "go.chromium.org/luci/logdog/common/renderer",
+    "go.chromium.org/luci/logdog/common/types",
+    "go.chromium.org/luci/lucictx",
+    "golang.org/x/sys/unix",
+    "google.golang.org/genproto/protobuf/field_mask",
+  ]
   solver-name = "gps-cdcl"
   solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index 1970090..3aace14 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -7,6 +7,16 @@
   branch = "master"
   source = "https://go.googlesource.com/sys.git"
 
+[[constraint]]
+  name = "github.com/google/uuid"
+  branch = "master"
+  source = "https://fuchsia.googlesource.com/third_party/github.com/google/uuid.git"
+
+[[constraint]]
+  name = "github.com/xinsnake/go-http-digest-auth-client"
+  branch = "master"
+  source = "https://fuchsia.googlesource.com/third_party/github.com/xinsnake/go-http-digest-auth-client.git"
+
 [[override]]
   name = "golang.org/x/net"
   branch = "master"
diff --git a/pdu/amt/amt.go b/pdu/amt/amt.go
new file mode 100644
index 0000000..03e717c
--- /dev/null
+++ b/pdu/amt/amt.go
@@ -0,0 +1,62 @@
+// Copyright 2018 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package amt
+
+import (
+	"fmt"
+	"net/url"
+
+	"github.com/google/uuid"
+	dac "github.com/xinsnake/go-http-digest-auth-client"
+)
+
+// Printf string with placeholders for destination uri, message uuid
+const payloadTmpl = `
+<?xml version="1.0" encoding="UTF-8"?>
+<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsman="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns:pms="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService">
+<s:Header>
+  <wsa:Action s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService/RequestPowerStateChange</wsa:Action>
+  <wsa:To s:mustUnderstand="true">%s</wsa:To>
+  <wsman:ResourceURI s:mustUnderstand="true">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService</wsman:ResourceURI>
+  <wsa:MessageID s:mustUnderstand="true">uuid:%s</wsa:MessageID>
+  <wsa:ReplyTo><wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address></wsa:ReplyTo>
+  <wsman:SelectorSet>
+    <wsman:Selector Name="Name">Intel(r) AMT Power Management Service</wsman:Selector>
+    <wsman:Selector Name="SystemName">Intel(r) AMT</wsman:Selector>
+    <wsman:Selector Name="CreationClassName">CIM_PowerManagementService</wsman:Selector>
+    <wsman:Selector Name="SystemCreationClassName">CIM_ComputerSystem</wsman:Selector>
+  </wsman:SelectorSet>
+</s:Header>
+<s:Body>
+  <pms:RequestPowerStateChange_INPUT>
+    <pms:PowerState>10</pms:PowerState>
+    <pms:ManagedElement>
+      <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
+      <wsa:ReferenceParameters>
+        <wsman:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</wsman:ResourceURI>
+        <wsman:SelectorSet>
+          <wsman:Selector Name="Name">ManagedSystem</wsman:Selector>
+          <wsman:Selector Name="CreationClassName">CIM_ComputerSystem</wsman:Selector>
+        </wsman:SelectorSet>
+      </wsa:ReferenceParameters>
+    </pms:ManagedElement>
+  </pms:RequestPowerStateChange_INPUT>
+</s:Body>
+</s:Envelope>
+`
+
+// Reboot sends a Master Bus Reset to an AMT compatible device at host:port.
+func Reboot(host, port, username, password string) error {
+	uri, err := url.Parse(fmt.Sprintf("http://%s:%s/wsman", host, port))
+	if err != nil {
+		return err
+	}
+	// Generate MessageID
+	uuid := uuid.New()
+	payload := fmt.Sprintf(payloadTmpl, uri.String(), uuid)
+	dr := dac.NewRequest(username, password, "POST", uri.String(), payload)
+	_, err = dr.Execute()
+	return nil
+}
diff --git a/pdu/reboot.go b/pdu/reboot.go
index cb53cf0..d99ba50 100644
--- a/pdu/reboot.go
+++ b/pdu/reboot.go
@@ -8,6 +8,7 @@
 	"net/http"
 	"strconv"
 
+	"fuchsia.googlesource.com/infra/infra/pdu/amt"
 	"fuchsia.googlesource.com/infra/infra/pdu/arduinorelay"
 	"fuchsia.googlesource.com/infra/infra/pdu/webcardlx"
 )
@@ -37,6 +38,8 @@
 // RebootDevice uses the given configuration to reboot the device.
 func RebootDevice(cfg *Config) error {
 	switch cfg.Type {
+	case "amt":
+		return amt.Reboot(cfg.Host, cfg.DevicePort, cfg.Username, cfg.Password)
 	case "arduinorelay":
 		return arduinorelay.Reboot(cfg.DevicePath, cfg.DevicePort)
 	case "webcardlx":