Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : 5 : #include <sys/times.h> 6 : #include <sys/types.h> 7 : #include <sys/wait.h> 8 : #include <unistd.h> 9 : #include <uve/vm_stat_kvm.h> 10 : #include <uve/vm_stat_data.h> 11 : #include <db/db.h> 12 : #include <db/db_entry.h> 13 : #include <db/db_table.h> 14 : #include <base/address.h> 15 : #include <ifmap/ifmap_agent_parser.h> 16 : #include <cmn/agent.h> 17 : #include <uve/vrouter_uve_entry.h> 18 : #include <sstream> 19 : #include <fstream> 20 : #include <uve/agent_uve.h> 21 : #include <uve/vm_uve_table.h> 22 : 23 : using namespace boost::uuids; 24 : using namespace boost::asio; 25 : 26 0 : VmStatKvm::VmStatKvm(Agent *agent, const uuid &vm_uuid) 27 0 : : VmStat(agent, vm_uuid) { 28 0 : } 29 : 30 0 : VmStatKvm::~VmStatKvm() { 31 0 : } 32 : 33 0 : void VmStatKvm::ReadCpuStat() { 34 0 : std::string tmp; 35 : //Typical output from command 36 : //Id: 1 37 : //Name: instance-00000001 38 : //UUID: 90cb7351-d2dc-4d8d-a216-2f460be183b6 39 : //OS Type: hvm 40 : //State: running 41 : //CPU(s): 1 42 : //CPU time: 13.4s 43 : //Max memory: 2097152 KiB 44 : 45 : //Get 'CPU time' from the output 46 0 : double cpu_stat = 0; 47 0 : std::string cpu_stat_str, state_str, cpu_count_str; 48 : 49 0 : while (data_ >> tmp) { 50 0 : if (tmp == "State:") { 51 0 : data_ >> state_str; 52 : } 53 0 : if (tmp == "CPU(s):") { 54 0 : data_ >> cpu_count_str; 55 : } 56 0 : if (tmp == "time:") { 57 0 : data_ >> cpu_stat_str; 58 : /* We expect 'State' and 'CPU(s)' fields to be present before 59 : * 'CPU time' field. So break from the loop when we are done with 60 : * reading of 'CPU time' field. */ 61 0 : break; 62 : } 63 : } 64 : 65 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_UNKNOWN; 66 0 : if (state_str.size()) { 67 0 : if (state_str == "running") { 68 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_ACTIVE; 69 0 : } else if (state_str == "paused") { 70 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_PAUSED; 71 0 : } else if (state_str == "shut") { 72 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_SHUTDOWN; 73 : } 74 : } 75 0 : vm_cpu_count_ = kInvalidCpuCount; 76 0 : if (cpu_count_str.size()) { 77 0 : stringToInteger(cpu_count_str, vm_cpu_count_); 78 : } 79 : //Remove the last character from 'cpu_stat_str' 80 0 : if (cpu_stat_str.size() >= 2) { 81 0 : cpu_stat_str.erase(cpu_stat_str.size() - 1); 82 : //Convert string to double 83 0 : std::stringstream ss(cpu_stat_str); 84 0 : ss >> cpu_stat; 85 0 : } 86 : 87 : time_t now; 88 0 : time(&now); 89 0 : if (prev_cpu_snapshot_time_) { 90 0 : cpu_usage_ = (cpu_stat - prev_cpu_stat_)/ 91 0 : difftime(now, prev_cpu_snapshot_time_); 92 0 : cpu_usage_ *= 100; 93 : } 94 : 95 0 : prev_cpu_stat_ = cpu_stat; 96 0 : prev_cpu_snapshot_time_ = now; 97 : 98 : //Clear buffer 99 0 : data_.str(" "); 100 0 : data_.clear(); 101 : 102 : //Trigger a request to start vcpu stat collection 103 0 : GetVcpuStat(); 104 0 : } 105 : 106 0 : void VmStatKvm::ReadVcpuStat() { 107 0 : std::string tmp; 108 0 : uint32_t index = 0; 109 0 : std::vector<double> vcpu_usage; 110 : //Read latest VCPU usage time 111 0 : while(data_ >> tmp) { 112 0 : if (tmp == "VCPU:") { 113 : //Current VCPU index 114 0 : data_ >> index; 115 : } 116 : 117 0 : if (tmp == "time:") { 118 0 : double usage = 0; 119 0 : data_ >> usage; 120 0 : vcpu_usage.push_back(usage); 121 : } 122 : } 123 : 124 0 : vcpu_usage_percent_.clear(); 125 0 : if (prev_vcpu_usage_.size() != vcpu_usage.size()) { 126 : //In case a new VCPU get added 127 0 : prev_vcpu_usage_ = vcpu_usage; 128 : } 129 : 130 : time_t now; 131 0 : time(&now); 132 : //Calculate VCPU usage 133 0 : if (prev_vcpu_snapshot_time_) { 134 0 : for (uint32_t i = 0; i < vcpu_usage.size(); i++) { 135 0 : double cpu_usage = (vcpu_usage[i] - prev_vcpu_usage_[i])/ 136 0 : difftime(now, prev_vcpu_snapshot_time_); 137 0 : cpu_usage *= 100; 138 0 : vcpu_usage_percent_.push_back(cpu_usage); 139 : } 140 : } 141 : 142 0 : prev_vcpu_usage_ = vcpu_usage; 143 0 : prev_vcpu_snapshot_time_ = now; 144 : 145 0 : data_.str(" "); 146 0 : data_.clear(); 147 : //Trigger a request to start mem stat 148 0 : GetMemStat(); 149 0 : } 150 : 151 0 : void VmStatKvm::ReadMemStat() { 152 0 : if (pid_) { 153 0 : std::ostringstream proc_file; 154 0 : proc_file << "/proc/"<<pid_<<"/status"; 155 0 : std::ifstream file(proc_file.str().c_str()); 156 : 157 0 : bool vmsize = false; 158 0 : bool peak = false; 159 0 : bool rss = false; 160 0 : std::string line; 161 0 : while (std::getline(file, line)) { 162 0 : if (line.find("VmSize") != std::string::npos) { 163 0 : std::stringstream vm(line); 164 0 : std::string tmp; vm >> tmp; vm >> virt_memory_; 165 0 : vmsize = true; 166 0 : } 167 0 : if (line.find("VmRSS") != std::string::npos) { 168 0 : std::stringstream vm(line); 169 0 : std::string tmp; 170 0 : vm >> tmp; 171 0 : vm >> mem_usage_; 172 0 : rss = true; 173 0 : } 174 0 : if (line.find("VmPeak") != std::string::npos) { 175 0 : std::stringstream vm(line); 176 0 : std::string tmp; vm >> tmp; vm >> virt_memory_peak_; 177 0 : peak = true; 178 0 : } 179 0 : if (rss && vmsize && peak) 180 0 : break; 181 : } 182 0 : } 183 : 184 0 : data_.str(" "); 185 0 : data_.clear(); 186 0 : GetDiskName(); 187 0 : } 188 : 189 0 : void VmStatKvm::GetCpuStat() { 190 0 : std::ostringstream cmd; 191 0 : cmd << "virsh dominfo " << agent_->GetUuidStr(vm_uuid_); 192 0 : ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadCpuStat, this)); 193 0 : } 194 : 195 0 : void VmStatKvm::GetVcpuStat() { 196 0 : std::ostringstream cmd; 197 0 : cmd << "virsh vcpuinfo " << agent_->GetUuidStr(vm_uuid_); 198 0 : ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadVcpuStat, this)); 199 0 : } 200 : 201 0 : void VmStatKvm::GetMemStat() { 202 0 : ReadMemStat(); 203 0 : } 204 : 205 0 : void VmStatKvm::GetDiskName() { 206 0 : std::ostringstream cmd; 207 0 : cmd << "virsh domblklist " << agent_->GetUuidStr(vm_uuid_) << " | grep " 208 0 : << agent_->GetUuidStr(vm_uuid_); 209 0 : ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadDiskName, this)); 210 0 : } 211 : 212 0 : void VmStatKvm::ReadDiskName() { 213 0 : data_ >> disk_name_; 214 0 : if (!disk_name_.empty()) { 215 0 : GetDiskStat(); 216 : } else { 217 0 : SendVmCpuStats(); 218 0 : StartTimer(); 219 : } 220 0 : } 221 : 222 0 : void VmStatKvm::GetDiskStat() { 223 0 : std::ostringstream cmd; 224 0 : cmd << "virsh domblkinfo " << agent_->GetUuidStr(vm_uuid_) << " " 225 0 : << disk_name_; 226 0 : ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadDiskStat, this)); 227 0 : } 228 : 229 0 : void VmStatKvm::ReadDiskStat() { 230 0 : bool disk_size_found = false, virtual_size_found = false; 231 0 : std::string tmp; 232 0 : std::string virtual_size_str, disk_size_str; 233 0 : while (data_ >> tmp) { 234 0 : if (tmp == "Capacity:") { 235 0 : data_ >> virtual_size_str; 236 0 : virtual_size_found = true; 237 0 : } else if (tmp == "Allocation:") { 238 0 : data_ >> disk_size_str; 239 0 : disk_size_found = true; 240 : } 241 0 : if (virtual_size_found && disk_size_found) { 242 0 : break; 243 : } 244 : } 245 0 : if (virtual_size_str.size() >= 2) { 246 : //Convert string to uint32_t 247 0 : std::stringstream ss(virtual_size_str); 248 0 : ss >> virtual_size_; 249 0 : } 250 : 251 0 : if (disk_size_str.size() >= 2) { 252 : //Convert string to uint32_t 253 0 : std::stringstream ss(disk_size_str); 254 0 : ss >> disk_size_; 255 0 : } 256 : 257 0 : SendVmCpuStats(); 258 0 : StartTimer(); 259 0 : } 260 : 261 0 : void VmStatKvm::ReadMemoryQuota() { 262 0 : std::string tmp; 263 0 : while (data_ >> tmp) { 264 0 : if (tmp == "actual") { 265 0 : data_ >> vm_memory_quota_; 266 : } 267 : } 268 0 : GetCpuStat(); 269 0 : } 270 : 271 0 : void VmStatKvm::GetMemoryQuota() { 272 0 : std::ostringstream cmd; 273 0 : cmd << "virsh dommemstat " << agent_->GetUuidStr(vm_uuid_); 274 0 : ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadMemoryQuota, this)); 275 0 : } 276 : 277 0 : bool VmStatKvm::TimerExpiry() { 278 0 : if (pid_ == 0) { 279 0 : GetPid(); 280 : } else { 281 : //Get CPU and memory stats 282 0 : GetCpuStat(); 283 : } 284 0 : return false; 285 : } 286 : 287 0 : void VmStatKvm::ReadPid() { 288 0 : std::string tmp; 289 : uint32_t pid; 290 : 291 0 : while (data_) { 292 0 : data_ >> pid; 293 0 : data_ >> tmp; 294 0 : if (tmp.find("qemu") != std::string::npos || 295 0 : tmp.find("kvm") != std::string::npos) { 296 : //Copy PID 297 0 : pid_ = pid; 298 0 : break; 299 : } 300 : //Flush out this line 301 0 : data_.ignore(512, '\n'); 302 : } 303 : 304 0 : data_.str(" "); 305 0 : data_.clear(); 306 0 : if (pid_) { 307 : //Successfully read pid of process, collect other data 308 0 : GetMemoryQuota(); 309 : } else { 310 0 : retry_++; 311 : //Retry after timeout 312 0 : if (retry_ < kRetryCount) { 313 0 : StartTimer(); 314 : } 315 : } 316 0 : } 317 : 318 0 : void VmStatKvm::GetPid() { 319 0 : std::ostringstream cmd; 320 0 : cmd << "ps -eo pid,cmd | grep " << agent_->GetUuidStr(vm_uuid_) 321 0 : << " | grep instance-"; 322 0 : ExecCmd(cmd.str(), boost::bind(&VmStatKvm::ReadPid, this)); 323 0 : } 324 : 325 0 : void VmStatKvm::Start() { 326 0 : GetPid(); 327 0 : }