diff --git a/.gitignore b/.gitignore index 7e99e36..65cfb56 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -*.pyc \ No newline at end of file +*.pyc +.vagrant/* +.idea/* +*,cover +coverage.xml +python/.coverage +python/htmlcov diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..37eed9c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +language: python + +branches: + only: + - master + - hp-van + +python: + - "2.6" + - "2.7" + + +before_install: + - sudo apt-get update -qq + - sudo apt-get install -y libxml2-dev python-libxml2 + - wget ftp://xmlsoft.org/libxml2/python/libxml2-python-2.6.15.tar.gz + - tar -xzvf libxml2-python-2.6.15.tar.gz + - cd libxml2-python-2.6.15 && python setup.py install +install: + - cd .. + - pip install -r python/requirements.txt + - pip install coveralls + +script: + - cd python + - coverage run --source=./ --omit=__init__.py,*Test.py,t/SciPass.py t/SciPassTest_mininet.py; coverage report -m; python -m coverage xml;coverage annotate; coverage html; + +after_success: + coveralls diff --git a/Makefile b/Makefile index d7555af..d0d84f4 100644 --- a/Makefile +++ b/Makefile @@ -10,10 +10,13 @@ clean: rm -rf dist test: - cd python; coverage run --source=./ --omit=__init__.py,*Test.py,t/SciPass.py t/Test.py; coverage report -m; coverage xml;coverage annotate; coverage html; + cd python; python -m coverage run --source=./ --omit=__init__.py,*Test.py,t/SciPass.py t/SciPassTest.py; python -m coverage report -m; python -m coverage xml;python -m coverage annotate; python -m coverage html; + +test_mininet: + cd python; python -m coverage run --source=./ --omit=__init__.py,*Test.py,t/SciPass.py t/SciPassTest_mininet.py; coverage report -m; python -m coverage xml;python -m coverage annotate; python -m coverage html; dist: rm -rf dist/$(NAME)-$(VERSION) mkdir -p dist/$(NAME)-$(VERSION) cp -r etc/ python/ SciPass.spec dist/$(NAME)-$(VERSION)/ - cd dist; tar -czvf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)/ --exclude .svn \ No newline at end of file + cd dist; tar -czvf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)/ --exclude .svn diff --git a/README.md b/README.md index d7a9aa8..80145f2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,53 @@ SciPass ======= -SciPass is a SDN powered Science DMZ and IDS Load Balancer. \ No newline at end of file +[![Travis Build status](https://travis-ci.org/chrissmall22/SciPass.svg?branch=master)](https://travis-ci.org/chrissmall22/SciPass) +[![Coverage Status](https://img.shields.io/coveralls/chrissmall22/SciPass.svg)](https://coveralls.io/r/chrissmall22/SciPass?branch=master) + +SciPass is a SDN powered Science DMZ and IDS Load Balancer. + + +VM Environment with OpenDayLight +======= + +The Vagrantfile provides a quick and easy way to spin up a VM containing all the code to run +SciPass and a Mininet based test environment to test functionality. + + + +To create the SciPass VM: + +First make sure Vagrant https://www.vagrantup.com/ is installed for your OS. You will need Vagrant 1.5 to use the Vagrant Cloud boxes. You should also have Hypervisor software installed. this has been tested agqinst VirtualBox but VMWare Workstation/Fusion should work as well. + +To create the VM with SciPass and ODL installed + + vagrant up + vagrant ssh scipass + +To start the Ryu version of SciPass + + cd scipass/python + sudo mkdir /etc/SciPass + sudo cp t/etc/SciPass-mininet.xml /etc/SciPass/SciPass.xml + ryu-manager Ryu.py + + +To create a Mininet network to match SciPass-mininet.xml + + cd mininet + sudo mn --topo single,9 --mac --switch ovsk --controller remote + +Postman +======= +Install Postman for Chrome http://www.getpostman.com/ + +Add repository at scipass/resources/postman/SciPass-API.json.postman_collection + + +OpenDayLight +=========== + +To start up the OpenDaylight Controller + + cd distribution-karaf-0.2.0-Helium + bin/karaf diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..a5594ff --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,37 @@ +# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! + + +# This uses Vagrant Cloud for simplicity so it needs to have Vagrant 1.5 +VAGRANTFILE_API_VERSION = "2" + +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + + config.vm.provision "shell", path: "resources/puppet/scripts/bootstrap.sh" + + config.vm.provision "puppet" do |puppet| + puppet.hiera_config_path = "resources/puppet/hiera.yaml" + puppet.working_directory = "/vagrant/resources/puppet" + puppet.manifests_path = "resources/puppet/manifests" + puppet.manifest_file = "base.pp" + end + + # Install single host with mininet and ODL + + config.vm.define "scipass" do |scipass| + scipass.vm.box = "ubuntu/trusty64" + scipass.vm.hostname = "scipass" + scipass.vm.network "private_network", ip: "192.168.50.70" + scipass.vm.provider :virtualbox do |vb| + vb.memory = 4096 + end + scipass.vm.provider "vmware_fusion" do |vf| + vf.vmx["memsize"] = "4096" + end + scipass.vm.provision "puppet" do |puppet| + puppet.hiera_config_path = "resources/puppet/hiera.yaml" + puppet.working_directory = "/vagrant/resources/puppet" + puppet.manifests_path = "resources/puppet/manifests" + puppet.manifest_file = "scipass.pp" + end + end +end \ No newline at end of file diff --git a/python/.coveragerc b/python/.coveragerc new file mode 100644 index 0000000..ef8801d --- /dev/null +++ b/python/.coveragerc @@ -0,0 +1,2 @@ +[report] +omit=*mock*,*libxml2*,*json*,*ipaddr*,*encodings*,*xmlrunner*,t/*.py diff --git a/python/SciPass.py b/python/SciPass.py index 074d54c..6606d5c 100644 --- a/python/SciPass.py +++ b/python/SciPass.py @@ -67,7 +67,7 @@ def good_flow(self, obj): for name in self.config[datapath_id]: for port in self.config[datapath_id][name]['ports']['lan']: for prefix in port['prefixes']: - self.logger.error("Comparing" + str(new_prefix) + " " + str(prefix['prefix'])) + #self.logger.error("Comparing" + str(new_prefix) + " " + str(prefix['prefix'])) if(prefix['prefix'].Contains( new_prefix )): in_port = port dpid = datapath_id @@ -701,7 +701,7 @@ def _setupSciDMZRules(self, dpid = None, domain_name = None): actions = [] actions.append({"type": "output", "port": int(ports['wan'][0]['port_id'])}) - self.logger.error("FW WAN -> WAN: ") + #self.logger.error("FW WAN -> WAN: ") self.fireForwardingStateChangeHandlers( dpid = dpid, domain = domain_name, header = header, @@ -717,7 +717,7 @@ def _setupSciDMZRules(self, dpid = None, domain_name = None): actions = [] actions.append({"type": "output", "port": int(ports['fw_wan'][0]['port_id'])}) - self.logger.error("WAN -> FW WAN") + #self.logger.error("WAN -> FW WAN") self.fireForwardingStateChangeHandlers( dpid = dpid, domain = domain_name, header = header, @@ -766,7 +766,7 @@ def _setupInlineIDS(self, dpid = None, domain_name = None): actions = [] actions.append({"type": "output", "port": int(ports['lan'][0]['port_id'])}) - self.logger.error("FW WAN -> WAN: ") + #self.logger.error("FW WAN -> WAN: ") self.fireForwardingStateChangeHandlers( dpid = dpid, domain = domain_name, header = header, diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..d422751 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1,8 @@ +Routes>=1.12.3 +bottle>=0.10.6 +coverage>=3.7.1 +mock>=1.0.1 +ipaddr>=2.1.0 +xmlrunner>=1.7.7 +WebOb>=1.1.1 + diff --git a/python/t/SciPassTest_mininet.py b/python/t/SciPassTest_mininet.py new file mode 100644 index 0000000..5835b01 --- /dev/null +++ b/python/t/SciPassTest_mininet.py @@ -0,0 +1,254 @@ +# Version of the SciPass tests that intergrates a mininet instance +# Provides an easy way to test without a physical switch and/or with more complex topologies + + +import sys +sys.path.append(".") +import pprint +import unittest +from mock import Mock +import logging +import ipaddr +import os +from SciPass import SciPass +import libxml2 + +logging.basicConfig() + + +class TestInit(unittest.TestCase): + + def test_valid_config(self): + api = SciPass( logger = logging.getLogger(__name__), + config = str(os.getcwd()) + "/t/etc/SciPass-mininet.xml" + ) + self.assertTrue(isinstance(api,SciPass)) + + +# def test_no_config(self): +# self.assertRaises(libxml2.parserError,SciPass) +# +# api = SciPass( logger = logging.getLogger(__name__), +# config = str(os.getcwd()) + "/t/etc/no_config.xml" ) + + +# def test_invalid_config(self): +# self.assertRaises(libxml2.parserError,SciPass) +# +# api = SciPass( logger = logging.getLogger(__name__), +# config = str(os.getcwd()) + "/t/etc/InvalidConfig.xml" ) + + def test_switch_init(self): + api = SciPass( logger = logging.getLogger(__name__), + config = str(os.getcwd()) + "/t/etc/SciPass-mininet.xml" ) + + #first setup the handler to get all the flows that were sent + flows = [] + def flowSent(dpid = None, header = None, actions = None,command = None, priority = None, idle_timeout = None, hard_timeout = None): + flows.append({'dpid': dpid, 'header': header, 'actions': actions, 'command': command, 'priority': priority, 'idle_timeout': idle_timeout, 'hard_timeout': hard_timeout}) + + api.registerForwardingStateChangeHandler(flowSent) + datapath = Mock(id=1) + api.switchJoined(datapath) + + self.assertTrue( len(flows) == 33) + #verify all of the 'flow details are set properly' + for flow in flows: + self.assertEquals(flow['dpid'], "%016x" % datapath.id) + self.assertEquals(flow['hard_timeout'], 0) + self.assertEquals(flow['idle_timeout'], 0) + + flow = flows[0] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 4}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 1, 'dl_type': None}) + self.assertEquals(flow['priority'], 5) + flow = flows[1] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 1}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':4, 'nw_dst': 167776512, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[2] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 4}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':1, 'nw_src': 167776512, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[3] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 1}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':4, 'nw_dst': 167776768, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[4] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 4}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':1, 'nw_src': 167776768, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[5] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 4}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 2, 'dl_type': None}) + self.assertEquals(flow['priority'], 5) + flow = flows[6] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 2}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':4, 'nw_dst': 167777024, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[7] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 4}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':2, 'nw_src': 167777024, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[8] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 2}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':4, 'nw_dst': 167777280, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[9] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 4}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port':2, 'nw_src': 167777280, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 10) + flow = flows[10] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 1}, {'type': 'output', 'port': 2}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 4, 'dl_type': None}) + self.assertEquals(flow['priority'], 3) + flow = flows[11] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 3}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 5, 'dl_type': None}) + self.assertEquals(flow['priority'], 10) + flow = flows[12] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': 5}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 3, 'dl_type': None}) + self.assertEquals(flow['priority'], 10) + flow = flows[13] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': '6'}, {'type': 'output', 'port': '4'}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 1, 'nw_src': 167776512, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[14] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': '6'}, {'type': 'output', 'port': '5'}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 3, 'nw_dst': 167776512, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[15] + self.assertEquals(flow['actions'],[]) + self.assertEquals(flow['command'],"DELETE_STRICT") + self.assertEquals(flow['header'], {'phys_port': 1, 'nw_src': 167776512, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[16] + self.assertEquals(flow['actions'],[]) + self.assertEquals(flow['command'],"DELETE_STRICT") + self.assertEquals(flow['header'], {'phys_port': 3, 'nw_dst': 167776512, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[17] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': '8'}, {'type': 'output', 'port': '4'}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 1, 'nw_src': 167776512, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[18] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': '8'}, {'type': 'output', 'port': '5'}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 3, 'nw_dst': 167776512, 'nw_dst_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[19] + self.assertEquals(flow['actions'],[{'type': 'output', 'port': '6'}, {'type': 'output', 'port': '4'}]) + self.assertEquals(flow['command'],"ADD") + self.assertEquals(flow['header'], {'phys_port': 1, 'nw_src': 167776768, 'nw_src_mask': 24}) + self.assertEquals(flow['priority'], 500) + flow = flows[20] + +class TestFunctionality(unittest.TestCase): + def setUp(self): + self.api = SciPass( logger = logging.getLogger(__name__), + config = str(os.getcwd()) + "/t/etc/SciPass-mininet.xml" ) + + def test_update_prefix_bw(self): + #first setup the handler to get all the flows that were sent + flows = [] + def flowSent(dpid = None, header = None, actions = None,command = None, priority = None, idle_timeout = None, hard_timeout = None): + flows.append({'dpid': dpid, 'header': header, 'actions': actions, 'command': command, 'priority': priority, 'idle_timeout': idle_timeout, 'hard_timeout': hard_timeout}) + + self.api.registerForwardingStateChangeHandler(flowSent) + datapath = Mock(id=1) + self.api.switchJoined(datapath) + + self.assertEquals( len(flows), 33) + self.api.updatePrefixBW("%016x" % datapath.id, ipaddr.IPv4Network("10.0.19.0/24"), 500,500) + self.assertTrue(self.api.getBalancer("%016x" % datapath.id, "R&E").getPrefixBW(ipaddr.IPv4Network("10.0.19.0/24")), 1000) + self.api.updatePrefixBW("%016x" % datapath.id, ipaddr.IPv4Network("10.0.17.0/24"), 500,500) + self.assertTrue(self.api.getBalancer("%016x" % datapath.id, "R&E").getPrefixBW(ipaddr.IPv4Network("10.0.17.0/24")), 1000) + + + def test_good_flow(self): + flows = [] + def flowSent(dpid = None, header = None, actions = None,command = None, priority = None, idle_timeout = None, hard_timeout = None): + flows.append({'dpid': dpid, 'header': header, 'actions': actions, 'command': command, 'priority': priority, 'idle_timeout': idle_timeout, 'hard_timeout': hard_timeout}) + + self.api.registerForwardingStateChangeHandler(flowSent) + datapath = Mock(id=1) + self.api.switchJoined(datapath) + #self.logger.error("testing good flow") + self.assertEquals(len(flows),33) + flows = [] + self.api.good_flow({"nw_src": "10.0.20.2/32", "nw_dst":"156.56.6.1/32", "tp_src":1, "tp_dst":2}) + self.assertEquals(len(flows),2) + flow = flows[0] + self.assertEqual(int(flow['hard_timeout']),0) + self.assertEqual(int(flow['idle_timeout']),90) + self.assertEqual(flow['actions'],[{'type': 'output', 'port': '3'}]) + self.assertEqual(flow['header'],{'phys_port': 2, 'nw_src_mask': 32, 'nw_dst_mask': 32, 'nw_src': 167777282, 'tp_dst': 2, 'tp_src': 1, 'nw_dst': 2620917249}) + self.assertEqual(int(flow['priority']),900) + self.assertEqual(flow['command'],"ADD") + self.assertEqual(flow['dpid'],"%016x" % datapath.id) + flow = flows[1] + self.assertEqual(int(flow['hard_timeout']),0) + self.assertEqual(int(flow['idle_timeout']),90) + self.assertEqual(flow['actions'],[{'type': 'output', 'port': '2'}]) + self.assertEqual(flow['header'],{'phys_port': 3, 'nw_src_mask': 32, 'nw_dst_mask': 32, 'nw_dst': 167777282, 'tp_dst': 1, 'tp_src': 2, 'nw_src': 2620917249}) + self.assertEqual(int(flow['priority']),900) + self.assertEqual(flow['command'],"ADD") + self.assertEqual(flow['dpid'],"%016x" % datapath.id) + + + + def test_bad_flow(self): + flows = [] + def flowSent(dpid = None, header = None, actions = None,command = None, priority = None, idle_timeout = None, hard_timeout = None): + flows.append({'dpid': dpid, 'header': header, 'actions': actions, 'command': command, 'priority': priority, 'idle_timeout': idle_timeout, 'hard_timeout': hard_timeout}) + + self.api.registerForwardingStateChangeHandler(flowSent) + datapath = Mock(id=1) + self.api.switchJoined(datapath) + #self.logger.error("testing good flow") + self.assertEquals(len(flows),33) + flows = [] + self.api.bad_flow({"nw_src": "10.0.20.2/32", "nw_dst":"156.56.6.1/32", "tp_src":1, "tp_dst":2}) + self.assertEquals(len(flows),2) + flow = flows[0] + self.assertEqual(int(flow['hard_timeout']),0) + self.assertEqual(int(flow['idle_timeout']),90) + self.assertEqual(flow['actions'],[]) + self.assertEqual(flow['header'],{'phys_port': 2, 'nw_src_mask': 32, 'nw_dst_mask': 32, 'nw_src': 167777282, 'tp_dst': 2, 'tp_src': 1, 'nw_dst': 2620917249}) + self.assertEqual(int(flow['priority']),900) + self.assertEqual(flow['command'],"ADD") + self.assertEqual(flow['dpid'],"%016x" % datapath.id) + flow = flows[1] + self.assertEqual(int(flow['hard_timeout']),0) + self.assertEqual(int(flow['idle_timeout']),90) + self.assertEqual(flow['actions'],[]) + self.assertEqual(flow['header'],{'phys_port': 3, 'nw_src_mask': 32, 'nw_dst_mask': 32, 'nw_dst': 167777282, 'tp_dst': 1, 'tp_src': 2, 'nw_src': 2620917249}) + self.assertEqual(int(flow['priority']),900) + self.assertEqual(flow['command'],"ADD") + self.assertEqual(flow['dpid'],"%016x" % datapath.id) + +def suite(): + suite = unittest.TestLoader().loadTestsFromTestCase(TestInit) + suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestFunctionality)) + return suite + + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(suite()) diff --git a/python/t/etc/SciPass-mininet.xml b/python/t/etc/SciPass-mininet.xml new file mode 100644 index 0000000..50f7787 --- /dev/null +++ b/python/t/etc/SciPass-mininet.xml @@ -0,0 +1,34 @@ + + + + + + + 10.0.17.0/24 + 10.0.18.0/24 + + + + 10.0.19.0/24 + 10.0.20.0/24 + + + + + + + + + + + + + + + + + + + diff --git a/resources/postman/SciPass-API.json.postman_collection b/resources/postman/SciPass-API.json.postman_collection new file mode 100644 index 0000000..5d72b77 --- /dev/null +++ b/resources/postman/SciPass-API.json.postman_collection @@ -0,0 +1,269 @@ +{ + "id": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "name": "SciPass API", + "description": "SDN powered Science DMZ and IDS Load Balancer \n\nhttp://globalnoc.iu.edu/sdn/scipass.html", + "order": [ + "4a642c67-78c5-7206-fff2-387c1259b279", + "cecfceb5-bf3e-e220-c372-eb3e8afccf13", + "516fb4bc-b5f0-b254-108e-d987e1c423ea", + "0723aabb-8fd8-9872-894d-8bae55366a68", + "f29ece83-d848-24dc-37aa-47b1509636d3", + "76b13b3a-a812-bd55-7f11-113b07c9b6f7", + "fd0990a4-90b1-1265-239d-0e9e65cc2a5f", + "84b5ad94-f614-f9f9-0af5-5f88ed96ba78", + "b44b355d-3582-6323-542e-c312ff4a776d", + "0b8f1794-fe4d-bcbf-c446-fd26ac1f1754" + ], + "folders": [], + "timestamp": 1415861202181, + "synced": false, + "requests": [ + { + "id": "0723aabb-8fd8-9872-894d-8bae55366a68", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/flows/good_flow", + "preRequestScript": "", + "pathVariables": {}, + "method": "PUT", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415862011803, + "name": "Put Good Flow", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "0b8f1794-fe4d-bcbf-c446-fd26ac1f1754", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/switch/0000000000000001/domain/R&E/flows", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [ + { + "key": "sensor_id", + "value": "1", + "type": "text", + "enabled": true + }, + { + "key": "load", + "value": "1", + "type": "text", + "enabled": true + } + ], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415863338608, + "name": "Get Domain R&E Flows", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "4a642c67-78c5-7206-fff2-387c1259b279", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/flows/get_good_flows", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415862080367, + "name": "Get Good Flows", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "516fb4bc-b5f0-b254-108e-d987e1c423ea", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/switch/0000000000000001/flows", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415861820792, + "name": "Get Flows from DPID 1", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "76b13b3a-a812-bd55-7f11-113b07c9b6f7", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/flows/good_flow", + "preRequestScript": "", + "pathVariables": {}, + "method": "PUT", + "data": [ + { + "key": "sensor_id", + "value": "1", + "type": "text", + "enabled": true + }, + { + "key": "load", + "value": "1", + "type": "text", + "enabled": true + } + ], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415862602431, + "name": "Put Sensor 1 Load 1", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "84b5ad94-f614-f9f9-0af5-5f88ed96ba78", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/switches", + "pathVariables": {}, + "preRequestScript": "", + "method": "GET", + "data": [ + { + "key": "sensor_id", + "value": "1", + "type": "text", + "enabled": true + }, + { + "key": "load", + "value": "1", + "type": "text", + "enabled": true + } + ], + "dataMode": "params", + "name": "Get Domains", + "description": "", + "descriptionFormat": "html", + "time": 1415863150714, + "version": 2, + "responses": [], + "tests": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "synced": false + }, + { + "id": "b44b355d-3582-6323-542e-c312ff4a776d", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/switch/0000000000000001/domain/R&E", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [ + { + "key": "sensor_id", + "value": "1", + "type": "text", + "enabled": true + }, + { + "key": "load", + "value": "1", + "type": "text", + "enabled": true + } + ], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415863274971, + "name": "Get Domain R&E Sensors", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "cecfceb5-bf3e-e220-c372-eb3e8afccf13", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/flows/get_bad_flows", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415861635872, + "name": "Get Bad Flows", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + }, + { + "id": "f29ece83-d848-24dc-37aa-47b1509636d3", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/flows/good_flow", + "pathVariables": {}, + "preRequestScript": "", + "method": "PUT", + "data": [], + "dataMode": "params", + "name": "Put Bad Flow", + "description": "", + "descriptionFormat": "html", + "time": 1415862418371, + "version": 2, + "responses": [], + "tests": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "synced": false + }, + { + "id": "fd0990a4-90b1-1265-239d-0e9e65cc2a5f", + "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nAccept: application/json\nContent-Type: application/json\n", + "url": "http://{{controllerHost}}:{{controllerPort}}/scipass/switches", + "preRequestScript": "", + "pathVariables": {}, + "method": "GET", + "data": [ + { + "key": "sensor_id", + "value": "1", + "type": "text", + "enabled": true + }, + { + "key": "load", + "value": "1", + "type": "text", + "enabled": true + } + ], + "dataMode": "params", + "version": 2, + "tests": "", + "time": 1415862643099, + "name": "Get Switches", + "description": "", + "collectionId": "35a3cdcb-55df-9be0-47d1-58a7fc4d0ce4", + "responses": [], + "synced": false + } + ] +} \ No newline at end of file diff --git a/resources/puppet/hiera.yaml b/resources/puppet/hiera.yaml new file mode 100644 index 0000000..8e2f646 --- /dev/null +++ b/resources/puppet/hiera.yaml @@ -0,0 +1,10 @@ +--- +:backends: + - yaml + - json +:yaml: + :datadir: /vagrant/resources/puppet/hieradata +:json: + :datadir: /vagrant/resources/puppet/hieradata +:hierarchy: + - hosts diff --git a/resources/puppet/hieradata/hosts.json b/resources/puppet/hieradata/hosts.json new file mode 100644 index 0000000..ee7086b --- /dev/null +++ b/resources/puppet/hieradata/hosts.json @@ -0,0 +1,8 @@ +{ + "hosts": { + "scipass": { + "name": "scipass", + "ipaddress": "192.168.50.70" + } +} +} \ No newline at end of file diff --git a/resources/puppet/manifests/base.pp b/resources/puppet/manifests/base.pp new file mode 100644 index 0000000..3f095f2 --- /dev/null +++ b/resources/puppet/manifests/base.pp @@ -0,0 +1,12 @@ +package {"git": + ensure => "installed" +} + +$hosts = hiera('hosts') + +file { "/etc/hosts": + ensure => file, + owner => "root", + group => "root", + content => template('/vagrant/resources/puppet/templates/hosts.erb') +} diff --git a/resources/puppet/manifests/mininet.pp b/resources/puppet/manifests/mininet.pp new file mode 100644 index 0000000..91ac25a --- /dev/null +++ b/resources/puppet/manifests/mininet.pp @@ -0,0 +1,103 @@ +$deps = [ 'build-essential', + 'debhelper', + 'dkms', + 'fakeroot', + 'graphviz', + 'linux-headers-generic', + 'python-all', + 'python-qt4', + 'python-zopeinterface', + 'python-twisted-conch', + 'python-twisted-web', + 'xauth' +] + +package { $deps: + ensure => installed, +} + +vcsrepo { '/home/vagrant/mininet': + ensure => present, + provider => git, + user => 'vagrant', + source => 'git://github.com/mininet/mininet', + revision => '2.1.0p2', + before => Exec['Install Mininet'] +} + +exec { 'Install Mininet': + command => 'bash mininet/util/install.sh -nf > /dev/null', + cwd => '/home/vagrant', + user => 'vagrant', + path => $::path, + timeout => 0 +} + +exec {'openvswitch-2.1.2.tar.gz': + command => 'wget http://openvswitch.org/releases/openvswitch-2.1.2.tar.gz', + cwd => '/home/vagrant', + path => $::path, + user => 'vagrant' +} + +exec { 'Extract Open vSwitch': + command => 'tar -xvf openvswitch-2.1.2.tar.gz', + cwd => '/home/vagrant', + user => 'vagrant', + path => $::path, + timeout => 0, + require => Exec['openvswitch-2.1.2.tar.gz'] +} + +exec { 'Compile Open vSwitch': + command => 'fakeroot debian/rules binary', + cwd => '/home/vagrant/openvswitch-2.1.2', + user => 'root', + path => $::path, + timeout => 0, + require => [Exec['Extract Open vSwitch'], Package[$deps]] +} + +package { 'openvswitch-common': + ensure => installed, + provider => dpkg, + source => '/home/vagrant/openvswitch-common_2.1.2-1_amd64.deb', + require => Exec['Compile Open vSwitch'] +} + +package { 'openvswitch-switch': + ensure => installed, + provider => dpkg, + source => '/home/vagrant/openvswitch-switch_2.1.2-1_amd64.deb', + require => Package['openvswitch-common'] +} + +package { 'openvswitch-datapath-dkms': + ensure => installed, + provider => dpkg, + source => '/home/vagrant/openvswitch-datapath-dkms_2.1.2-1_all.deb', + require => Package['openvswitch-switch'] +} + +package { 'openvswitch-pki': + ensure => installed, + provider => dpkg, + source => '/home/vagrant/openvswitch-pki_2.1.2-1_all.deb', + require => Package['openvswitch-datapath-dkms'] +} + +exec { 'Compile Test Controller': + command => 'sh boot.sh && sh configure && make', + cwd => '/home/vagrant/openvswitch-2.1.2', + path => $::path, + user => 'root', + require => [Exec['Compile Open vSwitch'], Package[$deps]] +} + +exec { 'Link Test Controller': + command => 'ln -s /home/vagrant/openvswitch-2.1.2/tests/test-controller /usr/bin/ovs-controller', + cwd => '/home/vagrant/openvswitch-2.1.2', + path => $::path, + user => 'root', + require => Exec['Compile Test Controller'] +} diff --git a/resources/puppet/manifests/scipass.pp b/resources/puppet/manifests/scipass.pp new file mode 100644 index 0000000..a7188c3 --- /dev/null +++ b/resources/puppet/manifests/scipass.pp @@ -0,0 +1,90 @@ +$deps = [ 'build-essential', + 'debhelper', + 'dkms', + 'fakeroot', + 'graphviz', + 'linux-headers-generic', + 'python-all', + 'python-qt4', + 'python-zopeinterface', + 'python-twisted-conch', + 'python-twisted-web', + 'xauth', + 'openvswitch-switch', + 'python-coverage', + 'python-mock', + 'python-ipaddr', + 'python-libxml2', + 'python-lxml', + 'python-webob', + 'python-routes', + 'python-paramiko', + 'python-oslo.config', + 'python-netaddr', + 'msgpack-python', + 'python-greenlet', + 'python-pip', + 'python-dev', + 'python-tox' +] + +package { $deps: + ensure => installed, +} + +vcsrepo { '/home/vagrant/mininet': + ensure => present, + provider => git, + user => 'vagrant', + source => 'git://github.com/mininet/mininet', + revision => '2.1.0p2', + before => Exec['Install Mininet'] +} + +exec { 'Install Mininet': + command => 'bash mininet/util/install.sh -nf > /dev/null', + cwd => '/home/vagrant', + user => 'vagrant', + path => $::path, + timeout => 0 +} + +exec {'ODL-2.0': + command => 'wget http://nexus.opendaylight.org/content/groups/public/org/opendaylight/integration/distribution-karaf/0.2.0-Helium/distribution-karaf-0.2.0-Helium.tar.gz', + cwd => '/home/vagrant', + path => $::path, + user => 'vagrant' +} + +exec { 'Extract ODL': + command => 'tar -xvf distribution-karaf-0.2.0-Helium.tar.gz', + cwd => '/home/vagrant', + user => 'vagrant', + path => $::path, + timeout => 0, + require => Exec['ODL-2.0'] +} + +vcsrepo { '/home/vagrant/scipass': + ensure => present, + provider => git, + user => 'vagrant', + source => 'https://github.com/GlobalNOC/SciPass', + revision => 'master' +} + +vcsrepo { '/home/vagrant/ryu': + ensure => present, + provider => git, + user => 'vagrant', + source => 'https://github.com/osrg/ryu', + before => Exec['Install Ryu'] +} + +exec { 'Install Ryu': + command => 'sudo python ./setup.py install', + cwd => '/home/vagrant/ryu', + user => 'vagrant', + path => $::path, + timeout => 0 +} diff --git a/resources/puppet/scripts/bootstrap.sh b/resources/puppet/scripts/bootstrap.sh new file mode 100644 index 0000000..b20f50c --- /dev/null +++ b/resources/puppet/scripts/bootstrap.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# +# This bootstraps Puppet on Ubuntu 12.04 LTS. +# +set -e + +# Load up the release information +. /etc/lsb-release + +REPO_DEB_URL="http://apt.puppetlabs.com/puppetlabs-release-${DISTRIB_CODENAME}.deb" + +#-------------------------------------------------------------------- +# NO TUNABLES BELOW THIS POINT +#-------------------------------------------------------------------- +if [ "$(id -u)" != "0" ]; then + echo "This script must be run as root." >&2 + exit 1 +fi + +if which puppet > /dev/null 2>&1 -a apt-cache policy | grep --quiet apt.puppetlabs.com; then + echo "Puppet is already installed." + exit 0 +fi + +# Do the initial apt-get update +echo "Initial apt-get update..." +apt-get update >/dev/null + +# Install wget if we have to (some older Ubuntu versions) +echo "Installing wget..." +apt-get install -y wget >/dev/null + +# Install the PuppetLabs repo +echo "Configuring PuppetLabs repo..." +repo_deb_path=$(mktemp) +wget --output-document="${repo_deb_path}" "${REPO_DEB_URL}" 2>/dev/null +dpkg -i "${repo_deb_path}" >/dev/null +apt-get update >/dev/null + +# Install Puppet +echo "Installing Puppet..." +DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install puppet >/dev/null + +echo "Puppet installed!" + +# Install RubyGems for the provider +echo "Installing RubyGems..." +if [ $DISTRIB_CODENAME != "trusty" ]; then + apt-get install -y rubygems >/dev/null +fi +gem install --no-ri --no-rdoc rubygems-update +update_rubygems >/dev/null + +# Installing Puppet Modules +puppet module install puppetlabs/vcsrepo +puppet module install puppetlabs/stdlib diff --git a/resources/puppet/templates/hosts.erb b/resources/puppet/templates/hosts.erb new file mode 100644 index 0000000..c23f40f --- /dev/null +++ b/resources/puppet/templates/hosts.erb @@ -0,0 +1,8 @@ +## Do Not Edit. Created by Puppet ## +127.0.0.1 localhost +255.255.255.255 broadcasthost +::1 localhost +fe80::1%lo0 localhost +<% @hosts.values.each do |h| %> +<%= h["ipaddress"] %> <%= h["name"] %> +<% end %>