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