/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.cloud.network.resource; import java.util.Map; import java.util.UUID; import javax.naming.ConfigurationException; import javax.ws.rs.core.MultivaluedMap; import org.apache.log4j.Logger; import org.libvirt.LibvirtException; import org.midonet.client.MidonetApi; import org.midonet.client.resource.Bridge; import org.midonet.client.resource.BridgePort; import org.midonet.client.resource.Host; import com.sun.jersey.core.util.MultivaluedMapImpl; import com.cloud.agent.api.to.NicTO; import com.cloud.exception.InternalErrorException; import com.cloud.hypervisor.kvm.resource.LibvirtVMDef; import com.cloud.hypervisor.kvm.resource.VifDriverBase; import com.cloud.network.Networks; import com.cloud.utils.NumbersUtil; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; public class MidoNetVifDriver extends VifDriverBase { private static final Logger s_logger = Logger.getLogger(MidoNetVifDriver.class); private int _timeout; private String _midoApiLocation = "http://localhost:8081/"; private static final String midoPostfix = "mnet"; @Override public void configure(Map<String, Object> params) throws ConfigurationException { super.configure(params); String value = (String)params.get("scripts.timeout"); _timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000; // Load Midonet API server location String midoLoc = (String)params.get("midonet.apiserver.address"); if (midoLoc != null) { _midoApiLocation = midoLoc; } } /* * Grab our host id in a file written by Midonet, then * return a Host. */ private Host getMyHost(MidonetApi api) { Script command = new Script("/bin/bash", _timeout); command.add("-c"); command.add("awk -F'=' '{if ($1~/host/) print $2}' /etc/midolman/host_uuid.properties"); OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); command.execute(parser); String host_uuid = parser.getLines().split("\\n")[0]; for (Host host : api.getHosts()) { if (host.getId().toString().equals(host_uuid)) { return host; } } return null; } /* * simple script to add the tap to the host and bring it up. */ private String addTap() { OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); Script command = new Script("/bin/bash", _timeout); command.add("-c"); command.add("ip tuntap add mode tap dev '%d" + midoPostfix + "' && ip link | grep " + midoPostfix + " | sort -n | tail -1 | awk -F': ' '{print $2}'"); command.execute(parser); String tapName = parser.getLines().split("\\n")[0]; command = new Script("/bin/bash", _timeout); command.add("-c"); command.add("ip link set " + tapName + " up"); command.execute(); return tapName; } @Override public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicAdapter) throws InternalErrorException, LibvirtException { if (s_logger.isDebugEnabled()) { s_logger.debug("nic=" + nic); } LibvirtVMDef.InterfaceDef intf = new LibvirtVMDef.InterfaceDef(); String trafficLabel = nic.getName(); if (nic.getBroadcastType() == Networks.BroadcastDomainType.Mido && (nic.getType() == Networks.TrafficType.Guest || nic.getType() == Networks.TrafficType.Public)) { /* * create the tap. */ String tapName = addTap(); /* * grab the tenant id and the network id from the Broadcast URI. * We need to pluck the values out of the String. The string * should look like "mido://[tenant_id].[bridge_name]" */ MultivaluedMap qNet = new MultivaluedMapImpl(); String nicAuthority = nic.getBroadcastUri().getAuthority(); String tenantId = nicAuthority.split("\\.")[0]; qNet.add("tenant_id", tenantId); String url = nicAuthority.split("\\.")[1]; String netName = url.split(":")[0]; MidonetApi api = new MidonetApi(_midoApiLocation); api.enableLogging(); for (Bridge b : api.getBridges(qNet)) { if (b.getName().equals(netName)) { for (BridgePort p : b.getPorts()) { UUID pvif = p.getVifId(); if (pvif != null && p.getVifId().toString().equals(nic.getUuid())) { getMyHost(api).addHostInterfacePort().interfaceName(tapName).portId(p.getId()).create(); break; } } } } intf.defEthernet(tapName, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter), ""); } else { throw new InternalErrorException("Only NICs of BroadcastDomain type Mido are supported by the MidoNetVifDriver"); } return intf; } @Override public void unplug(LibvirtVMDef.InterfaceDef iface) { String netName = iface.getBrName(); if (netName != null && netName.contains(midoPostfix)) { Script command = new Script("/bin/bash", _timeout); command.add("-c"); command.add("ip tuntap del " + iface.getBrName() + " mode tap"); command.execute(); } } }