OpenSDN source code
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
libvirt_instance_adapter.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Codilime
3  */
4 
5 #include <arpa/inet.h>
6 #include <errno.h>
7 #include <libvirt/libvirt.h>
8 #include <libvirt/virterror.h>
9 #include <net/if.h>
10 #include <linux/if_tun.h>
11 #include <boost/lexical_cast.hpp>
12 #include <boost/uuid/uuid_io.hpp>
13 #include <pugixml/pugixml.hpp>
14 #include <fstream>
15 #include "cfg/cfg_init.h"
17 #include "oper/service_instance.h"
18 #include "oper/instance_task.h"
19 #include "oper/interface_common.h"
20 #include "base/address.h"
21 #include "base/logging.h"
22 
23 using pugi::xml_document;
24 using pugi::xml_node;
25 using pugi::xml_attribute;
26 using pugi::xml_parse_result;
27 
29 
30 static bool close_descriptor(int fd) {
31  while (close(fd) < 0) {
32  if (errno != EINTR) {
33  LOG(ERROR, "Could not close descriptor, errno: " << errno);
34  return false;
35  }
36  }
37  return true;
38 }
39 
40 static bool alloc_tap_interface(const char *devname, int flags) {
41  struct ifreq ifr;
42  int fd;
43  LOG(DEBUG, "Allocating TAP device " << devname);
44 
45  while ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
46  if (errno != EINTR) {
47  LOG(ERROR, "Cannot open /dev/net/tun");
48  return false;
49  }
50  }
51 
52  memset(&ifr, 0, sizeof(ifr));
53  ifr.ifr_flags = flags;
54  strncpy(ifr.ifr_name, devname, IFNAMSIZ-1);
55 
56  if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
57  close_descriptor(fd);
58  LOG(ERROR, "Error creating tap interface "
59  << devname << ", errno: " << errno);
60  return false;
61  }
62  if (ioctl(fd, TUNSETPERSIST, 1) < 0) {
63  close_descriptor(fd);
64  LOG(ERROR, "Error setting persistent tap interface "
65  << devname << ", errno: " << errno);
66  return false;
67  }
68  close_descriptor(fd);
69  LOG(DEBUG, "Created device: " << ifr.ifr_name);
70 
71  ifr.ifr_flags = IFF_UP;
72  fd = socket(AF_INET, SOCK_DGRAM, 0);
73  if (fd < 0) {
74  LOG(ERROR, "Could not open AF_INET socket, errno: " << errno);
75  return false;
76  }
77  ioctl(fd, SIOCSIFFLAGS, &ifr);
78  close_descriptor(fd);
79  return true;
80 }
81 
82 static bool destroy_tap_interface(const char *devname) {
83  struct ifreq ifr;
84  int fd;
85 
86  while ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
87  if (errno != EINTR) {
88  LOG(ERROR, "Cannot open /dev/net/tun");
89  return false;
90  }
91  }
92 
93  memset(&ifr, 0, sizeof(ifr));
94  ifr.ifr_flags = IFF_TAP;
95  strncpy(ifr.ifr_name, devname, IFNAMSIZ-1);
96 
97  if (ioctl(fd, TUNSETIFF, (void *)&ifr) < 0) {
98  close_descriptor(fd);
99  LOG(ERROR, "Error opening tap interface "
100  << devname << ", errno: " << errno);
101  return false;
102  }
103  if (ioctl(fd, TUNSETPERSIST, 0) < 0) {
104  close_descriptor(fd);
105  LOG(ERROR, "Error destroying tap interface "
106  << devname << ", errno: " << errno);
107  return false;
108  }
109  close_descriptor(fd);
110  return true;
111 }
112 
113 static xml_node get_or_create_node(xml_node *parent,
114  const std::string &child_name) {
115  xml_node child = parent->child(child_name.c_str());
116  if (child)
117  return child;
118  return parent->append_child(child_name.c_str());
119 }
120 
122  CloseConnection();
123 }
124 
126  const ServiceInstance::Properties &si_properties, bool update) {
127  LOG(DEBUG, "creating libvirt instance start task");
128  if (!EnsureConnected())
129  return NULL;
130 
131  return new LibvirtInstanceAdapter::DomainStartTask(this, si_properties,
132  update);
133 }
134 
136  const ServiceInstance::Properties &si_properties) {
137  LOG(DEBUG, "creating libvirt instance stop task");
138  if (!EnsureConnected())
139  return NULL;
140 
141  return new LibvirtInstanceAdapter::DomainStopTask(this, si_properties);
142 }
143 
145  const ServiceInstance::Properties &props) {
146  LOG(DEBUG, "checked whether libvirt is "
147  "applicable for chosen service instance");
150 }
151 
153  is_running_ = true;
154  std::string dom_uuid_str =
155  boost::lexical_cast<std::string>(si_properties_.instance_id);
156 
157  // Domain configuration can't be updated w/o restarting anyway.
159 
160  // create a transient domain
161  std::string xml = XmlConf();
162  if (xml.empty()) {
163  LOG(ERROR, "Could not parse domain XML");
164  return false;
165  }
166  LOG(DEBUG, "Creating domain: " << xml);
167 
168  if (!CreateTAPInterfaces(dom_uuid_str)) {
169  LOG(ERROR, "Error creating TAP interfaces");
170  return false;
171  }
172 
173  virDomainPtr dom = virDomainCreateXML(parent_adapter_->conn_,
174  xml.c_str(), 0);
175  if (dom == NULL) {
176  LOG(ERROR, "Error creating domain: " << virGetLastErrorMessage());
177  return false;
178  }
180 
181  virDomainFree(dom);
182  LOG(DEBUG, "Domain created: " << dom_uuid_str);
183  return true;
184 }
185 
187  is_running_ = true;
188  std::string dom_uuid_str =
189  boost::lexical_cast<std::string>(si_properties_.instance_id);
190  parent_adapter_->EnsureDestroyed(dom_uuid_str, si_properties_);
191  return true;
192 }
193 
195  std::string dom_uuid_str =
196  boost::lexical_cast<std::string>(si_properties_.instance_id);
197  // make a pugixml config document out of si's instance data
198  xml_document domain_xml_conf;
199  xml_parse_result parse_result =
200  domain_xml_conf.load(si_properties_.instance_data.c_str());
201  if (!parse_result || !domain_xml_conf.child("domain")) {
202  LOG(ERROR, "Error parsing XML data or domain is missing");
203  return "";
204  }
205 
206  // make sure devices node exists
207  xml_node devices_node = domain_xml_conf.child("domain").child("devices");
208  if (!devices_node)
209  domain_xml_conf.child("domain").append_child("devices");
210 
211  // modify and save as a string for libvirt
212  DomainXMLAssignUUID(dom_uuid_str, domain_xml_conf);
213 
214  // interfaces defined by Contrail (left, right, management)
215  DomainXMLSetInterfaceData(domain_xml_conf, dom_uuid_str);
216 
217  std::stringstream domain_conf;
218  domain_xml_conf.save(domain_conf);
219  return domain_conf.str();
220 }
221 
223  const std::string &dom_uuid_str,
224  const xml_document &libvirt_xml_conf) {
225  xml_node domain_node = libvirt_xml_conf.child("domain");
226  xml_node uuid_node = get_or_create_node(&domain_node, "uuid");
227  xml_node name_node = get_or_create_node(&domain_node, "name");
228  if (name_node.text().empty())
229  name_node.text().set("contrail_si-");
230  uuid_node.text().set(dom_uuid_str.c_str());
231  name_node.text().set((std::string(name_node.text().get()) + "-" +
232  dom_uuid_str.substr(0, 5)).c_str());
233  LOG(DEBUG, dom_uuid_str.c_str());
234 }
235 
237  const std::string &dom_uuid) {
238  // to be deprecated. Agent code currently supports only 3 interfaces/dom.
239  std::string left = LibvirtInstanceAdapter::GenIntfName(dom_uuid, 'l');
240  std::string right = LibvirtInstanceAdapter::GenIntfName(dom_uuid, 'r');
241  std::string mgmt = LibvirtInstanceAdapter::GenIntfName(dom_uuid, 'm');
242 
243  return alloc_tap_interface(left.c_str(), IFF_TAP) &&
244  alloc_tap_interface(right.c_str(), IFF_TAP) &&
245  alloc_tap_interface(mgmt.c_str(), IFF_TAP);
246 }
247 
249  const xml_document &libvirt_xml_conf, const std::string &dom_uuid) {
250  LOG(DEBUG, "adding vrouter interface "
251  "data to libvirt instance configuration");
252  // to be deprecated. Agent code currently supports only 3 interfaces/dom.
253  xml_node devices_node = libvirt_xml_conf.child("domain").child("devices");
254 
255  DomainXMLAddInterface(&devices_node,
256  si_properties_.mac_addr_inside,
257  LibvirtInstanceAdapter::GenIntfName(dom_uuid, 'l'));
258  DomainXMLAddInterface(&devices_node,
259  si_properties_.mac_addr_outside,
260  LibvirtInstanceAdapter::GenIntfName(dom_uuid, 'r'));
261  DomainXMLAddInterface(&devices_node,
262  si_properties_.mac_addr_management,
263  LibvirtInstanceAdapter::GenIntfName(dom_uuid, 'm'));
264 }
265 
267  xml_node *devices_node, const std::string &mac_addr,
268  const std::string &intf_name) {
269  xml_node intf_node = devices_node->append_child("interface");
270  intf_node.append_attribute("type").set_value("ethernet");
271 
272  xml_node mac_node = intf_node.append_child("mac");
273  mac_node.append_attribute("address").set_value(mac_addr.c_str());
274 
275  xml_node target_node = intf_node.append_child("target");
276  target_node.append_attribute("dev").set_value(intf_name.c_str());
277 }
278 
280  const std::string &dom_uuid, char type) {
281  return std::string("tap_" + dom_uuid.substr(0, 8) + type);
282 }
283 
285  // gets called from InstanceTask threads
286  tbb::mutex::scoped_lock lock(conn_mutex_);
287 
288  LOG(DEBUG, "ensuring we have a libvirt connection");
289  if (conn_ == NULL) {
290  conn_ = virConnectOpen(libvirt_conn_addr_.c_str());
291  if (conn_ == NULL)
292  return false;
293  }
294  return true;
295 }
296 
298  const std::string &dom_uuid_str,
299  const ServiceInstance::Properties &si_properties) {
300  virDomainPtr dom = virDomainLookupByUUIDString(conn_, dom_uuid_str.c_str());
301  if (dom != NULL) {
302  std::string domain_name = std::string(virDomainGetName(dom));
303  virDomainDestroy(dom);
304  virDomainFree(dom);
305  }
306  std::string left = LibvirtInstanceAdapter::GenIntfName(dom_uuid_str, 'l');
307  std::string right = LibvirtInstanceAdapter::GenIntfName(dom_uuid_str, 'r');
308  std::string mgmt = LibvirtInstanceAdapter::GenIntfName(dom_uuid_str, 'm');
309  destroy_tap_interface(left.c_str());
310  destroy_tap_interface(right.c_str());
311  destroy_tap_interface(mgmt.c_str());
312  UnregisterInterfaces(si_properties);
313 }
314 
316  const ServiceInstance::Properties &si_properties) {
317  LOG(DEBUG, "registering TAP interfaces to vrouter");
318  std::string dom_uuid_str =
319  boost::lexical_cast<std::string>(si_properties.instance_id);
320  switch (si_properties.interface_count) {
321  case 3: // management interface
323  si_properties.vmi_management,
324  GenIntfName(dom_uuid_str, 'm'),
325  Ip4Address::from_string(
326  si_properties.ip_addr_management),
327  si_properties.mac_addr_management,
328  GenIntfName(dom_uuid_str, 'm'),
329  si_properties.instance_id,
333  Ip6Address(),
336  agent_->cfg()->cfg_interface_client()->FetchInterfaceData(
337  si_properties.vmi_management);
338  // fallover
339  case 2: // right interface
341  si_properties.vmi_outside,
342  GenIntfName(dom_uuid_str, 'r'),
343  Ip4Address::from_string(si_properties.ip_addr_outside),
344  si_properties.mac_addr_outside,
345  GenIntfName(dom_uuid_str, 'r'),
346  si_properties.instance_id,
350  Ip6Address(),
353  agent_->cfg()->cfg_interface_client()->FetchInterfaceData(
354  si_properties.vmi_outside);
355  // fallover
356  case 1: // left interface
358  si_properties.vmi_inside,
359  GenIntfName(dom_uuid_str, 'l'),
360  Ip4Address::from_string(si_properties.ip_addr_inside),
361  si_properties.mac_addr_inside,
362  GenIntfName(dom_uuid_str, 'l'),
363  si_properties.instance_id,
367  Ip6Address(),
370  agent_->cfg()->cfg_interface_client()->FetchInterfaceData(
371  si_properties.vmi_inside);
372  break;
373  default:
374  return false;
375  }
376  return true;
377 }
378 
380  const ServiceInstance::Properties &si_properties) {
381  switch (si_properties.interface_count) {
382  case 3: // management interface
384  si_properties.vmi_inside,
386  // fallover
387  case 2: // right interface
389  si_properties.vmi_outside,
391  // fallover
392  case 1: // left interface
394  si_properties.vmi_management,
396  break;
397  }
398 }
399 
401  tbb::mutex::scoped_lock lock(conn_mutex_);
402  LOG(DEBUG, "closing libvirt connection");
403  if (conn_ != NULL) {
404  virConnectClose(conn_);
405  conn_ = NULL;
406  }
407 }
static bool CreateTAPInterfaces(const std::string &dom_uuid)
InterfaceTable * interface_table() const
Definition: agent.h:465
static std::string GenIntfName(const std::string &dom_uuid, char type)
InstanceTask * CreateStartTask(const ServiceInstance::Properties &props, bool update)
static void DomainXMLAddInterface(pugi::xml_node *devices_node, const std::string &mac_addr, const std::string &intf_name)
void DomainXMLSetInterfaceData(const pugi::xml_document &libvirt_xml_conf, const std::string &dom_uuid)
void EnsureDestroyed(const std::string &dom_uuid_str, const ServiceInstance::Properties &si_properties)
static bool close_descriptor(int fd)
uint8_t type
Definition: load_balance.h:109
static const uint8_t vHostUserClient
Definition: vm_interface.h:363
static const uint32_t kInvalidVlanId
Definition: vm_interface.h:360
boost::asio::ip::address_v6 Ip6Address
Definition: address.h:15
boost::uuids::uuid instance_id
boost::uuids::uuid vmi_outside
boost::uuids::uuid vmi_management
static const std::string & NullString()
Definition: agent.h:437
static xml_node get_or_create_node(xml_node *parent, const std::string &child_name)
bool RegisterInterfaces(const ServiceInstance::Properties &si_properties)
static bool alloc_tap_interface(const char *devname, int flags)
bool isApplicable(const ServiceInstance::Properties &props)
void Delete()
Definition: db_entry.cc:131
static bool destroy_tap_interface(const char *devname)
#define LOG(_Level, _Msg)
Definition: logging.h:33
static void DomainXMLAssignUUID(const std::string &libvirt_conf_str, const pugi::xml_document &libvirt_xml_conf)
void UnregisterInterfaces(const ServiceInstance::Properties &si_properties)
AgentConfig * cfg() const
Definition: agent.cc:865
boost::uuids::uuid vmi_inside
static void NovaAdd(InterfaceTable *table, const boost::uuids::uuid &intf_uuid, const std::string &os_name, const Ip4Address &addr, const std::string &mac, const std::string &vn_name, const boost::uuids::uuid &vm_project_uuid, uint16_t tx_vlan_id, uint16_t rx_vlan_id, const std::string &parent, const Ip6Address &ipv6, uint8_t vhostuser_mode, Interface::Transport transport, uint8_t link_state)
InstanceTask * CreateStopTask(const ServiceInstance::Properties &props)
const ServiceInstance::Properties & si_properties_