github.com/vmware/govmomi@v0.37.1/nfc/lease_updater.go (about)

     1  /*
     2  Copyright (c) 2014-2015 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package nfc
    18  
    19  import (
    20  	"context"
    21  	"log"
    22  	"net/url"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	"github.com/vmware/govmomi/vim25/progress"
    28  	"github.com/vmware/govmomi/vim25/types"
    29  )
    30  
    31  type FileItem struct {
    32  	types.OvfFileItem
    33  	URL *url.URL
    34  
    35  	ch chan progress.Report
    36  }
    37  
    38  func NewFileItem(u *url.URL, item types.OvfFileItem) FileItem {
    39  	return FileItem{
    40  		OvfFileItem: item,
    41  		URL:         u,
    42  		ch:          make(chan progress.Report),
    43  	}
    44  }
    45  
    46  func (o FileItem) Sink() chan<- progress.Report {
    47  	return o.ch
    48  }
    49  
    50  // File converts the FileItem.OvfFileItem to an OvfFile
    51  func (o FileItem) File() types.OvfFile {
    52  	return types.OvfFile{
    53  		DeviceId: o.DeviceId,
    54  		Path:     o.Path,
    55  		Size:     o.Size,
    56  	}
    57  }
    58  
    59  type LeaseUpdater struct {
    60  	pos   int64 // Number of bytes (keep first to ensure 64 bit alignment)
    61  	total int64 // Total number of bytes (keep first to ensure 64 bit alignment)
    62  
    63  	lease *Lease
    64  
    65  	done chan struct{} // When lease updater should stop
    66  
    67  	wg sync.WaitGroup // Track when update loop is done
    68  }
    69  
    70  func newLeaseUpdater(ctx context.Context, lease *Lease, info *LeaseInfo) *LeaseUpdater {
    71  	l := LeaseUpdater{
    72  		lease: lease,
    73  
    74  		done: make(chan struct{}),
    75  	}
    76  
    77  	for _, item := range info.Items {
    78  		l.total += item.Size
    79  		go l.waitForProgress(item)
    80  	}
    81  
    82  	// Kickstart update loop
    83  	l.wg.Add(1)
    84  	go l.run()
    85  
    86  	return &l
    87  }
    88  
    89  func (l *LeaseUpdater) waitForProgress(item FileItem) {
    90  	var pos, total int64
    91  
    92  	total = item.Size
    93  
    94  	for {
    95  		select {
    96  		case <-l.done:
    97  			return
    98  		case p, ok := <-item.ch:
    99  			// Return in case of error
   100  			if ok && p.Error() != nil {
   101  				return
   102  			}
   103  
   104  			if !ok {
   105  				// Last element on the channel, add to total
   106  				atomic.AddInt64(&l.pos, total-pos)
   107  				return
   108  			}
   109  
   110  			// Approximate progress in number of bytes
   111  			x := int64(float32(total) * (p.Percentage() / 100.0))
   112  			atomic.AddInt64(&l.pos, x-pos)
   113  			pos = x
   114  		}
   115  	}
   116  }
   117  
   118  func (l *LeaseUpdater) run() {
   119  	defer l.wg.Done()
   120  
   121  	tick := time.NewTicker(2 * time.Second)
   122  	defer tick.Stop()
   123  
   124  	for {
   125  		select {
   126  		case <-l.done:
   127  			return
   128  		case <-tick.C:
   129  			// From the vim api HttpNfcLeaseProgress(percent) doc, percent ==
   130  			// "Completion status represented as an integer in the 0-100 range."
   131  			// Always report the current value of percent, as it will renew the
   132  			// lease even if the value hasn't changed or is 0.
   133  			percent := int32(float32(100*atomic.LoadInt64(&l.pos)) / float32(l.total))
   134  			err := l.lease.Progress(context.TODO(), percent)
   135  			if err != nil {
   136  				log.Printf("NFC lease progress: %s", err)
   137  				return
   138  			}
   139  		}
   140  	}
   141  }
   142  
   143  func (l *LeaseUpdater) Done() {
   144  	close(l.done)
   145  	l.wg.Wait()
   146  }