yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/esxi/monitor.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package esxi
    16  
    17  import (
    18  	"fmt"
    19  	"time"
    20  
    21  	"github.com/vmware/govmomi/object"
    22  	"github.com/vmware/govmomi/performance"
    23  	"github.com/vmware/govmomi/view"
    24  	"github.com/vmware/govmomi/vim25/types"
    25  
    26  	"yunion.io/x/jsonutils"
    27  	"yunion.io/x/log"
    28  	"yunion.io/x/pkg/errors"
    29  
    30  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    31  )
    32  
    33  const (
    34  	MONTYPE_HOSTSYSTEM     = "HostSystem"
    35  	MONTYPE_VIRTUALMACHINE = "VirtualMachine"
    36  )
    37  
    38  func (self *SESXiClient) GetEcsMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
    39  	metricName := ""
    40  	switch opts.MetricType {
    41  	case cloudprovider.VM_METRIC_TYPE_CPU_USAGE:
    42  		metricName = "cpu.usage.average"
    43  	case cloudprovider.VM_METRIC_TYPE_MEM_USAGE:
    44  		metricName = "mem.usage.average"
    45  	case cloudprovider.VM_METRIC_TYPE_NET_BPS_RX:
    46  		metricName = "net.bytesRx.average"
    47  	case cloudprovider.VM_METRIC_TYPE_NET_BPS_TX:
    48  		metricName = "net.bytesTx.average"
    49  	case cloudprovider.VM_METRIC_TYPE_DISK_IO_READ_BPS:
    50  		metricName = "disk.read.average"
    51  	case cloudprovider.VM_METRIC_TYPE_DISK_IO_WRITE_BPS:
    52  		metricName = "disk.write.average"
    53  	default:
    54  		return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "%s", opts.MetricType)
    55  	}
    56  	return self.getEcsMetrics(metricName, opts.MetricType, opts.StartTime, opts.EndTime)
    57  }
    58  
    59  func (self *SESXiClient) getEcsMetrics(metricName string, metricType cloudprovider.TMetricType, start, end time.Time) ([]cloudprovider.MetricValues, error) {
    60  	m := view.NewManager(self.client.Client)
    61  	v, err := m.CreateContainerView(self.context, self.client.Client.ServiceContent.RootFolder, nil, true)
    62  	if err != nil {
    63  		return nil, errors.Wrapf(err, "CreateContainerView")
    64  	}
    65  	vms, err := v.Find(self.context, []string{MONTYPE_VIRTUALMACHINE}, nil)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	perfManager := performance.NewManager(self.client.Client)
    70  	counterInfo, err := perfManager.CounterInfoByName(self.context)
    71  	if err != nil {
    72  		return nil, errors.Wrapf(err, "CounterInfoByName")
    73  	}
    74  
    75  	counter, ok := counterInfo[metricName]
    76  	if !ok {
    77  		return nil, fmt.Errorf("not found %s", metricName)
    78  	}
    79  
    80  	queries := []types.PerfQuerySpec{}
    81  	for i := range vms {
    82  		query := types.PerfQuerySpec{
    83  			Entity: vms[i].Reference(),
    84  			MetricId: []types.PerfMetricId{
    85  				{
    86  					CounterId: counter.Key,
    87  				},
    88  			},
    89  			Format:     "normal",
    90  			IntervalId: 20,
    91  			StartTime:  &start,
    92  			EndTime:    &end,
    93  		}
    94  		queries = append(queries, query)
    95  	}
    96  
    97  	sample, err := perfManager.Query(self.context, queries)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	result, err := perfManager.ToMetricSeries(self.context, sample)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	ret := []cloudprovider.MetricValues{}
   108  	for _, metric := range result {
   109  		vm := object.NewVirtualMachine(self.client.Client, metric.Entity)
   110  		metricValue := cloudprovider.MetricValues{}
   111  		metricValue.MetricType = metricType
   112  		metricValue.Id = vm.UUID(self.context)
   113  		if len(metric.Value) == 0 {
   114  			continue
   115  		}
   116  		for i, v := range metric.SampleInfo {
   117  			if v.Timestamp.Second() != 0 {
   118  				continue
   119  			}
   120  			var value float64
   121  			if len(metric.Value[0].Value) > i {
   122  				value = float64(metric.Value[0].Value[i])
   123  			}
   124  			switch counter.UnitInfo.GetElementDescription().Key {
   125  			case "percent":
   126  				value = value / 100.0
   127  			case "kiloBytesPerSecond":
   128  				value = value * 1024.0
   129  			default:
   130  				log.Errorf("unknow unit: %s", counter.UnitInfo.GetElementDescription().Key)
   131  			}
   132  			metricValue.Values = append(metricValue.Values, cloudprovider.MetricValue{
   133  				Timestamp: v.Timestamp,
   134  				Value:     value,
   135  			})
   136  		}
   137  		ret = append(ret, metricValue)
   138  	}
   139  	return ret, nil
   140  }
   141  
   142  func (self *SESXiClient) GetHostMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   143  	metricName := ""
   144  	switch opts.MetricType {
   145  	case cloudprovider.HOST_METRIC_TYPE_CPU_USAGE:
   146  		metricName = "cpu.usage.average"
   147  	case cloudprovider.HOST_METRIC_TYPE_MEM_USAGE:
   148  		metricName = "mem.usage.average"
   149  	case cloudprovider.HOST_METRIC_TYPE_NET_BPS_RX:
   150  		metricName = "net.received.average"
   151  	case cloudprovider.HOST_METRIC_TYPE_NET_BPS_TX:
   152  		metricName = "net.transmitted.average"
   153  	case cloudprovider.HOST_METRIC_TYPE_DISK_IO_READ_BPS:
   154  		metricName = "disk.read.average"
   155  	case cloudprovider.HOST_METRIC_TYPE_DISK_IO_WRITE_BPS:
   156  		metricName = "disk.write.average"
   157  	default:
   158  		return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "%s", opts.MetricType)
   159  	}
   160  	return self.getHostMetrics(metricName, opts.MetricType, opts.StartTime, opts.EndTime)
   161  }
   162  
   163  func (self *SESXiClient) getHostMetrics(metricName string, metricType cloudprovider.TMetricType, start, end time.Time) ([]cloudprovider.MetricValues, error) {
   164  	m := view.NewManager(self.client.Client)
   165  	v, err := m.CreateContainerView(self.context, self.client.Client.ServiceContent.RootFolder, nil, true)
   166  	if err != nil {
   167  		return nil, errors.Wrapf(err, "CreateContainerView")
   168  	}
   169  	vms, err := v.Find(self.context, []string{"HostSystem"}, nil)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	perfManager := performance.NewManager(self.client.Client)
   174  	counterInfo, err := perfManager.CounterInfoByName(self.context)
   175  	if err != nil {
   176  		return nil, errors.Wrapf(err, "CounterInfoByName")
   177  	}
   178  
   179  	counter, ok := counterInfo[metricName]
   180  	if !ok {
   181  		return nil, fmt.Errorf("not found %s", metricName)
   182  	}
   183  	keys := []types.PerfMetricId{}
   184  	for _, v := range counterInfo {
   185  		keys = append(keys, types.PerfMetricId{
   186  			CounterId: v.Key,
   187  		})
   188  	}
   189  
   190  	queries := []types.PerfQuerySpec{}
   191  	for i := range vms {
   192  		query := types.PerfQuerySpec{
   193  			Entity: vms[i].Reference(),
   194  			MetricId: []types.PerfMetricId{
   195  				{
   196  					CounterId: counter.Key,
   197  				},
   198  			},
   199  			Format:     "normal",
   200  			IntervalId: 0,
   201  			StartTime:  &start,
   202  			EndTime:    &end,
   203  		}
   204  		queries = append(queries, query)
   205  	}
   206  
   207  	sample, err := perfManager.Query(self.context, queries)
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  
   212  	result, err := perfManager.ToMetricSeries(self.context, sample)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	dcs, err := self.GetDatacenters()
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	hostMap := map[string]string{}
   222  	for i := range dcs {
   223  		if dcs[i] == nil {
   224  			continue
   225  		}
   226  		part, err := dcs[i].GetIHosts()
   227  		if err != nil {
   228  			continue
   229  		}
   230  		for j := range part {
   231  			host := part[j].(*SHost)
   232  			hostMap[host.GetId()] = host.GetGlobalId()
   233  		}
   234  	}
   235  
   236  	ret := []cloudprovider.MetricValues{}
   237  	for _, metric := range result {
   238  		metricValue := cloudprovider.MetricValues{}
   239  		metricValue.MetricType = metricType
   240  		hostId, ok := hostMap[metric.Entity.Value]
   241  		if !ok {
   242  			continue
   243  		}
   244  		metricValue.Id = hostId
   245  		if len(metric.Value) == 0 {
   246  			continue
   247  		}
   248  		for i, v := range metric.SampleInfo {
   249  			if v.Timestamp.Second() != 0 {
   250  				continue
   251  			}
   252  			var value float64
   253  			if len(metric.Value[0].Value) > i {
   254  				value = float64(metric.Value[0].Value[i])
   255  			}
   256  			switch counter.UnitInfo.GetElementDescription().Key {
   257  			case "percent":
   258  				value = value / 100.0
   259  			case "kiloBytesPerSecond":
   260  				value = value * 1024.0
   261  			default:
   262  				log.Errorf("unknow unit: %s", counter.UnitInfo.GetElementDescription().Key)
   263  			}
   264  			metricValue.Values = append(metricValue.Values, cloudprovider.MetricValue{
   265  				Timestamp: v.Timestamp,
   266  				Value:     value,
   267  			})
   268  		}
   269  		ret = append(ret, metricValue)
   270  	}
   271  	return ret, nil
   272  }
   273  
   274  func (self *SESXiClient) GetMetrics(opts *cloudprovider.MetricListOptions) ([]cloudprovider.MetricValues, error) {
   275  	switch opts.ResourceType {
   276  	case cloudprovider.METRIC_RESOURCE_TYPE_SERVER:
   277  		return self.GetEcsMetrics(opts)
   278  	case cloudprovider.METRIC_RESOURCE_TYPE_HOST:
   279  		return self.GetHostMetrics(opts)
   280  	default:
   281  		return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "%s", opts.ResourceType)
   282  	}
   283  }
   284  
   285  func (self *SESXiClient) GetMetricTypes() (jsonutils.JSONObject, error) {
   286  	perfManager := performance.NewManager(self.client.Client)
   287  	counterInfo, err := perfManager.CounterInfoByName(self.context)
   288  	if err != nil {
   289  		return nil, errors.Wrapf(err, "CounterInfoByName")
   290  	}
   291  	return jsonutils.Marshal(counterInfo), nil
   292  }