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.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 <init/agent_param.h>
19 : #include <uve/vrouter_uve_entry.h>
20 : #include <sstream>
21 : #include <fstream>
22 : #include <uve/agent_uve.h>
23 : #include <uve/vm_uve_table.h>
24 :
25 : using namespace boost::uuids;
26 : using namespace boost::asio;
27 :
28 0 : VmStat::VmStat(Agent *agent, const uuid &vm_uuid):
29 0 : agent_(agent), vm_uuid_(vm_uuid), mem_usage_(0),
30 0 : virt_memory_(0), virt_memory_peak_(0), vm_memory_quota_(0),
31 0 : prev_cpu_stat_(0), cpu_usage_(0),
32 0 : prev_cpu_snapshot_time_(0), prev_vcpu_snapshot_time_(0),
33 0 : input_(*(agent_->event_manager()->io_service())),
34 0 : timer_(TimerManager::CreateTimer(*(agent_->event_manager())->io_service(),
35 0 : "VmStatTimer")), marked_delete_(false), pid_(0), retry_(0), virtual_size_(0),
36 0 : disk_size_(0), disk_name_(),
37 0 : vm_state_(VrouterAgentVmState::VROUTER_AGENT_VM_UNKNOWN),
38 0 : prev_vm_state_(VrouterAgentVmState::VROUTER_AGENT_VM_UNKNOWN),
39 0 : vm_cpu_count_(kInvalidCpuCount), prev_vm_cpu_count_(kInvalidCpuCount) {
40 0 : }
41 :
42 0 : VmStat::~VmStat() {
43 0 : TimerManager::DeleteTimer(timer_);
44 0 : }
45 :
46 0 : void VmStat::ReadData(const boost::system::error_code &ec,
47 : size_t read_bytes, DoneCb &cb) {
48 0 : if (read_bytes) {
49 0 : data_<< rx_buff_;
50 : }
51 :
52 0 : if (ec) {
53 0 : boost::system::error_code close_ec;
54 0 : input_.close(close_ec);
55 0 : call_back_ = cb;
56 : //Enqueue a request to process data
57 0 : VmStatData *vm_stat_data = new VmStatData(this);
58 :
59 : VmUveTable *vmt = static_cast<VmUveTable *>
60 0 : (agent_->uve()->vm_uve_table());
61 0 : vmt->EnqueueVmStatData(vm_stat_data);
62 : } else {
63 0 : bzero(rx_buff_, sizeof(rx_buff_));
64 0 : async_read(input_, boost::asio::buffer(rx_buff_, kBufLen),
65 0 : boost::bind(&VmStat::ReadData, this, placeholders::error,
66 : placeholders::bytes_transferred, cb));
67 : }
68 0 : }
69 :
70 0 : void VmStat::ProcessData() {
71 0 : if (!call_back_.empty())
72 0 : call_back_();
73 0 : }
74 :
75 0 : void VmStat::ExecCmd(std::string cmd, DoneCb cb) {
76 : char *argv[4];
77 0 : char shell[80] = "/bin/sh";
78 0 : char option[80] = "-c";
79 : char ccmd[256];
80 0 : memset(ccmd, 0, sizeof(ccmd));
81 0 : strncpy(ccmd, cmd.c_str(), sizeof(ccmd)-1);
82 :
83 0 : argv[0] = shell;
84 0 : argv[1] = option;
85 0 : argv[2] = ccmd;
86 0 : argv[3] = 0;
87 :
88 : int out[2];
89 0 : if (pipe(out) < 0) {
90 0 : return;
91 : }
92 :
93 0 : if (vfork() == 0) {
94 : //Close read end of pipe
95 0 : close(out[0]);
96 0 : dup2(out[1], STDOUT_FILENO);
97 : //Close out[1] as stdout is a exact replica of out[1]
98 0 : close(out[1]);
99 :
100 : /* Close all the open fds before execvp */
101 0 : CloseTaskFds();
102 0 : execvp(argv[0], argv);
103 0 : perror("execvp");
104 0 : exit(127);
105 : }
106 :
107 : //Close write end of pipe
108 0 : close(out[1]);
109 :
110 0 : boost::system::error_code ec;
111 0 : int fd = ::dup(out[0]);
112 0 : close(out[0]);
113 0 : if (fd == -1) {
114 0 : return;
115 : }
116 0 : input_.assign(fd, ec);
117 0 : if (ec) {
118 0 : close(fd);
119 0 : return;
120 : }
121 :
122 0 : bzero(rx_buff_, sizeof(rx_buff_));
123 0 : async_read(input_, boost::asio::buffer(rx_buff_, kBufLen),
124 0 : boost::bind(&VmStat::ReadData, this, placeholders::error,
125 : placeholders::bytes_transferred, cb));
126 : }
127 :
128 0 : bool VmStat::BuildVmStatsMsg(VirtualMachineStats *uve) {
129 0 : uve->set_name(UuidToString(vm_uuid_));
130 :
131 0 : std::vector<VmCpuStats> cpu_stats_list;
132 0 : VmCpuStats stats;
133 0 : stats.set_cpu_one_min_avg(cpu_usage_);
134 0 : stats.set_vm_memory_quota(vm_memory_quota_);
135 0 : stats.set_rss(mem_usage_);
136 0 : stats.set_virt_memory(virt_memory_);
137 0 : stats.set_peak_virt_memory(virt_memory_peak_);
138 0 : stats.set_disk_allocated_bytes(virtual_size_);
139 0 : stats.set_disk_used_bytes(disk_size_);
140 :
141 :
142 0 : cpu_stats_list.push_back(stats);
143 0 : uve->set_cpu_stats(cpu_stats_list);
144 :
145 0 : return true;
146 0 : }
147 :
148 0 : bool VmStat::BuildVmMsg(UveVirtualMachineAgent *uve) {
149 0 : uve->set_name(UuidToString(vm_uuid_));
150 :
151 0 : VmCpuStats stats;
152 0 : stats.set_cpu_one_min_avg(cpu_usage_);
153 0 : stats.set_vm_memory_quota(vm_memory_quota_);
154 0 : stats.set_rss(mem_usage_);
155 0 : stats.set_virt_memory(virt_memory_);
156 0 : stats.set_peak_virt_memory(virt_memory_peak_);
157 0 : stats.set_disk_allocated_bytes(virtual_size_);
158 0 : stats.set_disk_used_bytes(disk_size_);
159 :
160 0 : uve->set_cpu_info(stats);
161 :
162 0 : vnsConstants vns;
163 0 : if (vm_state_ != VrouterAgentVmState::VROUTER_AGENT_VM_UNKNOWN) {
164 0 : if (vm_state_ != prev_vm_state_) {
165 0 : uve->set_vm_state(vns.VrouterAgentVmStateMap.at(vm_state_));
166 0 : prev_vm_state_ = vm_state_;
167 : }
168 : }
169 :
170 0 : if (vm_cpu_count_ != kInvalidCpuCount) {
171 0 : if (vm_cpu_count_ != prev_vm_cpu_count_) {
172 0 : uve->set_vm_cpu_count(vm_cpu_count_);
173 0 : prev_vm_cpu_count_ = vm_cpu_count_;
174 : }
175 : }
176 :
177 0 : return true;
178 0 : }
179 :
180 0 : void VmStat::SendVmCpuStats() {
181 : //We need to send same cpu info in two different UVEs
182 : //(VirtualMachineStats and UveVirtualMachineAgent). One of them uses
183 : //stats-oracle infra and other one does not use it. We need two because
184 : //stats-oracle infra returns only SUM of cpu-info over a period of time
185 : //and current value is returned using non-stats-oracle version. Using
186 : //stats oracle infra we can still query the current value but for simpler
187 : //interface we are sending current value in separate UVE.
188 : //Also the non-stats oracle version has additional fields of vm_state and
189 : //vm_cpu_count which are not sent in stats-oracle version.
190 0 : VirtualMachineStats vm_agent;
191 0 : if (BuildVmStatsMsg(&vm_agent)) {
192 : VmUveTable *vmt = static_cast<VmUveTable *>
193 0 : (agent_->uve()->vm_uve_table());
194 0 : vmt->DispatchVmStatsMsg(vm_agent);
195 : }
196 0 : UveVirtualMachineAgent vm_msg;
197 0 : if (BuildVmMsg(&vm_msg)) {
198 0 : agent_->uve()->vm_uve_table()->DispatchVmMsg(vm_msg);
199 : }
200 0 : }
201 :
202 0 : bool VmStat::TimerExpiry() {
203 0 : return false;
204 : }
205 :
206 0 : void VmStat::StartTimer() {
207 0 : timer_->Cancel();
208 0 : timer_->Start(agent_->params()->vmi_vm_vn_uve_interval_msecs(),
209 : boost::bind(&VmStat::TimerExpiry, this));
210 0 : }
211 :
212 0 : void VmStat::Start() {
213 0 : }
214 :
215 0 : void VmStat::Stop() {
216 0 : marked_delete_ = true;
217 0 : if (timer_->running() || retry_ == kRetryCount) {
218 : //If timer is fired, then we are in middle of
219 : //vm stat collection, in such case dont delete the vm stat
220 : //entry as asio may be using it
221 0 : delete this;
222 : }
223 0 : }
|