github.com/vmware/govmomi@v0.37.2/object/datastore_file_manager.go (about)

     1  /*
     2  Copyright (c) 2017-2018 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 object
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"context"
    23  	"fmt"
    24  	"io"
    25  	"log"
    26  	"path"
    27  	"strings"
    28  
    29  	"github.com/vmware/govmomi/vim25/progress"
    30  	"github.com/vmware/govmomi/vim25/soap"
    31  )
    32  
    33  // DatastoreFileManager combines FileManager and VirtualDiskManager to manage files on a Datastore
    34  type DatastoreFileManager struct {
    35  	Datacenter         *Datacenter
    36  	Datastore          *Datastore
    37  	FileManager        *FileManager
    38  	VirtualDiskManager *VirtualDiskManager
    39  
    40  	Force            bool
    41  	DatacenterTarget *Datacenter
    42  }
    43  
    44  // NewFileManager creates a new instance of DatastoreFileManager
    45  func (d Datastore) NewFileManager(dc *Datacenter, force bool) *DatastoreFileManager {
    46  	c := d.Client()
    47  
    48  	m := &DatastoreFileManager{
    49  		Datacenter:         dc,
    50  		Datastore:          &d,
    51  		FileManager:        NewFileManager(c),
    52  		VirtualDiskManager: NewVirtualDiskManager(c),
    53  		Force:              force,
    54  		DatacenterTarget:   dc,
    55  	}
    56  
    57  	return m
    58  }
    59  
    60  func (m *DatastoreFileManager) WithProgress(ctx context.Context, s progress.Sinker) context.Context {
    61  	return context.WithValue(ctx, m, s)
    62  }
    63  
    64  func (m *DatastoreFileManager) wait(ctx context.Context, task *Task) error {
    65  	var logger progress.Sinker
    66  	if s, ok := ctx.Value(m).(progress.Sinker); ok {
    67  		logger = s
    68  	}
    69  	_, err := task.WaitForResult(ctx, logger)
    70  	return err
    71  }
    72  
    73  // Delete dispatches to the appropriate Delete method based on file name extension
    74  func (m *DatastoreFileManager) Delete(ctx context.Context, name string) error {
    75  	switch path.Ext(name) {
    76  	case ".vmdk":
    77  		return m.DeleteVirtualDisk(ctx, name)
    78  	default:
    79  		return m.DeleteFile(ctx, name)
    80  	}
    81  }
    82  
    83  // DeleteFile calls FileManager.DeleteDatastoreFile
    84  func (m *DatastoreFileManager) DeleteFile(ctx context.Context, name string) error {
    85  	p := m.Path(name)
    86  
    87  	task, err := m.FileManager.DeleteDatastoreFile(ctx, p.String(), m.Datacenter)
    88  	if err != nil {
    89  		return err
    90  	}
    91  
    92  	return m.wait(ctx, task)
    93  }
    94  
    95  // DeleteVirtualDisk calls VirtualDiskManager.DeleteVirtualDisk
    96  // Regardless of the Datastore type, DeleteVirtualDisk will fail if 'ddb.deletable=false',
    97  // so if Force=true this method attempts to set 'ddb.deletable=true' before starting the delete task.
    98  func (m *DatastoreFileManager) DeleteVirtualDisk(ctx context.Context, name string) error {
    99  	p := m.Path(name)
   100  
   101  	var merr error
   102  
   103  	if m.Force {
   104  		merr = m.markDiskAsDeletable(ctx, p)
   105  	}
   106  
   107  	task, err := m.VirtualDiskManager.DeleteVirtualDisk(ctx, p.String(), m.Datacenter)
   108  	if err != nil {
   109  		log.Printf("markDiskAsDeletable(%s): %s", p, merr)
   110  		return err
   111  	}
   112  
   113  	return m.wait(ctx, task)
   114  }
   115  
   116  // CopyFile calls FileManager.CopyDatastoreFile
   117  func (m *DatastoreFileManager) CopyFile(ctx context.Context, src string, dst string) error {
   118  	srcp := m.Path(src)
   119  	dstp := m.Path(dst)
   120  
   121  	task, err := m.FileManager.CopyDatastoreFile(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
   122  	if err != nil {
   123  		return err
   124  	}
   125  
   126  	return m.wait(ctx, task)
   127  }
   128  
   129  // Copy dispatches to the appropriate FileManager or VirtualDiskManager Copy method based on file name extension
   130  func (m *DatastoreFileManager) Copy(ctx context.Context, src string, dst string) error {
   131  	srcp := m.Path(src)
   132  	dstp := m.Path(dst)
   133  
   134  	f := m.FileManager.CopyDatastoreFile
   135  
   136  	if srcp.IsVMDK() {
   137  		// types.VirtualDiskSpec=nil as it is not implemented by vCenter
   138  		f = func(ctx context.Context, src string, srcDC *Datacenter, dst string, dstDC *Datacenter, force bool) (*Task, error) {
   139  			return m.VirtualDiskManager.CopyVirtualDisk(ctx, src, srcDC, dst, dstDC, nil, force)
   140  		}
   141  	}
   142  
   143  	task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	return m.wait(ctx, task)
   149  }
   150  
   151  // MoveFile calls FileManager.MoveDatastoreFile
   152  func (m *DatastoreFileManager) MoveFile(ctx context.Context, src string, dst string) error {
   153  	srcp := m.Path(src)
   154  	dstp := m.Path(dst)
   155  
   156  	task, err := m.FileManager.MoveDatastoreFile(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	return m.wait(ctx, task)
   162  }
   163  
   164  // Move dispatches to the appropriate FileManager or VirtualDiskManager Move method based on file name extension
   165  func (m *DatastoreFileManager) Move(ctx context.Context, src string, dst string) error {
   166  	srcp := m.Path(src)
   167  	dstp := m.Path(dst)
   168  
   169  	f := m.FileManager.MoveDatastoreFile
   170  
   171  	if srcp.IsVMDK() {
   172  		f = m.VirtualDiskManager.MoveVirtualDisk
   173  	}
   174  
   175  	task, err := f(ctx, srcp.String(), m.Datacenter, dstp.String(), m.DatacenterTarget, m.Force)
   176  	if err != nil {
   177  		return err
   178  	}
   179  
   180  	return m.wait(ctx, task)
   181  }
   182  
   183  // Path converts path name to a DatastorePath
   184  func (m *DatastoreFileManager) Path(name string) *DatastorePath {
   185  	var p DatastorePath
   186  
   187  	if !p.FromString(name) {
   188  		p.Path = name
   189  		p.Datastore = m.Datastore.Name()
   190  	}
   191  
   192  	return &p
   193  }
   194  
   195  func (m *DatastoreFileManager) markDiskAsDeletable(ctx context.Context, path *DatastorePath) error {
   196  	r, _, err := m.Datastore.Download(ctx, path.Path, &soap.DefaultDownload)
   197  	if err != nil {
   198  		return err
   199  	}
   200  
   201  	defer r.Close()
   202  
   203  	hasFlag := false
   204  	buf := new(bytes.Buffer)
   205  
   206  	s := bufio.NewScanner(&io.LimitedReader{R: r, N: 2048}) // should be only a few hundred bytes, limit to be sure
   207  
   208  	for s.Scan() {
   209  		line := s.Text()
   210  		if strings.HasPrefix(line, "ddb.deletable") {
   211  			hasFlag = true
   212  			continue
   213  		}
   214  
   215  		fmt.Fprintln(buf, line)
   216  	}
   217  
   218  	if err := s.Err(); err != nil {
   219  		return err // any error other than EOF
   220  	}
   221  
   222  	if !hasFlag {
   223  		return nil // already deletable, so leave as-is
   224  	}
   225  
   226  	// rewrite the .vmdk with ddb.deletable flag removed (the default is true)
   227  	return m.Datastore.Upload(ctx, buf, path.Path, &soap.DefaultUpload)
   228  }