Line data Source code
1 : /* 2 : * Copyright (c) 2015 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_docker.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 : VmStatDocker::VmStatDocker(Agent *agent, const uuid &vm_uuid) 28 0 : : VmStat(agent, vm_uuid), container_id_() { 29 0 : } 30 : 31 0 : VmStatDocker::~VmStatDocker() { 32 0 : } 33 : 34 0 : void VmStatDocker::Start() { 35 0 : if (vm_uuid_ == nil_uuid()) { 36 0 : return; 37 : } 38 0 : GetContainerId(); 39 : } 40 : 41 0 : void VmStatDocker::ReadCpuStat() { 42 0 : double cpu_stat = 0; 43 0 : std::string cpu_stat_str; 44 0 : data_ >> cpu_stat_str; 45 : 46 0 : if (!cpu_stat_str.empty()) { 47 : //Convert string to double 48 0 : std::stringstream ss(cpu_stat_str); 49 0 : ss >> cpu_stat; 50 : //Convert from Nanoseconds to seconds 51 0 : cpu_stat = cpu_stat / kOneSecInNanoSecs; 52 0 : } 53 : 54 : time_t now; 55 0 : time(&now); 56 0 : if (prev_cpu_snapshot_time_) { 57 0 : cpu_usage_ = (cpu_stat - prev_cpu_stat_)/ 58 0 : difftime(now, prev_cpu_snapshot_time_); 59 0 : cpu_usage_ *= 100; 60 : } 61 : 62 0 : prev_cpu_stat_ = cpu_stat; 63 0 : prev_cpu_snapshot_time_ = now; 64 : 65 : //Clear buffer 66 0 : data_.str(" "); 67 0 : data_.clear(); 68 : 69 : //Trigger a request to start Memory stat collection 70 0 : GetMemoryQuota(); 71 0 : } 72 : 73 0 : void VmStatDocker::ReadMemStat() { 74 0 : if (pid_) { 75 0 : std::ostringstream proc_file; 76 0 : proc_file << "/proc/"<<pid_<<"/status"; 77 0 : std::ifstream file(proc_file.str().c_str()); 78 : 79 0 : bool vmsize = false; 80 0 : bool rss = false; 81 0 : bool peak = false; 82 0 : std::string line; 83 0 : while (std::getline(file, line)) { 84 0 : if (line.find("VmSize") != std::string::npos) { 85 0 : std::stringstream vm(line); 86 0 : std::string tmp; vm >> tmp; vm >> virt_memory_; 87 0 : vmsize = true; 88 0 : } 89 0 : if (line.find("VmRSS") != std::string::npos) { 90 0 : std::stringstream vm(line); 91 0 : std::string tmp; 92 0 : vm >> tmp; 93 0 : vm >> mem_usage_; 94 0 : rss = true; 95 0 : } 96 0 : if (line.find("VmPeak") != std::string::npos) { 97 0 : std::stringstream vm(line); 98 0 : std::string tmp; vm >> tmp; vm >> virt_memory_peak_; 99 0 : peak = true; 100 0 : } 101 0 : if (rss && vmsize && peak) 102 0 : break; 103 : } 104 0 : } 105 : 106 0 : data_.str(" "); 107 0 : data_.clear(); 108 : 109 0 : SendVmCpuStats(); 110 0 : StartTimer(); 111 0 : } 112 : 113 0 : void VmStatDocker::GetCpuStat() { 114 0 : std::ostringstream cmd; 115 0 : cmd << "cat /sys/fs/cgroup/cpuacct/docker/" << container_id_ 116 0 : << "/cpuacct.usage"; 117 0 : ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadCpuStat, this)); 118 0 : } 119 : 120 0 : void VmStatDocker::GetMemoryQuota() { 121 0 : std::ostringstream cmd; 122 0 : cmd << "cat /sys/fs/cgroup/memory/docker/" << container_id_ 123 0 : << "/memory.stat"; 124 0 : ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadMemoryQuota, this)); 125 0 : } 126 : 127 0 : void VmStatDocker::GetMemStat() { 128 0 : ReadMemStat(); 129 0 : } 130 : 131 0 : void VmStatDocker::GetPid() { 132 0 : std::ostringstream cmd; 133 0 : cmd << "docker inspect " << container_id_ << "| grep -A3 \\\"Paused\\\":"; 134 0 : ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadPid, this)); 135 0 : } 136 : 137 0 : void VmStatDocker::ReadPid() { 138 : /* Expecting data_ to have the following content 139 : * "Paused": false, 140 : * "Pid": 24430, 141 : * "Restarting": false, 142 : * "Running": true, 143 : */ 144 0 : string tmp, paused_str, pid_str, running_str; 145 0 : while (data_ >> tmp) { 146 0 : if (tmp.find("Paused") != std::string::npos) { 147 0 : data_ >> paused_str; 148 : } 149 0 : if (tmp.find("Pid") != std::string::npos) { 150 0 : data_ >> pid_str; 151 : } 152 0 : if (tmp.find("Running") != std::string::npos) { 153 0 : data_ >> running_str; 154 0 : break; 155 : } 156 : } 157 : 158 : //Remove the last character from 'pid_str' 159 0 : if (pid_str.size() >= 2) { 160 0 : pid_str.erase(pid_str.size() - 1); 161 : //Convert string to uint32_t 162 0 : std::stringstream ss(pid_str); 163 0 : ss >> pid_; 164 0 : } 165 : 166 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_UNKNOWN; 167 0 : if (paused_str == "true,") { 168 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_PAUSED; 169 0 : } else if (running_str == "false,") { 170 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_SHUTDOWN; 171 0 : } else if (running_str == "true,") { 172 0 : vm_state_ = VrouterAgentVmState::VROUTER_AGENT_VM_ACTIVE; 173 : } 174 0 : data_.str(" "); 175 0 : data_.clear(); 176 0 : GetMemStat(); 177 0 : } 178 : 179 0 : void VmStatDocker::ReadMemoryQuota() { 180 0 : std::string tmp; 181 0 : while (data_ >> tmp) { 182 0 : if (tmp == "hierarchical_memory_limit") { 183 0 : data_ >> vm_memory_quota_; 184 : /* Convert the 'vm_memory_quota_' to KiB to make it consistent with 185 : Kvm's 'vm_memory_quota_' */ 186 0 : vm_memory_quota_/= 1024; 187 0 : break; 188 : } 189 : } 190 : 191 0 : data_.str(" "); 192 0 : data_.clear(); 193 0 : GetPid(); 194 0 : } 195 : 196 0 : bool VmStatDocker::TimerExpiry() { 197 0 : if (container_id_.empty()) { 198 0 : GetContainerId(); 199 : } else { 200 : //Get CPU and memory stats 201 0 : GetCpuStat(); 202 : } 203 0 : return false; 204 : } 205 : 206 0 : void VmStatDocker::ReadContainerId() { 207 0 : data_ >> container_id_; 208 : //Clear buffer 209 0 : data_.str(" "); 210 0 : data_.clear(); 211 : 212 0 : if (!container_id_.empty()) { 213 : //Successfully read container-id, collect other data 214 0 : GetCpuStat(); 215 : } else { 216 0 : retry_++; 217 : //Retry after timeout 218 0 : if (retry_ < kRetryCount) { 219 0 : StartTimer(); 220 : } 221 : } 222 0 : } 223 : 224 0 : void VmStatDocker::GetContainerId() { 225 0 : std::ostringstream cmd; 226 0 : cmd << "docker ps --no-trunc | grep " << agent_->GetUuidStr(vm_uuid_) 227 0 : << " | awk '{ print $1 }'"; 228 0 : ExecCmd(cmd.str(), boost::bind(&VmStatDocker::ReadContainerId, this)); 229 0 : }