From 68295457775bf67506734139a895846781cf4f23 Mon Sep 17 00:00:00 2001 From: ashoksingh Date: Mon, 18 May 2015 12:50:10 +0530 Subject: [PATCH] Add support to export docker container statistics from agent. -Define a new hypervisor mode for docker in agent. -Define different class types for collection of stats for KVM and Docker -Based on hypervisor type create appropriate object for collection of stats Change-Id: I81290adf9d10494d0dde21976f353a0c55064f21 Closes-Bug: #1454211 --- src/vnsw/agent/cmn/agent.cc | 8 + src/vnsw/agent/cmn/agent.h | 2 + src/vnsw/agent/init/agent_param.cc | 2 + src/vnsw/agent/init/agent_param.h | 2 + src/vnsw/agent/uve/SConscript | 2 + src/vnsw/agent/uve/vm_stat.cc | 266 ----------------------- src/vnsw/agent/uve/vm_stat.h | 25 +-- src/vnsw/agent/uve/vm_stat_docker.cc | 203 ++++++++++++++++++ src/vnsw/agent/uve/vm_stat_docker.h | 34 +++ src/vnsw/agent/uve/vm_stat_kvm.cc | 303 +++++++++++++++++++++++++++ src/vnsw/agent/uve/vm_stat_kvm.h | 35 ++++ src/vnsw/agent/uve/vm_uve_table.cc | 22 +- 12 files changed, 614 insertions(+), 290 deletions(-) create mode 100644 src/vnsw/agent/uve/vm_stat_docker.cc create mode 100644 src/vnsw/agent/uve/vm_stat_docker.h create mode 100644 src/vnsw/agent/uve/vm_stat_kvm.cc create mode 100644 src/vnsw/agent/uve/vm_stat_kvm.h diff --git a/src/vnsw/agent/cmn/agent.cc b/src/vnsw/agent/cmn/agent.cc index 727b89e51c9..18f131a79c8 100644 --- a/src/vnsw/agent/cmn/agent.cc +++ b/src/vnsw/agent/cmn/agent.cc @@ -70,6 +70,14 @@ bool Agent::isXenMode() { return params_->isXenMode(); } +bool Agent::isKvmMode() { + return params_->isKvmMode(); +} + +bool Agent::isDockerMode() { + return params_->isDockerMode(); +} + static void SetTaskPolicyOne(const char *task, const char *exclude_list[], int count) { TaskScheduler *scheduler = TaskScheduler::GetInstance(); diff --git a/src/vnsw/agent/cmn/agent.h b/src/vnsw/agent/cmn/agent.h index 871e0acbba2..41552cb8b43 100644 --- a/src/vnsw/agent/cmn/agent.h +++ b/src/vnsw/agent/cmn/agent.h @@ -792,6 +792,8 @@ class Agent { AgentParam *params() const { return params_; } bool isXenMode(); + bool isKvmMode(); + bool isDockerMode(); // Agent param accessor functions bool isVmwareMode() const; bool isVmwareVcenterMode() const; diff --git a/src/vnsw/agent/init/agent_param.cc b/src/vnsw/agent/init/agent_param.cc index 3dc2eecb960..148d300d841 100644 --- a/src/vnsw/agent/init/agent_param.cc +++ b/src/vnsw/agent/init/agent_param.cc @@ -334,6 +334,8 @@ void AgentParam::ParseHypervisor() { hypervisor_mode_ = AgentParam::MODE_VMWARE; GetValueFromTree(vmware_physical_port_, "HYPERVISOR.vmware_physical_interface"); + } else if (opt_str.get() == "docker") { + hypervisor_mode_ = AgentParam::MODE_DOCKER; } else { hypervisor_mode_ = AgentParam::MODE_KVM; } diff --git a/src/vnsw/agent/init/agent_param.h b/src/vnsw/agent/init/agent_param.h index a2100993e2b..74aacaed93d 100644 --- a/src/vnsw/agent/init/agent_param.h +++ b/src/vnsw/agent/init/agent_param.h @@ -34,6 +34,7 @@ class AgentParam { MODE_KVM, MODE_XEN, MODE_VMWARE, + MODE_DOCKER, }; enum VmwareMode { @@ -155,6 +156,7 @@ class AgentParam { HypervisorMode mode() const { return hypervisor_mode_; } bool isXenMode() const { return hypervisor_mode_ == MODE_XEN; } bool isKvmMode() const { return hypervisor_mode_ == MODE_KVM; } + bool isDockerMode() const { return hypervisor_mode_ == MODE_DOCKER; } bool isVmwareMode() const { return hypervisor_mode_ == MODE_VMWARE; } bool isVmwareVcenterMode() const { return vmware_mode_ == VCENTER; } VmwareMode vmware_mode() const { return vmware_mode_; } diff --git a/src/vnsw/agent/uve/SConscript b/src/vnsw/agent/uve/SConscript index c756794ac09..79d6fa6ce92 100644 --- a/src/vnsw/agent/uve/SConscript +++ b/src/vnsw/agent/uve/SConscript @@ -113,6 +113,8 @@ libstatsuve = env.Library('statsuve', 'interface_uve_stats_table.cc', 'stats_manager.cc', 'vm_stat.cc', + 'vm_stat_kvm.cc', + 'vm_stat_docker.cc', 'vm_uve_entry.cc', 'vm_uve_table.cc', 'vn_uve_entry.cc', diff --git a/src/vnsw/agent/uve/vm_stat.cc b/src/vnsw/agent/uve/vm_stat.cc index ed3bdff473e..264795f9586 100644 --- a/src/vnsw/agent/uve/vm_stat.cc +++ b/src/vnsw/agent/uve/vm_stat.cc @@ -113,138 +113,6 @@ void VmStat::ExecCmd(std::string cmd, DoneCb cb) { placeholders::bytes_transferred, cb)); } -void VmStat::ReadCpuStat() { - std::string tmp; - //Typical output from command - //Id: 1 - //Name: instance-00000001 - //UUID: 90cb7351-d2dc-4d8d-a216-2f460be183b6 - //OS Type: hvm - //State: running - //CPU(s): 1 - //CPU time: 13.4s - //Max memory: 2097152 KiB - - //Get 'CPU time' from the output - double cpu_stat = 0; - std::string cpu_stat_str; - while (data_ >> tmp) { - if (tmp == "time:") { - data_ >> cpu_stat_str; - break; - } - } - - //Remove the last character from 'cpu_stat_str' - if (cpu_stat_str.size() >= 2) { - cpu_stat_str.erase(cpu_stat_str.size() - 1); - //Convert string to double - stringstream ss(cpu_stat_str); - ss >> cpu_stat; - } - - time_t now; - time(&now); - if (prev_cpu_snapshot_time_) { - cpu_usage_ = (cpu_stat - prev_cpu_stat_)/ - difftime(now, prev_cpu_snapshot_time_); - cpu_usage_ *= 100; - } - - prev_cpu_stat_ = cpu_stat; - prev_cpu_snapshot_time_ = now; - - //Clear buffer - data_.str(" "); - data_.clear(); - - //Trigger a request to start vcpu stat collection - GetVcpuStat(); -} - -void VmStat::ReadVcpuStat() { - std::string tmp; - uint32_t index = 0; - std::vector vcpu_usage; - //Read latest VCPU usage time - while(data_ >> tmp) { - if (tmp == "VCPU:") { - //Current VCPU index - data_ >> index; - } - - if (tmp == "time:") { - double usage = 0; - data_ >> usage; - vcpu_usage.push_back(usage); - } - } - - vcpu_usage_percent_.clear(); - if (prev_vcpu_usage_.size() != vcpu_usage.size()) { - //In case a new VCPU get added - prev_vcpu_usage_ = vcpu_usage; - } - - time_t now; - time(&now); - //Calculate VCPU usage - if (prev_vcpu_snapshot_time_) { - for (uint32_t i = 0; i < vcpu_usage.size(); i++) { - double cpu_usage = (vcpu_usage[i] - prev_vcpu_usage_[i])/ - difftime(now, prev_vcpu_snapshot_time_); - cpu_usage *= 100; - vcpu_usage_percent_.push_back(cpu_usage); - } - } - - prev_vcpu_usage_ = vcpu_usage; - prev_vcpu_snapshot_time_ = now; - - data_.str(" "); - data_.clear(); - //Trigger a request to start mem stat - GetMemStat(); -} - -void VmStat::ReadMemStat() { - if (pid_) { - std::ostringstream proc_file; - proc_file << "/proc/"<> tmp; vm >> virt_memory_; - vmsize = true; - } - if (line.find("VmRSS") != std::string::npos) { - std::stringstream vm(line); - std::string tmp; - vm >> tmp; - vm >> mem_usage_; - rss = true; - } - if (line.find("VmPeak") != std::string::npos) { - std::stringstream vm(line); - std::string tmp; vm >> tmp; vm >> virt_memory_peak_; - peak = true; - } - if (rss && vmsize && peak) - break; - } - } - - data_.str(" "); - data_.clear(); - GetDiskName(); -} - bool VmStat::BuildVmStatsMsg(VirtualMachineStats *uve) { uve->set_name(UuidToString(vm_uuid_)); @@ -282,30 +150,6 @@ bool VmStat::BuildVmMsg(UveVirtualMachineAgent *uve) { return true; } -void VmStat::GetCpuStat() { - std::ostringstream cmd; - cmd << "virsh dominfo " << agent_->GetUuidStr(vm_uuid_); - ExecCmd(cmd.str(), boost::bind(&VmStat::ReadCpuStat, this)); -} - -void VmStat::GetVcpuStat() { - std::ostringstream cmd; - cmd << "virsh vcpuinfo " << agent_->GetUuidStr(vm_uuid_); - ExecCmd(cmd.str(), boost::bind(&VmStat::ReadVcpuStat, this)); -} - -void VmStat::GetMemStat() { - ReadMemStat(); -} - -void VmStat::GetDiskName() { - std::ostringstream cmd; - cmd << "virsh domblklist " << agent_->GetUuidStr(vm_uuid_) << " | grep " - << agent_->GetUuidStr(vm_uuid_); - ExecCmd(cmd.str(), boost::bind(&VmStat::ReadDiskName, this)); -} - - void VmStat::SendVmCpuStats() { //We need to send same cpu info in two different UVEs //(VirtualMachineStats and UveVirtualMachineAgent). One of them uses @@ -326,78 +170,7 @@ void VmStat::SendVmCpuStats() { } } -void VmStat::ReadDiskName() { - data_ >> disk_name_; - if (!disk_name_.empty()) { - GetDiskStat(); - } else { - SendVmCpuStats(); - StartTimer(); - } -} - -void VmStat::GetDiskStat() { - std::ostringstream cmd; - cmd << "virsh domblkinfo " << agent_->GetUuidStr(vm_uuid_) << " " - << disk_name_; - ExecCmd(cmd.str(), boost::bind(&VmStat::ReadDiskStat, this)); -} - -void VmStat::ReadDiskStat() { - bool disk_size_found = false, virtual_size_found = false; - std::string tmp; - std::string virtual_size_str, disk_size_str; - while (data_ >> tmp) { - if (tmp == "Capacity:") { - data_ >> virtual_size_str; - virtual_size_found = true; - } else if (tmp == "Allocation:") { - data_ >> disk_size_str; - disk_size_found = true; - } - if (virtual_size_found && disk_size_found) { - break; - } - } - if (virtual_size_str.size() >= 2) { - //Convert string to uint32_t - stringstream ss(virtual_size_str); - ss >> virtual_size_; - } - - if (disk_size_str.size() >= 2) { - //Convert string to uint32_t - stringstream ss(disk_size_str); - ss >> disk_size_; - } - - SendVmCpuStats(); - StartTimer(); -} - -void VmStat::ReadMemoryQuota() { - std::string tmp; - while (data_ >> tmp) { - if (tmp == "actual") { - data_ >> vm_memory_quota_; - } - } - GetCpuStat(); -} - -void VmStat::GetMemoryQuota() { - std::ostringstream cmd; - cmd << "virsh dommemstat " << agent_->GetUuidStr(vm_uuid_); - ExecCmd(cmd.str(), boost::bind(&VmStat::ReadMemoryQuota, this)); -} - bool VmStat::TimerExpiry() { - if (pid_ == 0) { - GetPid(); - } else { - //Get CPU and memory stats - GetCpuStat(); - } return false; } @@ -406,46 +179,7 @@ void VmStat::StartTimer() { timer_->Start(kTimeout, boost::bind(&VmStat::TimerExpiry, this)); } -void VmStat::ReadPid() { - std::string tmp; - uint32_t pid; - - while (data_) { - data_ >> pid; - data_ >> tmp; - if (tmp.find("qemu") != std::string::npos || - tmp.find("kvm") != std::string::npos) { - //Copy PID - pid_ = pid; - break; - } - //Flush out this line - data_.ignore(512, '\n'); - } - - data_.str(" "); - data_.clear(); - if (pid_) { - //Successfully read pid of process, collect other data - GetMemoryQuota(); - } else { - retry_++; - //Retry after timeout - if (retry_ < kRetryCount) { - StartTimer(); - } - } -} - -void VmStat::GetPid() { - std::ostringstream cmd; - cmd << "ps -eo pid,cmd | grep " << agent_->GetUuidStr(vm_uuid_) - << " | grep instance-"; - ExecCmd(cmd.str(), boost::bind(&VmStat::ReadPid, this)); -} - void VmStat::Start() { - GetPid(); } void VmStat::Stop() { diff --git a/src/vnsw/agent/uve/vm_stat.h b/src/vnsw/agent/uve/vm_stat.h index 581078e7d7d..190f58074f7 100644 --- a/src/vnsw/agent/uve/vm_stat.h +++ b/src/vnsw/agent/uve/vm_stat.h @@ -24,35 +24,22 @@ class VmStat { typedef boost::function DoneCb; VmStat(Agent *agent, const boost::uuids::uuid &vm_uuid); - ~VmStat(); + virtual ~VmStat(); bool marked_delete() const { return marked_delete_; } - void Start(); + virtual void Start(); void Stop(); - void HandleSigChild(const boost::system::error_code& error, int sig); void ProcessData(); private: bool BuildVmStatsMsg(VirtualMachineStats *uve); bool BuildVmMsg(UveVirtualMachineAgent *uve); - void ReadCpuStat(); - void ReadVcpuStat(); - void ReadMemStat(); - void ReadDiskStat(); - void ReadDiskName(); - void GetCpuStat(); - void GetVcpuStat(); - void GetMemStat(); - void GetDiskName(); - void GetDiskStat(); void ReadData(const boost::system::error_code &ec, size_t read_bytes, DoneCb &cb); - void ExecCmd(std::string cmd, DoneCb cb); + virtual bool TimerExpiry(); + +protected: void StartTimer(); - bool TimerExpiry(); - void GetPid(); - void ReadPid(); - void ReadMemoryQuota(); - void GetMemoryQuota(); + void ExecCmd(std::string cmd, DoneCb cb); void SendVmCpuStats(); Agent *agent_; diff --git a/src/vnsw/agent/uve/vm_stat_docker.cc b/src/vnsw/agent/uve/vm_stat_docker.cc new file mode 100644 index 00000000000..43915346266 --- /dev/null +++ b/src/vnsw/agent/uve/vm_stat_docker.cc @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::uuids; +using namespace boost::asio; + +VmStatDocker::VmStatDocker(Agent *agent, const uuid &vm_uuid) + : VmStat(agent, vm_uuid), container_id_() { +} + +VmStatDocker::~VmStatDocker() { +} + +void VmStatDocker::Start() { + if (vm_uuid_ == nil_uuid()) { + return; + } + GetContainerId(); +} + +void VmStatDocker::ReadCpuStat() { + double cpu_stat = 0; + std::string cpu_stat_str; + data_ >> cpu_stat_str; + + if (!cpu_stat_str.empty()) { + //Convert string to double + stringstream ss(cpu_stat_str); + ss >> cpu_stat; + //Convert from Nanoseconds to seconds + cpu_stat = cpu_stat / kOneSecInNanoSecs; + } + + time_t now; + time(&now); + if (prev_cpu_snapshot_time_) { + cpu_usage_ = (cpu_stat - prev_cpu_stat_)/ + difftime(now, prev_cpu_snapshot_time_); + cpu_usage_ *= 100; + } + + prev_cpu_stat_ = cpu_stat; + prev_cpu_snapshot_time_ = now; + + //Clear buffer + data_.str(" "); + data_.clear(); + + //Trigger a request to start Memory stat collection + GetMemoryQuota(); +} + +void VmStatDocker::ReadMemStat() { + if (pid_) { + std::ostringstream proc_file; + proc_file << "/proc/"<> tmp; vm >> virt_memory_; + vmsize = true; + } + if (line.find("VmRSS") != std::string::npos) { + std::stringstream vm(line); + std::string tmp; + vm >> tmp; + vm >> mem_usage_; + rss = true; + } + if (line.find("VmPeak") != std::string::npos) { + std::stringstream vm(line); + std::string tmp; vm >> tmp; vm >> virt_memory_peak_; + peak = true; + } + if (rss && vmsize && peak) + break; + } + } + + data_.str(" "); + data_.clear(); + + SendVmCpuStats(); + StartTimer(); +} + +void VmStatDocker::GetCpuStat() { + std::ostringstream cmd; + cmd << "cat /sys/fs/cgroup/cpuacct/docker/" << container_id_ + << "/cpuacct.usage"; + ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadCpuStat, this)); +} + +void VmStatDocker::GetMemoryQuota() { + std::ostringstream cmd; + cmd << "cat /sys/fs/cgroup/memory/docker/" << container_id_ + << "/memory.stat"; + ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadMemoryQuota, this)); +} + +void VmStatDocker::GetMemStat() { + ReadMemStat(); +} + +void VmStatDocker::GetPid() { + std::ostringstream cmd; + cmd << "docker inspect " << container_id_ + << "| grep \\\"Pid\\\": | awk '{print $2}'"; + ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadPid, this)); +} + +void VmStatDocker::ReadPid() { + std::string pid_str; + data_ >> pid_str; + //Remove the last character from 'pid_str' + if (pid_str.size() >= 2) { + pid_str.erase(pid_str.size() - 1); + //Convert string to uint32_t + stringstream ss(pid_str); + ss >> pid_; + } + + data_.str(" "); + data_.clear(); + GetMemStat(); +} + +void VmStatDocker::ReadMemoryQuota() { + std::string tmp; + while (data_ >> tmp) { + if (tmp == "hierarchical_memory_limit") { + data_ >> vm_memory_quota_; + /* Convert the 'vm_memory_quota_' to KiB to make it consistent with + Kvm's 'vm_memory_quota_' */ + vm_memory_quota_/= 1024; + break; + } + } + + data_.str(" "); + data_.clear(); + GetPid(); +} + +bool VmStatDocker::TimerExpiry() { + if (container_id_.empty()) { + GetContainerId(); + } else { + //Get CPU and memory stats + GetCpuStat(); + } + return false; +} + +void VmStatDocker::ReadContainerId() { + data_ >> container_id_; + //Clear buffer + data_.str(" "); + data_.clear(); + + if (!container_id_.empty()) { + //Successfully read container-id, collect other data + GetCpuStat(); + } else { + retry_++; + //Retry after timeout + if (retry_ < kRetryCount) { + StartTimer(); + } + } +} + +void VmStatDocker::GetContainerId() { + std::ostringstream cmd; + cmd << "docker ps --no-trunc | grep " << agent_->GetUuidStr(vm_uuid_) + << " | awk '{ print $1 }'"; + ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadContainerId, this)); +} diff --git a/src/vnsw/agent/uve/vm_stat_docker.h b/src/vnsw/agent/uve/vm_stat_docker.h new file mode 100644 index 00000000000..c9a5494a350 --- /dev/null +++ b/src/vnsw/agent/uve/vm_stat_docker.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. + */ + +#ifndef vnsw_agent_vm_stat_docker_h +#define vnsw_agent_vm_stat_docker_h + +#include "vm_stat.h" + +class VmStatDocker : public VmStat { +public: + VmStatDocker(Agent *agent, const boost::uuids::uuid &vm_uuid); + ~VmStatDocker(); + static const long kOneSecInNanoSecs = 1000000000; + + void Start(); +private: + void ReadContainerId(); + void GetContainerId(); + void ReadCpuStat(); + void GetCpuStat(); + void ReadMemoryQuota(); + void GetMemoryQuota(); + void ReadMemStat(); + void GetMemStat(); + bool TimerExpiry(); + void GetPid(); + void ReadPid(); + + std::string container_id_; + + DISALLOW_COPY_AND_ASSIGN(VmStatDocker); +}; +#endif // vnsw_agent_vm_stat_docker_h diff --git a/src/vnsw/agent/uve/vm_stat_kvm.cc b/src/vnsw/agent/uve/vm_stat_kvm.cc new file mode 100644 index 00000000000..593eb576ac0 --- /dev/null +++ b/src/vnsw/agent/uve/vm_stat_kvm.cc @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::uuids; +using namespace boost::asio; + +VmStatKvm::VmStatKvm(Agent *agent, const uuid &vm_uuid) + : VmStat(agent, vm_uuid) { +} + +VmStatKvm::~VmStatKvm() { +} + +void VmStatKvm::ReadCpuStat() { + std::string tmp; + //Typical output from command + //Id: 1 + //Name: instance-00000001 + //UUID: 90cb7351-d2dc-4d8d-a216-2f460be183b6 + //OS Type: hvm + //State: running + //CPU(s): 1 + //CPU time: 13.4s + //Max memory: 2097152 KiB + + //Get 'CPU time' from the output + double cpu_stat = 0; + std::string cpu_stat_str; + while (data_ >> tmp) { + if (tmp == "time:") { + data_ >> cpu_stat_str; + break; + } + } + + //Remove the last character from 'cpu_stat_str' + if (cpu_stat_str.size() >= 2) { + cpu_stat_str.erase(cpu_stat_str.size() - 1); + //Convert string to double + stringstream ss(cpu_stat_str); + ss >> cpu_stat; + } + + time_t now; + time(&now); + if (prev_cpu_snapshot_time_) { + cpu_usage_ = (cpu_stat - prev_cpu_stat_)/ + difftime(now, prev_cpu_snapshot_time_); + cpu_usage_ *= 100; + } + + prev_cpu_stat_ = cpu_stat; + prev_cpu_snapshot_time_ = now; + + //Clear buffer + data_.str(" "); + data_.clear(); + + //Trigger a request to start vcpu stat collection + GetVcpuStat(); +} + +void VmStatKvm::ReadVcpuStat() { + std::string tmp; + uint32_t index = 0; + std::vector vcpu_usage; + //Read latest VCPU usage time + while(data_ >> tmp) { + if (tmp == "VCPU:") { + //Current VCPU index + data_ >> index; + } + + if (tmp == "time:") { + double usage = 0; + data_ >> usage; + vcpu_usage.push_back(usage); + } + } + + vcpu_usage_percent_.clear(); + if (prev_vcpu_usage_.size() != vcpu_usage.size()) { + //In case a new VCPU get added + prev_vcpu_usage_ = vcpu_usage; + } + + time_t now; + time(&now); + //Calculate VCPU usage + if (prev_vcpu_snapshot_time_) { + for (uint32_t i = 0; i < vcpu_usage.size(); i++) { + double cpu_usage = (vcpu_usage[i] - prev_vcpu_usage_[i])/ + difftime(now, prev_vcpu_snapshot_time_); + cpu_usage *= 100; + vcpu_usage_percent_.push_back(cpu_usage); + } + } + + prev_vcpu_usage_ = vcpu_usage; + prev_vcpu_snapshot_time_ = now; + + data_.str(" "); + data_.clear(); + //Trigger a request to start mem stat + GetMemStat(); +} + +void VmStatKvm::ReadMemStat() { + if (pid_) { + std::ostringstream proc_file; + proc_file << "/proc/"<> tmp; vm >> virt_memory_; + vmsize = true; + } + if (line.find("VmRSS") != std::string::npos) { + std::stringstream vm(line); + std::string tmp; + vm >> tmp; + vm >> mem_usage_; + rss = true; + } + if (line.find("VmPeak") != std::string::npos) { + std::stringstream vm(line); + std::string tmp; vm >> tmp; vm >> virt_memory_peak_; + peak = true; + } + if (rss && vmsize && peak) + break; + } + } + + data_.str(" "); + data_.clear(); + GetDiskName(); +} + +void VmStatKvm::GetCpuStat() { + std::ostringstream cmd; + cmd << "virsh dominfo " << agent_->GetUuidStr(vm_uuid_); + ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadCpuStat, this)); +} + +void VmStatKvm::GetVcpuStat() { + std::ostringstream cmd; + cmd << "virsh vcpuinfo " << agent_->GetUuidStr(vm_uuid_); + ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadVcpuStat, this)); +} + +void VmStatKvm::GetMemStat() { + ReadMemStat(); +} + +void VmStatKvm::GetDiskName() { + std::ostringstream cmd; + cmd << "virsh domblklist " << agent_->GetUuidStr(vm_uuid_) << " | grep " + << agent_->GetUuidStr(vm_uuid_); + ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadDiskName, this)); +} + +void VmStatKvm::ReadDiskName() { + data_ >> disk_name_; + if (!disk_name_.empty()) { + GetDiskStat(); + } else { + SendVmCpuStats(); + StartTimer(); + } +} + +void VmStatKvm::GetDiskStat() { + std::ostringstream cmd; + cmd << "virsh domblkinfo " << agent_->GetUuidStr(vm_uuid_) << " " + << disk_name_; + ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadDiskStat, this)); +} + +void VmStatKvm::ReadDiskStat() { + bool disk_size_found = false, virtual_size_found = false; + std::string tmp; + std::string virtual_size_str, disk_size_str; + while (data_ >> tmp) { + if (tmp == "Capacity:") { + data_ >> virtual_size_str; + virtual_size_found = true; + } else if (tmp == "Allocation:") { + data_ >> disk_size_str; + disk_size_found = true; + } + if (virtual_size_found && disk_size_found) { + break; + } + } + if (virtual_size_str.size() >= 2) { + //Convert string to uint32_t + stringstream ss(virtual_size_str); + ss >> virtual_size_; + } + + if (disk_size_str.size() >= 2) { + //Convert string to uint32_t + stringstream ss(disk_size_str); + ss >> disk_size_; + } + + SendVmCpuStats(); + StartTimer(); +} + +void VmStatKvm::ReadMemoryQuota() { + std::string tmp; + while (data_ >> tmp) { + if (tmp == "actual") { + data_ >> vm_memory_quota_; + } + } + GetCpuStat(); +} + +void VmStatKvm::GetMemoryQuota() { + std::ostringstream cmd; + cmd << "virsh dommemstat " << agent_->GetUuidStr(vm_uuid_); + ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadMemoryQuota, this)); +} + +bool VmStatKvm::TimerExpiry() { + if (pid_ == 0) { + GetPid(); + } else { + //Get CPU and memory stats + GetCpuStat(); + } + return false; +} + +void VmStatKvm::ReadPid() { + std::string tmp; + uint32_t pid; + + while (data_) { + data_ >> pid; + data_ >> tmp; + if (tmp.find("qemu") != std::string::npos || + tmp.find("kvm") != std::string::npos) { + //Copy PID + pid_ = pid; + break; + } + //Flush out this line + data_.ignore(512, '\n'); + } + + data_.str(" "); + data_.clear(); + if (pid_) { + //Successfully read pid of process, collect other data + GetMemoryQuota(); + } else { + retry_++; + //Retry after timeout + if (retry_ < kRetryCount) { + StartTimer(); + } + } +} + +void VmStatKvm::GetPid() { + std::ostringstream cmd; + cmd << "ps -eo pid,cmd | grep " << agent_->GetUuidStr(vm_uuid_) + << " | grep instance-"; + ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadPid, this)); +} + +void VmStatKvm::Start() { + GetPid(); +} diff --git a/src/vnsw/agent/uve/vm_stat_kvm.h b/src/vnsw/agent/uve/vm_stat_kvm.h new file mode 100644 index 00000000000..f28dd409ac0 --- /dev/null +++ b/src/vnsw/agent/uve/vm_stat_kvm.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. + */ + +#ifndef vnsw_agent_vm_stat_kvm_h +#define vnsw_agent_vm_stat_kvm_h + +#include "vm_stat.h" + +class VmStatKvm : public VmStat { +public: + VmStatKvm(Agent *agent, const boost::uuids::uuid &vm_uuid); + ~VmStatKvm(); + + void Start(); +private: + void ReadCpuStat(); + void ReadVcpuStat(); + void ReadMemStat(); + void ReadDiskStat(); + void ReadDiskName(); + void GetCpuStat(); + void GetVcpuStat(); + void GetMemStat(); + void GetDiskName(); + void GetDiskStat(); + bool TimerExpiry(); + void GetPid(); + void ReadPid(); + void ReadMemoryQuota(); + void GetMemoryQuota(); + + DISALLOW_COPY_AND_ASSIGN(VmStatKvm); +}; +#endif // vnsw_agent_vm_stat_kvm_h diff --git a/src/vnsw/agent/uve/vm_uve_table.cc b/src/vnsw/agent/uve/vm_uve_table.cc index 07822aef1c4..d5abe590219 100644 --- a/src/vnsw/agent/uve/vm_uve_table.cc +++ b/src/vnsw/agent/uve/vm_uve_table.cc @@ -6,6 +6,8 @@ #include #include #include +#include +#include VmUveTable::VmUveTable(Agent *agent, uint32_t default_intvl) : VmUveTableBase(agent, default_intvl) { @@ -73,14 +75,24 @@ void VmUveTable::SendVmStatsMsg(const boost::uuids::uuid &u) { void VmUveTable::VmStatCollectionStart(VmUveVmState *state, const VmEntry *vm) { //Create object to poll for VM stats - VmStat *stat = new VmStat(agent_, vm->GetUuid()); - stat->Start(); - state->stat_ = stat; + VmStat *stat = NULL; + if (agent_->isKvmMode()) { + stat = new VmStatKvm(agent_, vm->GetUuid()); + } else if (agent_->isDockerMode()) { + stat = new VmStatDocker(agent_, vm->GetUuid()); + } + + if (stat) { + stat->Start(); + state->stat_ = stat; + } } void VmUveTable::VmStatCollectionStop(VmUveVmState *state) { - state->stat_->Stop(); - state->stat_ = NULL; + if (state->stat_) { + state->stat_->Stop(); + state->stat_ = NULL; + } } void VmUveTable::EnqueueVmStatData(VmStatData *data) {