github.com/vmware/govmomi@v0.51.0/simulator/http_nfc_lease.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package simulator
     6  
     7  import (
     8  	"crypto/sha1"
     9  	"encoding/hex"
    10  	"fmt"
    11  	"hash"
    12  	"io"
    13  	"log"
    14  	"net/http"
    15  	"net/url"
    16  	"os"
    17  	"strings"
    18  	"sync"
    19  
    20  	"github.com/vmware/govmomi/vim25/methods"
    21  	"github.com/vmware/govmomi/vim25/mo"
    22  	"github.com/vmware/govmomi/vim25/soap"
    23  	"github.com/vmware/govmomi/vim25/types"
    24  )
    25  
    26  type metadata struct {
    27  	sha1 []byte
    28  	size int64
    29  }
    30  
    31  type HttpNfcLease struct {
    32  	mo.HttpNfcLease
    33  	files    map[string]string
    34  	metadata map[string]metadata
    35  }
    36  
    37  var (
    38  	nfcLease  sync.Map // HTTP access to NFC leases are token based and do not require Session auth
    39  	nfcPrefix = "/nfc/"
    40  )
    41  
    42  // ServeNFC handles NFC file upload/download
    43  func ServeNFC(w http.ResponseWriter, r *http.Request) {
    44  	p := strings.Split(r.URL.Path, "/")
    45  	id, name := p[len(p)-2], p[len(p)-1]
    46  	ref := types.ManagedObjectReference{Type: "HttpNfcLease", Value: id}
    47  	l, ok := nfcLease.Load(ref)
    48  	if !ok {
    49  		log.Printf("invalid NFC lease: %s", id)
    50  		http.NotFound(w, r)
    51  		return
    52  	}
    53  	lease := l.(*HttpNfcLease)
    54  	file, ok := lease.files[name]
    55  	if !ok {
    56  		log.Printf("invalid NFC device id: %s", name)
    57  		http.NotFound(w, r)
    58  		return
    59  	}
    60  
    61  	status := http.StatusOK
    62  	var sum hash.Hash
    63  	var dst io.Writer = w
    64  	var src io.ReadCloser
    65  
    66  	switch r.Method {
    67  	case http.MethodPut, http.MethodPost:
    68  		sum = sha1.New()
    69  		dst = sum
    70  		src = r.Body
    71  	case http.MethodGet:
    72  		f, err := os.Open(file)
    73  		if err != nil {
    74  			http.NotFound(w, r)
    75  			return
    76  		}
    77  		src = f
    78  	default:
    79  		status = http.StatusMethodNotAllowed
    80  	}
    81  
    82  	n, err := io.Copy(dst, src)
    83  	_ = src.Close()
    84  	if sum != nil {
    85  		lease.metadata[name] = metadata{
    86  			sha1: sum.Sum(nil),
    87  			size: n,
    88  		}
    89  	}
    90  
    91  	msg := fmt.Sprintf("transferred %d bytes", n)
    92  	if err != nil {
    93  		status = http.StatusInternalServerError
    94  		msg = err.Error()
    95  	}
    96  	tracef("nfc %s %s: %s", r.Method, file, msg)
    97  	w.WriteHeader(status)
    98  }
    99  
   100  func (l *HttpNfcLease) error(ctx *Context, err *types.LocalizedMethodFault) {
   101  	ctx.WithLock(l, func() {
   102  		ctx.Update(l, []types.PropertyChange{
   103  			{Name: "state", Val: types.HttpNfcLeaseStateError},
   104  			{Name: "error", Val: err},
   105  		})
   106  	})
   107  }
   108  
   109  func (l *HttpNfcLease) ready(ctx *Context, entity types.ManagedObjectReference, urls []types.HttpNfcLeaseDeviceUrl) {
   110  	info := &types.HttpNfcLeaseInfo{
   111  		Lease:        l.Self,
   112  		Entity:       entity,
   113  		DeviceUrl:    urls,
   114  		LeaseTimeout: 300,
   115  	}
   116  
   117  	ctx.WithLock(l, func() {
   118  		ctx.Update(l, []types.PropertyChange{
   119  			{Name: "state", Val: types.HttpNfcLeaseStateReady},
   120  			{Name: "info", Val: info},
   121  		})
   122  	})
   123  }
   124  
   125  func newHttpNfcLease(ctx *Context) *HttpNfcLease {
   126  	lease := &HttpNfcLease{
   127  		HttpNfcLease: mo.HttpNfcLease{
   128  			State: types.HttpNfcLeaseStateInitializing,
   129  		},
   130  		files:    make(map[string]string),
   131  		metadata: make(map[string]metadata),
   132  	}
   133  
   134  	ctx.Session.Put(lease)
   135  	nfcLease.Store(lease.Reference(), lease)
   136  
   137  	return lease
   138  }
   139  
   140  func leaseURL(ctx *Context) *url.URL {
   141  	opt := ctx.Map.OptionManager().find("vcsim.server.url")
   142  
   143  	u, _ := url.Parse(opt.Value.(string))
   144  
   145  	// See NfcLease.DeviceUrl doc:
   146  	//  If a "*" is returned the client must substitute "*" with the
   147  	//  hostname or IP address used when connecting to the server.
   148  	// This is the case when connecting directly to an ESX host.
   149  	if ctx.Map.IsESX() {
   150  		u.Host = "*"
   151  	}
   152  
   153  	return u
   154  }
   155  
   156  func (l *HttpNfcLease) HttpNfcLeaseComplete(ctx *Context, req *types.HttpNfcLeaseComplete) soap.HasFault {
   157  	ctx.Session.Remove(ctx, req.This)
   158  	nfcLease.Delete(req.This)
   159  
   160  	return &methods.HttpNfcLeaseCompleteBody{
   161  		Res: new(types.HttpNfcLeaseCompleteResponse),
   162  	}
   163  }
   164  
   165  func (l *HttpNfcLease) HttpNfcLeaseAbort(ctx *Context, req *types.HttpNfcLeaseAbort) soap.HasFault {
   166  	ctx.Session.Remove(ctx, req.This)
   167  	nfcLease.Delete(req.This)
   168  
   169  	return &methods.HttpNfcLeaseAbortBody{
   170  		Res: new(types.HttpNfcLeaseAbortResponse),
   171  	}
   172  }
   173  
   174  func (l *HttpNfcLease) HttpNfcLeaseProgress(ctx *Context, req *types.HttpNfcLeaseProgress) soap.HasFault {
   175  	l.TransferProgress = req.Percent
   176  
   177  	return &methods.HttpNfcLeaseProgressBody{
   178  		Res: new(types.HttpNfcLeaseProgressResponse),
   179  	}
   180  }
   181  
   182  func (l *HttpNfcLease) getDeviceKey(name string) string {
   183  	for _, devUrl := range l.Info.DeviceUrl {
   184  		if name == devUrl.TargetId {
   185  			return devUrl.Key
   186  		}
   187  	}
   188  	return "unknown"
   189  }
   190  
   191  func (l *HttpNfcLease) HttpNfcLeaseGetManifest(ctx *Context, req *types.HttpNfcLeaseGetManifest) soap.HasFault {
   192  	entries := []types.HttpNfcLeaseManifestEntry{}
   193  	for name, md := range l.metadata {
   194  		entries = append(entries, types.HttpNfcLeaseManifestEntry{
   195  			Key:  l.getDeviceKey(name),
   196  			Sha1: hex.EncodeToString(md.sha1),
   197  			Size: md.size,
   198  		})
   199  	}
   200  	return &methods.HttpNfcLeaseGetManifestBody{
   201  		Res: &types.HttpNfcLeaseGetManifestResponse{
   202  			Returnval: entries,
   203  		},
   204  	}
   205  }