From 39c0ac797ba9ad5ab0ab0c3ad804c7bb3eaabb62 Mon Sep 17 00:00:00 2001 From: wanyaoqi Date: Wed, 27 Feb 2019 12:11:14 +0800 Subject: [PATCH] add host metadat, radix minor fix --- pkg/appsrv/appsrv.go | 5 +- pkg/appsrv/radix.go | 5 +- pkg/appsrv/stats.go | 2 +- pkg/hostman/diskhandlers/diskhandler.go | 3 - pkg/hostman/host_services.go | 6 + pkg/hostman/metadata/metadatahandler.go | 254 ++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 pkg/hostman/metadata/metadatahandler.go diff --git a/pkg/appsrv/appsrv.go b/pkg/appsrv/appsrv.go index 3b24dcb823..6d99af64cf 100644 --- a/pkg/appsrv/appsrv.go +++ b/pkg/appsrv/appsrv.go @@ -388,7 +388,10 @@ func (app *Application) ListenAndServeWithCleanup(addr string, onStop func()) { func (app *Application) ListenAndServeTLSWithCleanup(addr string, certFile, keyFile string, onStop func()) { s := app.initServer(addr) - app.registerCleanShutdown(s, onStop) + if onStop != nil { + app.registerCleanShutdown(s, onStop) + } + var err error if len(certFile) == 0 && len(keyFile) == 0 { err = s.ListenAndServe() diff --git a/pkg/appsrv/radix.go b/pkg/appsrv/radix.go index 7052860e85..57a88b3f97 100644 --- a/pkg/appsrv/radix.go +++ b/pkg/appsrv/radix.go @@ -33,7 +33,10 @@ func isRegexSegment(seg string) bool { func (r *RadixNode) Add(segments []string, data interface{}) error { err := r.add(segments, data, 1, nil) - return fmt.Errorf("Add Node error: %s %s", err, strings.Join(segments, "/")) + if err != nil { + return fmt.Errorf("Add Node error: %s %s", err, strings.Join(segments, "/")) + } + return nil } func (r *RadixNode) add(segments []string, data interface{}, depth int, segNames map[int]string) error { diff --git a/pkg/appsrv/stats.go b/pkg/appsrv/stats.go index 5eb9c22699..8135efdbc5 100644 --- a/pkg/appsrv/stats.go +++ b/pkg/appsrv/stats.go @@ -11,7 +11,7 @@ import ( func doStatsHandler(stats *jsonutils.JSONArray, method, path string, hi, total *SHandlerInfo) { s := jsonutils.NewDict() s.Add(jsonutils.NewString(method), "method") - s.Add(jsonutils.NewString("/"+path), "path") + s.Add(jsonutils.NewString(path), "path") s.Add(jsonutils.NewString(hi.GetName(nil)), "name") s.Add(jsonutils.NewInt(hi.counter2XX.hit), "hit.2XX") s.Add(jsonutils.NewFloat(hi.counter2XX.duration), "duration.2XX") diff --git a/pkg/hostman/diskhandlers/diskhandler.go b/pkg/hostman/diskhandlers/diskhandler.go index c2d6a9279f..db5304c813 100644 --- a/pkg/hostman/diskhandlers/diskhandler.go +++ b/pkg/hostman/diskhandlers/diskhandler.go @@ -6,7 +6,6 @@ import ( "net/http" "yunion.io/x/jsonutils" - "yunion.io/x/log" "yunion.io/x/pkg/util/regutils" "yunion.io/x/onecloud/pkg/appsrv" @@ -175,13 +174,11 @@ func diskDelete(ctx context.Context, storage storageman.IStorage, diskId string, } func diskResize(ctx context.Context, storage storageman.IStorage, diskId string, disk storageman.IDisk, body jsonutils.JSONObject) (interface{}, error) { - log.Errorln(body.String()) diskInfo, err := body.Get("disk") if err != nil { return nil, httperrors.NewMissingParameterError("disk") } serverId, _ := diskInfo.GetString("server_id") - log.Errorln(serverId, guestman.GetGuestManager().Status(serverId)) if len(serverId) > 0 && guestman.GetGuestManager().Status(serverId) == "running" { sizeMb, _ := diskInfo.Int("size") return guestman.GetGuestManager().OnlineResizeDisk(ctx, serverId, diskId, sizeMb) diff --git a/pkg/hostman/host_services.go b/pkg/hostman/host_services.go index 5b2a0a610e..512ecaf8f3 100644 --- a/pkg/hostman/host_services.go +++ b/pkg/hostman/host_services.go @@ -16,6 +16,7 @@ import ( "yunion.io/x/onecloud/pkg/hostman/hostinfo" "yunion.io/x/onecloud/pkg/hostman/hostmetrics" "yunion.io/x/onecloud/pkg/hostman/hostutils" + "yunion.io/x/onecloud/pkg/hostman/metadata" "yunion.io/x/onecloud/pkg/hostman/options" "yunion.io/x/onecloud/pkg/hostman/storageman" ) @@ -53,6 +54,11 @@ func (host *SHostService) StartService() { host.initHandlers(app) <-hostinfo.Instance().IsRegistered // wait host and guest init + // Init Metadata handler + go metadata.StartService( + cloudcommon.InitApp(&options.HostOptions.CommonOptions, false), + options.HostOptions.Address, options.HostOptions.Port+1000) + cronManager := cronman.GetCronJobManager(false) cronManager.AddJob2( "CleanRecycleDiskFiles", 1, 3, 0, 0, storageman.CleanRecycleDiskfiles, false) diff --git a/pkg/hostman/metadata/metadatahandler.go b/pkg/hostman/metadata/metadatahandler.go new file mode 100644 index 0000000000..5c3e25daaf --- /dev/null +++ b/pkg/hostman/metadata/metadatahandler.go @@ -0,0 +1,254 @@ +package metadata + +import ( + "context" + "encoding/base64" + "fmt" + "net" + "net/http" + "strconv" + "strings" + + "yunion.io/x/log" + "yunion.io/x/onecloud/pkg/appsrv" + "yunion.io/x/onecloud/pkg/hostman/guestman" + "yunion.io/x/onecloud/pkg/hostman/hostutils" + "yunion.io/x/onecloud/pkg/httperrors" + "yunion.io/x/pkg/util/netutils" +) + +func addMetadataHandler(prefix string, app *appsrv.Application) { + for _, method := range []string{"GET", "HEAD"} { + app.AddHandler(method, fmt.Sprintf("%s/", + prefix, `(latest|\d{4}-\d{2}-\d{2})`), versionOnly) + } + + for _, method := range []string{"GET", "HEAD"} { + app.AddHandler(method, fmt.Sprintf("%s//user-data", + prefix, `(latest|\d{4}-\d{2}-\d{2})`), userData) + app.AddHandler(method, fmt.Sprintf("%s//meta-data", + prefix, `(latest|\d{4}-\d{2}-\d{2})`), metaData) + } +} + +func versionOnly(ctx context.Context, w http.ResponseWriter, r *http.Request) { + hostutils.Response(ctx, w, strings.Join([]string{"meta-data", "user-data"}, "\n")) +} + +func userData(ctx context.Context, w http.ResponseWriter, r *http.Request) { + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + hostutils.Response(ctx, w, httperrors.NewBadRequestError("Parse Remoteaddr %s error %s", r.RemoteAddr, err.Error())) + return + } + guestDesc, gusetNic := guestman.GetGuestManager().GetGuestNicDesc("", ip, "", "", false) + if guestDesc == nil || gusetNic == nil { + hostutils.Response(ctx, w, "") + return + } + + if !guestDesc.Contains("user_data") { + hostutils.Response(ctx, w, "") + return + } + + guestUserData, _ := guestDesc.GetString("user_data") + userDataDecoded, err := base64.StdEncoding.DecodeString(guestUserData) + if err != nil { + guestId, _ := guestDesc.GetString("id") + log.Errorf("Error format user_data %s, %s", guestId, guestUserData) + hostutils.Response(ctx, w, "") + return + } + hostutils.Response(ctx, w, string(userDataDecoded)) +} + +func metaData(ctx context.Context, w http.ResponseWriter, r *http.Request) { + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + hostutils.Response(ctx, w, httperrors.NewBadRequestError("Parse Remoteaddr %s error %s", r.RemoteAddr, err.Error())) + return + } + + guestDesc, gusetNic := guestman.GetGuestManager().GetGuestNicDesc("", ip, "", "", false) + if guestDesc == nil || gusetNic == nil { + hostutils.Response(ctx, w, "") + return + } + + req := appsrv.SplitPath(r.URL.Path)[2:] + + if len(req) == 0 { + resNames := []string{ + "ami-launch-index", + "block-device-mapping/", "hostname", + "instance-id", "instance-type", + "local-hostname", "local-ipv4", "mac", + "public-hostname", "public-ipv4", + "network_config/", + //"amiid", "ami-manifest-path", + //"instance-action", "kernel-id", + //"ipv4-associations", "network/", + //"placement/", "public-keys/", + //"reservation-id", "security-groups", "password", + } + if guestDesc.Contains("pubkey") { + resNames = append(resNames, "public-keys/") + } + if guestDesc.Contains("zone") { + resNames = append(resNames, "placement/") + } + if guestDesc.Contains("secgroup") { + resNames = append(resNames, "security-groups/") + } + hostutils.Response(ctx, w, strings.Join(resNames, "\n")) + } else { + resName := req[0] + switch resName { + case "public-keys": + if guestDesc.Contains("pubkey") { + if len(req) == 1 { + hostutils.Response(ctx, w, "0=my-public-key") + return + } else if len(req) == 2 { + hostutils.Response(ctx, w, "openssh-key") + return + } else if len(req) == 3 { + pubkey, _ := guestDesc.GetString("pubkeu") + hostutils.Response(ctx, w, pubkey) + return + } + } + case "hostname", "public-hostname", "local-hostname": + guestName, _ := guestDesc.GetString("name") + hostutils.Response(ctx, w, guestName) + return + case "instance-id": + guestUUID, _ := guestDesc.GetString("uuid") + hostutils.Response(ctx, w, guestUUID) + return + case "instance-type": + flavor, err := guestDesc.GetString("flavor") + if err != nil { + flavor = "customized" + } + hostutils.Response(ctx, w, flavor) + return + case "mac": + macs := make([]string, 0) + guestNics, _ := guestDesc.GetArray("nics") + for _, nic := range guestNics { + nicMac, _ := nic.GetString("mac") + macs = append(macs, nicMac) + } + hostutils.Response(ctx, w, strings.Join(macs, "\n")) + return + case "local-ipv4": + ips := make([]string, 0) + guestNics, _ := guestDesc.GetArray("nics") + for _, nic := range guestNics { + nicip, _ := nic.GetString("ip") + ips = append(ips, nicip) + } + hostutils.Response(ctx, w, strings.Join(ips, "\n")) + return + case "public-ipv4": + ips := make([]string, 0) + guestNics, _ := guestDesc.GetArray("nics") + for _, nic := range guestNics { + nicip, _ := nic.GetString("ip") + ipv4, _ := netutils.NewIPV4Addr(nicip) + if !netutils.IsPrivate(ipv4) { + ips = append(ips, nicip) + } + } + hostutils.Response(ctx, w, strings.Join(ips, "\n")) + return + case "placement": + if guestDesc.Contains("zone") { + if len(req) == 1 { + hostutils.Response(ctx, w, "availability-zone") + return + } else if len(req) == 2 && req[1] == "availability-zone" { + guestZone, _ := guestDesc.GetString("zone") + hostutils.Response(ctx, w, guestZone) + return + } + } + case "security-groups": + if guestDesc.Contains("secgroup") { + guestSecgroup, _ := guestDesc.GetString("secgroup") + hostutils.Response(ctx, w, guestSecgroup) + return + } + case "ami-launch-index": + hostutils.Response(ctx, w, "0") + return + case "network_config": + if len(req) == 1 { + hostutils.Response(ctx, w, + strings.Join([]string{"name", "content_path"}, "\n")) + return + } else if len(req) == 2 { + if req[1] == "name" { + hostutils.Response(ctx, w, "network_config") + return + } else if req[1] == "content_path" { + hostutils.Response(ctx, w, "content/0001") + return + } + } + case "block-device-mapping": + guestDisks, _ := guestDesc.GetArray("disks") + swapDisks := make([]string, 0) + dataDisk := make([]string, 0) + for _, d := range guestDisks { + fs, err := d.GetString("fs") + idx, _ := d.Int("index") + if err != nil && fs == "swap" { + swapDisks = append(swapDisks, strconv.Itoa(int(idx))) + } else { + dataDisk = append(dataDisk, strconv.Itoa(int(idx))) + } + } + if len(req) == 1 { + devs := []string{"root"} + if len(swapDisks) > 0 { + devs = append(devs, "swap") + } + if len(dataDisk) > 0 { + for i := 0; i < len(dataDisk); i++ { + devs = append(devs, fmt.Sprintf("ephemeral%d", i+1)) + } + } + hostutils.Response(ctx, w, strings.Join(devs, "\n")) + return + } else if len(req) == 2 { + devs := []string{} + if req[1] == "root" { + devs = append(devs, "/dev/root") + } else if req[1] == "swap" { + for i := 0; i < len(swapDisks); i++ { + idx, _ := strconv.Atoi(swapDisks[i]) + devs = append(devs, fmt.Sprintf("/dev/vd%c1", 'a'+idx)) + } + } else if strings.HasPrefix(req[1], "ephemeral") || strings.HasPrefix(req[1], "ebs") { + for i := 0; i < len(dataDisk); i++ { + idx, _ := strconv.Atoi(dataDisk[i]) + devs = append(devs, fmt.Sprintf("/dev/vd%c1", 'a'+idx)) + } + } + hostutils.Response(ctx, w, strings.Join(devs, "\n")) + return + } + } + } + hostutils.Response(ctx, w, httperrors.NewNotFoundError("Resource not handled")) +} + +func StartService(app *appsrv.Application, address string, port int) { + addMetadataHandler("", app) + addr := net.JoinHostPort(address, strconv.Itoa(port)) + log.Infof("Host Metadata Start listen on %s://%s", "http", addr) + app.ListenAndServeWithCleanup(addr, nil) +}