dragonflow/doc/source/sfc-example/sfc-example.yaml

328 lines
11 KiB
YAML

heat_template_version: 2015-04-30
description: |
SFC example deployment
The script deploys 2 Fedora VMs:
* A VM with a UDP echo server, that listens on port 2345, and replies any
any datagram it receives back to the sender.
* A VM acting as a service function, that receives all port 2345 UDP packets
originating from the first VM, and replaces all instances of sf_filter
with sf_sub.
How to deploy:
$ openstack stack create -t doc/source/sfc-example/sfc-example.yaml stackname
Wait a few minutes
$ openstack stack show stackname
Look for server_fip address
e.g.:
server_fip=$(openstack stack show -f yaml stackname |
shyaml get-value outputs.0.output_value)
$ echo dragonflow | nc -u $server_fip 2345
DRAGONFLOW
The service function VM needs a few minutes to install dependencies.
parameters:
key_name:
type: string
label: Keypair name
default: stack
image_id:
type: string
label: Image ID
default: Fedora-Cloud-Base-25-1.3.x86_64
provider_net:
type: string
label: Provider net to use
default: public
sf_filter:
type: string
label: Filter to look for in returned messages
default: dragonflow
sf_sub:
type: string
label: The text to plug instead of filtered messages
default: DRAGONFLOW
resources:
flavor:
type: OS::Nova::Flavor
properties:
name: sfc-test-flavor
disk: 3
ram: 1024
vcpus: 1
private_net:
type: OS::Neutron::Net
properties:
name: sfc-test-net
private_subnet:
type: OS::Neutron::Subnet
properties:
name: sfc-test-subnet
network_id: { get_resource: private_net }
cidr: 20.0.0.0/24
gateway_ip: 20.0.0.1
enable_dhcp: true
allocation_pools:
- start: 20.0.0.10
end: 20.0.0.100
router:
type: OS::Neutron::Router
properties:
name: sfc-test-router
external_gateway_info:
network: { get_param: provider_net }
router_interface:
type: OS::Neutron::RouterInterface
properties:
router_id: { get_resource: router }
subnet_id: { get_resource: private_subnet }
sec_group:
type: OS::Neutron::SecurityGroup
properties:
name: sfc-test-sg
rules:
- remote_ip_prefix: 0.0.0.0/0
protocol: tcp
- remote_ip_prefix: 0.0.0.0/0
protocol: udp
- remote_ip_prefix: 0.0.0.0/0
protocol: icmp
source_vm_port:
type: OS::Neutron::Port
properties:
name: sfc-test-src-vm-port
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
security_groups:
- { get_resource: sec_group }
source_vm:
type: OS::Nova::Server
properties:
name: sfc-test-src-vm
admin_pass: test
key_name: { get_param: key_name }
flavor: { get_resource: flavor }
image: { get_param: image_id }
networks:
- port: { get_resource: source_vm_port }
user_data_format: RAW
user_data: |
#cloud-config
write_files:
- content: |
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('', 2345))
while True:
data, address = sock.recvfrom(1024)
sock.sendto(data, address)
path: /tmp/echo.py
runcmd:
- python3 /tmp/echo.py
source_fip:
type: OS::Neutron::FloatingIP
properties:
floating_network: { get_param: provider_net }
port_id: { get_resource: source_vm_port }
sf_port_ctrl:
type: OS::Neutron::Port
properties:
name: sfc-test-sf-port-ctrl
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
security_groups:
- { get_resource: sec_group }
sf_port_ingress:
type: OS::Neutron::Port
properties:
name: sfc-test-sf-port-ingress
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
port_security_enabled: false
sf_port_egress:
type: OS::Neutron::Port
properties:
name: sfc-test-sf-port-egress
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
port_security_enabled: false
sf_vm:
type: OS::Nova::Server
properties:
name: sfc-test-sf
admin_pass: test
key_name: { get_param: key_name }
flavor: { get_resource: flavor }
image: { get_param: image_id }
networks:
- port: { get_resource: sf_port_ctrl }
- port: { get_resource: sf_port_ingress }
- port: { get_resource: sf_port_egress }
user_data_format: RAW
user_data:
str_replace:
template: |
#cloud-config
write_files:
- content: |
import os
from os_ken.base import app_manager
from os_ken.controller import ofp_event
from os_ken.controller.handler import CONFIG_DISPATCHER
from os_ken.controller.handler import MAIN_DISPATCHER
from os_ken.controller.handler import set_ev_cls
from os_ken.lib.packet import packet
from os_ken.lib.packet import ethernet
from os_ken.lib.packet import ipv4
from os_ken.lib.packet import mpls
from os_ken.lib.packet import udp
from os_ken.ofproto import ofproto_v1_3
FILTER = os.environ.get('SF_FILTER')
SUB = os.environ.get('SF_SUB')
class SimpleServiceFunction(app_manager.OsKenApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
@set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER)
def switch_features_handler(self, ev):
msg = ev.msg
dp = msg.datapath
ofp_parser = dp.ofproto_parser
message = dp.ofproto_parser.OFPFlowMod(
datapath=dp,
table_id=0,
command=dp.ofproto.OFPFC_ADD,
priority=100,
match=ofp_parser.OFPMatch(in_port=1, eth_type=0x8847),
instructions=[
ofp_parser.OFPInstructionActions(
dp.ofproto.OFPIT_APPLY_ACTIONS,
[
ofp_parser.OFPActionOutput(
ofproto_v1_3.OFPP_CONTROLLER,
ofproto_v1_3.OFPCML_NO_BUFFER,
)
],
),
],
)
dp.send_msg(message)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dp = msg.datapath
ofp_parser = dp.ofproto_parser
pkt = packet.Packet(msg.data)
payload = pkt.protocols[-1]
if isinstance(payload, (bytes, bytearray)):
new_payload = payload.decode(
'utf-8'
).replace(
FILTER,
SUB,
).encode('utf-8')
new_pkt = packet.Packet()
new_pkt.add_protocol(pkt.get_protocol(ethernet.ethernet))
new_pkt.add_protocol(pkt.get_protocol(mpls.mpls))
pkt_ip = pkt.get_protocol(ipv4.ipv4)
pkt_ip.csum = 0
pkt_ip.total_length = 0
new_pkt.add_protocol(pkt_ip)
pkt_udp = pkt.get_protocol(udp.udp)
pkt_udp.csum = 0
new_pkt.add_protocol(pkt_udp)
new_pkt.add_protocol(new_payload)
new_pkt.serialize()
pkt = new_pkt
actions = [ofp_parser.OFPActionOutput(port=2)]
out = ofp_parser.OFPPacketOut(
datapath=dp,
buffer_id=ofproto_v1_3.OFP_NO_BUFFER,
in_port=ofproto_v1_3.OFPP_CONTROLLER,
data=pkt.data,
actions=actions,
)
dp.send_msg(out)
path: /tmp/controller.py
- content: |
#!/bin/bash
dnf install -y openvswitch python3-ryu
systemctl start openvswitch
ovs-vsctl add-br br-sf
ovs-vsctl set-controller br-sf tcp:127.0.0.1:6653
ovs-vsctl add-port br-sf eth1
ovs-vsctl add-port br-sf eth2
ovs-ofctl del-flows br-sf
ip link set dev eth1 up
ip link set dev eth2 up
SF_FILTER=$filter SF_SUB=$sub ryu-manager-3 /tmp/controller.py
path: /tmp/run.sh
runcmd:
- sudo bash -x /tmp/run.sh
params:
$filter: { get_param: sf_filter }
$sub: { get_param: sf_sub }
sf_fip:
type: OS::Neutron::FloatingIP
properties:
floating_network: { get_param: provider_net }
port_id: { get_resource: sf_port_ctrl }
port_pair:
type: OS::Neutron::PortPair
properties:
name: sfc-test-pp
ingress: { get_resource: sf_port_ingress }
egress: { get_resource: sf_port_egress }
service_function_parameters:
correlation: mpls
depends_on: sf_vm
port_pair_group:
type: OS::Neutron::PortPairGroup
properties:
name: sfc-test-ppg
port_pairs:
- { get_resource: port_pair }
flow_classifier:
type: OS::Neutron::FlowClassifier
properties:
name: sfc-test-fc
logical_source_port: { get_resource: source_vm_port }
ethertype: IPv4
protocol: udp
source_port_range_min: 2345
source_port_range_max: 2345
port_chain:
type: OS::Neutron::PortChain
properties:
name: sfc-test-pc
flow_classifiers:
- { get_resource: flow_classifier }
port_pair_groups:
- { get_resource: port_pair_group }
outputs:
server_fip:
description: Floating IP of the echo server
value: { get_attr: [source_fip, floating_ip_address] }