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