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