Skip to content

Commit 36541a2

Browse files
committed
adding distributed routing support for KVM OVS
some check style error fixes
1 parent 423a748 commit 36541a2

6 files changed

Lines changed: 373 additions & 10 deletions

File tree

plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@
7878
import com.cloud.agent.api.OvsFetchInterfaceAnswer;
7979
import com.cloud.agent.api.OvsFetchInterfaceCommand;
8080
import com.cloud.agent.api.OvsSetupBridgeCommand;
81+
import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand;
82+
import com.cloud.agent.api.OvsVpcRoutingPolicyConfigCommand;
8183
import com.cloud.agent.api.PingCommand;
8284
import com.cloud.agent.api.PingRoutingCommand;
8385
import com.cloud.agent.api.PingRoutingWithNwGroupsCommand;
@@ -1360,6 +1362,10 @@ public Answer executeRequest(Command cmd) {
13601362
return execute((OvsCreateTunnelCommand)cmd);
13611363
} else if (cmd instanceof OvsDestroyTunnelCommand) {
13621364
return execute((OvsDestroyTunnelCommand)cmd);
1365+
} else if (cmd instanceof OvsVpcPhysicalTopologyConfigCommand) {
1366+
return execute((OvsVpcPhysicalTopologyConfigCommand) cmd);
1367+
} else if (cmd instanceof OvsVpcRoutingPolicyConfigCommand) {
1368+
return execute((OvsVpcRoutingPolicyConfigCommand) cmd);
13631369
} else {
13641370
s_logger.warn("Unsupported command ");
13651371
return Answer.createUnsupportedCommandAnswer(cmd);
@@ -1401,6 +1407,47 @@ private Answer execute(OvsDestroyBridgeCommand cmd) {
14011407
return new Answer(cmd, true, null);
14021408
}
14031409

1410+
public Answer execute(OvsVpcPhysicalTopologyConfigCommand cmd) {
1411+
1412+
String bridge = cmd.getBridgeName();
1413+
try {
1414+
Script command = new Script(_ovsTunnelPath, _timeout, s_logger);
1415+
command.add("configure_ovs_bridge_for_network_topology");
1416+
command.add("--bridge", bridge);
1417+
command.add("--config", cmd.getVpcConfigInJson());
1418+
1419+
String result = command.execute();
1420+
if (result.equalsIgnoreCase("SUCCESS")) {
1421+
return new Answer(cmd, true, result);
1422+
} else {
1423+
return new Answer(cmd, false, result);
1424+
}
1425+
} catch (Exception e) {
1426+
s_logger.warn("caught exception while updating host with latest routing polcies", e);
1427+
return new Answer(cmd, false, e.getMessage());
1428+
}
1429+
}
1430+
1431+
public Answer execute(OvsVpcRoutingPolicyConfigCommand cmd) {
1432+
1433+
try {
1434+
Script command = new Script(_ovsTunnelPath, _timeout, s_logger);
1435+
command.add("configure_ovs_bridge_for_routing_policies");
1436+
command.add("--bridge", cmd.getBridgeName());
1437+
command.add("--config", cmd.getVpcConfigInJson());
1438+
1439+
String result = command.execute();
1440+
if (result.equalsIgnoreCase("SUCCESS")) {
1441+
return new Answer(cmd, true, result);
1442+
} else {
1443+
return new Answer(cmd, false, result);
1444+
}
1445+
} catch (Exception e) {
1446+
s_logger.warn("caught exception while updating host with latest VPC topology", e);
1447+
return new Answer(cmd, false, e.getMessage());
1448+
}
1449+
}
1450+
14041451
private synchronized void destroyTunnelNetwork(String bridge) {
14051452
try {
14061453
findOrCreateTunnelNetwork(bridge);

plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@
8888
import org.apache.cloudstack.storage.to.VolumeObjectTO;
8989

9090
import com.cloud.agent.IAgentControl;
91-
9291
import com.cloud.agent.api.Answer;
9392
import com.cloud.agent.api.AttachIsoCommand;
9493
import com.cloud.agent.api.AttachVolumeAnswer;
@@ -149,6 +148,7 @@
149148
import com.cloud.agent.api.OvsSetTagAndFlowCommand;
150149
import com.cloud.agent.api.OvsSetupBridgeCommand;
151150
import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand;
151+
import com.cloud.agent.api.OvsVpcRoutingPolicyConfigCommand;
152152
import com.cloud.agent.api.PerformanceMonitorAnswer;
153153
import com.cloud.agent.api.PerformanceMonitorCommand;
154154
import com.cloud.agent.api.PingCommand;

plugins/network-elements/ovs/src/com/cloud/agent/api/OvsVpcRoutingPolicyConfigCommand.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import com.google.gson.Gson;
2020
import com.google.gson.GsonBuilder;
21-
import java.util.UUID;
2221

2322
/**
2423
* This command represents logical view of VM's connectivity in VPC.

plugins/network-elements/ovs/src/com/cloud/network/ovs/OvsTunnelManagerImpl.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,25 @@
1616
// under the License.
1717
package com.cloud.network.ovs;
1818

19-
import com.amazonaws.services.ec2.model.NetworkAcl;
20-
import com.cloud.agent.api.*;
19+
import com.cloud.agent.api.Answer;
20+
import com.cloud.agent.api.Command;
21+
import com.cloud.agent.api.OvsCreateTunnelAnswer;
22+
import com.cloud.agent.api.OvsCreateTunnelCommand;
23+
import com.cloud.agent.api.OvsDestroyBridgeCommand;
24+
import com.cloud.agent.api.OvsDestroyTunnelCommand;
25+
import com.cloud.agent.api.OvsFetchInterfaceAnswer;
26+
import com.cloud.agent.api.OvsFetchInterfaceCommand;
27+
import com.cloud.agent.api.OvsSetupBridgeCommand;
28+
import com.cloud.agent.api.OvsVpcPhysicalTopologyConfigCommand;
29+
import com.cloud.agent.api.OvsVpcRoutingPolicyConfigCommand;
2130
import com.cloud.network.dao.NetworkDao;
2231
import com.cloud.network.dao.NetworkVO;
23-
import com.cloud.network.vpc.*;
32+
import com.cloud.network.vpc.NetworkACLVO;
33+
import com.cloud.network.vpc.NetworkACLItemDao;
34+
import com.cloud.network.vpc.NetworkACLItemVO;
35+
import com.cloud.network.vpc.dao.VpcDao;
36+
import com.cloud.network.vpc.VpcManager;
37+
import com.cloud.network.vpc.VpcVO;
2438
import com.cloud.network.vpc.dao.NetworkACLDao;
2539
import com.cloud.vm.dao.VMInstanceDao;
2640
import com.cloud.vm.Nic;
@@ -63,7 +77,6 @@
6377
import com.cloud.network.ovs.dao.OvsTunnelNetworkDao;
6478
import com.cloud.network.ovs.dao.OvsTunnelNetworkVO;
6579
import com.cloud.network.ovs.dao.OvsTunnel;
66-
import com.cloud.network.vpc.dao.VpcDao;
6780
import com.cloud.utils.component.ManagerBase;
6881
import com.cloud.utils.concurrency.NamedThreadFactory;
6982
import com.cloud.utils.db.DB;

scripts/vm/network/vnet/cloudstack_pluginlib.py

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def _build_flow_expr(**kwargs):
174174
dl_dst = 'dl_dst' in kwargs and ",dl_dst=%s" % kwargs['dl_dst'] or ''
175175
nw_src = 'nw_src' in kwargs and ",nw_src=%s" % kwargs['nw_src'] or ''
176176
nw_dst = 'nw_dst' in kwargs and ",nw_dst=%s" % kwargs['nw_dst'] or ''
177+
table = 'table' in kwargs and ",table=%s" % kwargs['table'] or ''
177178
proto = 'proto' in kwargs and ",%s" % kwargs['proto'] or ''
178179
ip = ('nw_src' in kwargs or 'nw_dst' in kwargs) and ',ip' or ''
179180
flow = (flow + in_port + dl_type + dl_src + dl_dst +
@@ -217,3 +218,228 @@ def del_all_flows(bridge):
217218
def del_port(bridge, port):
218219
delPort = [VSCTL_PATH, "del-port", bridge, port]
219220
do_cmd(delPort)
221+
222+
223+
def get_network_id_for_vif(vif_name):
224+
domain_id, device_id = vif_name[3:len(vif_name)].split(".")
225+
dom_uuid = do_cmd([XE_PATH, "vm-list", "dom-id=%s" % domain_id, "--minimal"])
226+
vif_uuid = do_cmd([XE_PATH, "vif-list", "vm-uuid=%s" % dom_uuid, "device=%s" % device_id, "--minimal"])
227+
vnet = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=other-config",
228+
"param-key=cloudstack-network-id"])
229+
return vnet
230+
231+
def get_network_id_for_tunnel_port(tunnelif_name):
232+
vnet = do_cmd([VSCTL_PATH, "get", "interface", tunnelif_name, "options:cloudstack-network-id"])
233+
return vnet
234+
235+
def clear_flooding_rules_for_port(bridge, ofport):
236+
del_flows(bridge, in_port=ofport, table=2)
237+
238+
def add_flooding_rules_for_port(bridge, in_ofport, out_ofports):
239+
action = "".join("output:%s," %ofport for ofport in out_ofports)[:-1]
240+
add_flow(bridge, priority=1100, in_port=in_ofport, table=1, actions=action)
241+
242+
def get_ofport_for_vif(vif_name):
243+
return do_cmd([VSCTL_PATH, "get", "interface", vif_name, "ofport"])
244+
245+
def get_macaddress_of_vif(vif_name):
246+
domain_id, device_id = vif_name[3:len(vif_name)].split(".")
247+
dom_uuid = do_cmd([XE_PATH, "vm-list", "dom-id=%s" % domain_id, "--minimal"])
248+
vif_uuid = do_cmd([XE_PATH, "vif-list", "vm-uuid=%s" % dom_uuid, "device=%s" % device_id, "--minimal"])
249+
mac = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=MAC"])
250+
return mac
251+
252+
def get_vif_name_from_macaddress(macaddress):
253+
vif_uuid = do_cmd([XE_PATH, "vif-list", "MAC=%s" % macaddress, "--minimal"])
254+
vif_device_id = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=device"])
255+
vm_uuid = do_cmd([XE_PATH, "vif-param-get", "uuid=%s" % vif_uuid, "param-name=vm-uuid"])
256+
vm_domain_id = do_cmd([XE_PATH, "vm-param-get", "uuid=%s" % vm_uuid, "param-name=dom-id"])
257+
return "vif"+vm_domain_id+"."+vif_device_id
258+
259+
def add_mac_lookup_table_entry(bridge, mac_address, out_of_port):
260+
add_flow(bridge, priority=1100, dl_dst=mac_address, table=1, actions="output:%s" % out_of_port)
261+
262+
def delete_mac_lookup_table_entry(bridge, mac_address):
263+
del_flows(bridge, dl_dst=mac_address, table=1)
264+
265+
def add_ip_lookup_table_entry(bridge, ip, dst_tier_gateway_mac, dst_vm_mac):
266+
action_str = "mod_dl_sr:%s" % dst_tier_gateway_mac + ",mod_dl_dst:%s" % dst_vm_mac +",resubmit(,5)"
267+
addflow = [OFCTL_PATH, "add-flow", bridge, "table=4", "nw_dst=%s" % ip, "actions=%s" %action_str]
268+
do_cmd(addflow)
269+
270+
def get_vms_on_host(vpc, host_id):
271+
all_vms = vpc.vms
272+
vms_on_host = []
273+
for vm in all_vms:
274+
if vm.hostid == host_id:
275+
vms_on_host.append(vm)
276+
return vms_on_host
277+
278+
def get_network_details(vpc, network_uuid):
279+
tiers = vpc.tiers
280+
for tier in tiers:
281+
if tier.networkuuid == network_uuid:
282+
return tier
283+
return None
284+
285+
class jsonLoader(object):
286+
def __init__(self, obj):
287+
for k in obj:
288+
v = obj[k]
289+
if isinstance(v, dict):
290+
setattr(self, k, jsonLoader(v))
291+
elif isinstance(v, (list, tuple)):
292+
if len(v) > 0 and isinstance(v[0], dict):
293+
setattr(self, k, [jsonLoader(elem) for elem in v])
294+
else:
295+
setattr(self, k, v)
296+
else:
297+
setattr(self, k, v)
298+
299+
def __getattr__(self, val):
300+
if val in self.__dict__:
301+
return self.__dict__[val]
302+
else:
303+
return None
304+
305+
def __repr__(self):
306+
return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v)
307+
in self.__dict__.iteritems()))
308+
309+
def __str__(self):
310+
return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v)
311+
in self.__dict__.iteritems()))
312+
313+
def configure_bridge_for_network_topology(bridge, this_host_id, json_config):
314+
vpconfig = jsonLoader(json.loads(json_config)).vpc
315+
316+
if vpconfig is None:
317+
logging.debug("WARNING:Can't find VPC info in json config file")
318+
return "FAILURE:IMPROPER_JSON_CONFG_FILE"
319+
320+
# get the list of Vm's in the VPC from the JSON config
321+
this_host_vms = get_vms_on_host(vpconfig, this_host_id)
322+
323+
for vm in this_host_vms:
324+
for nic in vm.nics:
325+
mac_addr = nic.macaddress
326+
ip = nic.ipaddress
327+
vif_name = get_vif_name_from_macaddress(mac_addr)
328+
of_port = get_ofport_for_vif(vif_name)
329+
network = get_network_details(vpconfig, nic.networkuuid)
330+
331+
# Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet on the found OFPORT
332+
add_mac_lookup_table_entry(bridge, mac_addr, of_port)
333+
334+
# Add flow rule in L3 look up table: if the destination IP = VM's IP then modify the packet
335+
# to set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table
336+
add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr)
337+
338+
# Add flow entry to send with intra tier traffic from the NIC to L2 lookup path)
339+
addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port,
340+
"nw_dst=%s" %network.cidr, "actions=resubmit(,1)"]
341+
do_cmd(addflow)
342+
343+
#add flow entry to send inter-tier traffic from the NIC to egress ACL table(to L3 lookup path)
344+
addflow = [OFCTL_PATH, "add-flow", bridge, "table=0", "in_port=%s" % of_port,
345+
"dl_dst=%s" %network.gatewaymac, "nw_dst=%s" %vpconfig.cidr, "actions=resubmit(,3)"]
346+
do_cmd(addflow)
347+
348+
# get the list of hosts on which VPC spans from the JSON config
349+
vpc_spanning_hosts = vpconfig.hosts
350+
351+
for host in vpc_spanning_hosts:
352+
if this_host_id == host.hostid:
353+
continue
354+
other_host_vms = get_vms_on_host(vpconfig, host.hostid)
355+
for vm in other_host_vms:
356+
for nic in vm.nics:
357+
mac_addr = nic.macaddress
358+
ip = nic.ipaddress
359+
network = get_network_details(vpconfig, nic.networkuuid)
360+
gre_key = network.grekey
361+
362+
# generate tunnel name from tunnel naming convention
363+
tunnel_name = "t%s-%s-%s" % (gre_key, this_host_id, host.hostid)
364+
of_port = get_ofport_for_vif(tunnel_name)
365+
366+
# Add flow rule in L2 look up table, if the destination mac = MAC of the nic send packet tunnel port
367+
add_mac_lookup_table_entry(bridge, mac_addr, of_port)
368+
369+
# Add flow tule in L3 look up table: if the destination IP = VM's IP then modify the packet
370+
# set DST MAC = VM's MAC, SRC MAC=tier gateway MAC and send to egress table
371+
add_ip_lookup_table_entry(bridge, ip, network.gatewaymac, mac_addr)
372+
373+
return "SUCCESS: successfully configured bridge as per the VPC topology"
374+
375+
def get_acl(vpcconfig, required_acl_id):
376+
acls = vpcconfig.acls
377+
for acl in acls:
378+
if acl.id == required_acl_id:
379+
return acl
380+
return None
381+
382+
def configure_ovs_bridge_for_routing_policies(bridge, json_config):
383+
vpconfig = jsonLoader(json.loads(json_config)).vpc
384+
385+
if vpconfig is None:
386+
logging.debug("WARNING:Can't find VPC info in json config file")
387+
return "FAILURE:IMPROPER_JSON_CONFG_FILE"
388+
389+
# First flush current egress ACL's before re-applying the ACL's
390+
del_flows(bridge, table=3)
391+
392+
egress_rules_added = False
393+
ingress_rules_added = False
394+
395+
tiers = vpconfig.tiers
396+
for tier in tiers:
397+
tier_cidr = tier.cidr
398+
acl = get_acl(vpconfig, tier.aclid)
399+
acl_items = acl.aclitems
400+
401+
for acl_item in acl_items:
402+
number = acl_item.number
403+
action = acl_item.action
404+
direction = acl_item.direction
405+
source_port_start = acl_item.sourceportstart
406+
source_port_end = acl_item.sourceportend
407+
protocol = acl_item.protocol
408+
source_cidrs = acl_item.sourcecidrs
409+
acl_priority = 1000 + number
410+
for source_cidr in source_cidrs:
411+
if direction is "ingress":
412+
ingress_rules_added = True
413+
# add flow rule to do action (allow/deny) for flows where source IP of the packet is in
414+
# source_cidr and destination ip is in tier_cidr
415+
port = source_port_start
416+
while (port < source_port_end):
417+
if action is "deny":
418+
add_flow(bridge, priority= acl_priority, table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port,
419+
nw_proto=protocol, actions='drop')
420+
if action is "allow":
421+
add_flow(bridge, priority= acl_priority,table=5, nw_src=source_cidr, nw_dst=tier_cidr, tp_dst=port,
422+
nw_proto=protocol, actions='resubmit(,1)')
423+
port = port + 1
424+
425+
elif direction in "egress":
426+
egress_rules_added = True
427+
# add flow rule to do action (allow/deny) for flows where destination IP of the packet is in
428+
# source_cidr and source ip is in tier_cidr
429+
port = source_port_start
430+
while (port < source_port_end):
431+
if action is "deny":
432+
add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port,
433+
nw_proto=protocol, actions='drop')
434+
if action is "allow":
435+
add_flow(bridge, priority= acl_priority, table=5, nw_src=tier_cidr, nw_dst=source_cidr, tp_dst=port,
436+
nw_proto=protocol, actions='resubmit(,1)')
437+
port = port + 1
438+
439+
if egress_rules_added is False:
440+
# add a default rule in egress table to forward packet to L3 lookup table
441+
add_flow(bridge, priority=0, table=3, actions='resubmit(,4)')
442+
443+
if ingress_rules_added is False:
444+
# add a default rule in egress table drop packets
445+
add_flow(bridge, priority=0, table=5, actions='drop')

0 commit comments

Comments
 (0)