课程目录

  1. SDN编程基础
    1.1 Python简介
    1.2 Python基本数据类型–列表
    1.3 Python基本数据类型–字符串
    1.4 Python基本数据类型–字典、集合
    1.5 Python基本结构
    1.6 Python中函数与对象
    1.7 Python中的模块
    1.8 Python Web编程

  2. SDN理论基础
    2.1 SDN是什么
    2.2 SDN体系结构
    2.3 SDN典型应用
    2.4 OpenFlow概述

  3. SDN控制器编程
    3.1 SDN及ODL介绍
    3.2 ODL初体验实验
    3.3 OSGi及Karaf
    3.4 OFPlugin&Mininet
    3.5 RESTCONF介绍
    3.6 Maven基础
    3.7 ODL APP开发思路
    3.8 ODL Hello
    3.9 子项目L2Switch

SDN编程基础

Python简介

版本差异

版本差异:python的两个版本存在一些兼容性问题,选择版本时尽量选择python3,python2.7将于2020年停止更新,但目前大量网络设备上但python运行环境还是python2,如NXOS。Junos17.1同样已自带python,不需要额外安装。不过需要使用junos scripts运行,不能直接运行。例子如下:

root@junos:/var/db/scripts/op # cat ver.py
#!/usr/bin/python
import sys
print(sys.version)

# JUNOS配置
root@junos # set system scripts op file ver.py

#JUNOS运行python脚本
root@junos # run op ver.py
2.7.8 (default, Jun 17 2017, 05:48:24)
[GCC 4.2.1 (for JUNOS)]

运行环境

通常情况下,建议每一个项目都创建一个虚拟环境,在这样一个虚拟环境中用到的第三方库、自己写的库十分清晰。这样一个运行环境可以直接拷贝同一个目录到另一台相同操作系统的目录下直接运行。

用virtualenv创建python虚拟运行环境

在编写python程序时,通常会用到一些第三方的库文件,并且不同的项目通常会调用不同的第三方库。如果所有项目都混在一起,这显然不太方便进行管理。virtualenv是一个虚拟环境管理工具

pip install virtualenv
virtualenv project1
Using base prefix '/usr'
New python executable in /home/lab/project1/bin/python3
Also creating executable in /home/lab/project1/bin/python
Installing setuptools,,pip,wheel. . . done.
source ./project1/bin/activate

使用venv创建python虚拟运行环境

python3.3及以上版本在标准库中有一个venv的模块,这个模块和virutualenv实现了类似的功能。如果你的系统中没有包含venv模块,我们也可以通过命令来进行安装。

apt-get install python3-venv #安装模块
python3 -m venv venv1 #创建python虚拟环境,创建一个venv1的目录
source venv1/bin/activate #启用这个虚拟环境

使用pyenv来创建不同版本的共存

前面两种方法中的主要功能是在一个操作系统里实现不同的python运行环境,其python的软件版本是相同的。如果我们希望快速创建不同
python版本的运行环境,pyenv是一个不错的选择。Pyenv是一个开源项目,它的源代码托管在GitHub中:https://github.com/pyenv/pyenv
安装步骤:

apt-get install make build-essential libssl-dev zliblg-dev
apt-get install libbz2-dev libbreadline-dev libsqlite3-dev wget curl
apt-get install llvm libncurses5-dev libcursesw5-dev
curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash
pyenv update
pyenv install -list
pyenv install 3.6.2
pyenv version #查看软件版本
pyenv global 3.6.2 #设置全局python版本

使用docker来创建

Python基本数据类型

  • 数字
    python中基本不用考虑长度问题造成溢出
    python2中整数相除得到整数,如需得到小数,使用from __future__ import division重载除法
    二进制到十进制:0b1000
    十进制到二进制:bin(32)
    十六进制到十进制:0xFF
    十进制到十六进制:hex(128)
  • 列表
    列表是一种可变的序列。
    创建一个列表的方式为,列表中的每个元素通过逗号分开,最外面使用方括号。如果是一个空的列表可以用一对方括号来表示。
devices = []
host = ['R1', '1.1.1.1', 22]

索引与切片
python中可为负(倒着数)

>>>host[0]
'R1'
>>>host[-1]
22
>>>host[1:3]#切片,取第2~第4个元素,不含第4个;如果超过了最大值则取到列表结尾而不会报错
['1.1.1.1',22]
>>>host[-2:]#不写数字意味着取到最后
['1.1.1.1',22]
>>>host.append('R2')
>>>host.append('2222')
>>>host.append(22)
['R1', '1.1.1.1', 22, 'R2', '2222']
>>>host[1::3]#步长为3取到最后
['1.1.1.1', '2222']
>>>host[-2::-3]#步长为负向左取
['2222', '1.1.1.1']
>>>host[::-1]#从开头到结尾步长为-1取;得到列表的反序列
[22, '2222', 'R2', 22, '1.1.1.1', 'R1']
>>>host.insert(0,'info')#在位置0前面插入'info'
['info', 'R1', '1.1.1.1', 22, 'R2', '2222', 22]
>>>host.pop()#取出最后一个元素
22
['info', 'R1', '1.1.1.1', 22, 'R2', '2222']
>>>host.remove('info')#移除第一个出现的'info'
['R1', '1.1.1.1', 22, 'R2', '2222']
>>>host[0]='R11'#修改第一个值
['R11', '1.1.1.1', 22, 'R2', '2222']

在host列表中,包含了三个元素,前两个是字符串,最后一个是数字。在python的列表中每个元素的数据类型是可以不相同的。python列表能容纳的元素个数是无限大的,其容量大小受限于硬件(内存)。

列表 加法

>>>device = ['R3','3.3.3.3',22]
>>>host + device
['R11', '1.1.1.1', 22, 'R2', '2222', 'R3', '3.3.3.3', 22]
>>>host.extend(device)
>>>host
['R11', '1.1.1.1', 22, 'R2', '2222', 'R3', '3.3.3.3', 22]
>>>host.append(device)
>>>host
['R11', '1.1.1.1', 22, 'R2', '2222', ['R3', '3.3.3.3', 22]]

列表 乘法

>>>device
['R3','3.3.3.3',22]
>>>device * 3
['R3', '3.3.3.3', 22, 'R3', '3.3.3.3', 22, 'R3', '3.3.3.3', 22]
  • 元组
    一个有顺序的序列,但(与列表不同的是)元祖的值不可变
>>>login=('R1','admin','admin@123')
>>>login
('R1', 'admin', 'admin@123')
>>>login[0]
R1
>>>login[1:]
('admin', 'admin@123')
>>>id(login)
4389238736 #这是一个动态的值(内存地址),每次的值并不一样
>>>login=('R1','admin','admin@123')
>>>id(login)
>>>4365010336 #内存的地址已经发生了变化,重新分配了一个空间 而修改列表的值时内存地址不会发生变化
  • 字符串
    字符串与元组同样不可修改,重新赋值时内存中的地址已经发生了变化
a = "abc"
b = 'avcd'
c = '''asdf
   cqwdfs
fw''' #此时可以保留换行和空格

字符串合并(加法)

a="abc""def"
b="abc"+"def"

字符串的格式化

>>> _type = "ip address"
>>>ip = "10.1.1.1"
>>>R1="R1‘s %s is %s." % (_type, ip)
R1‘s ip address is 10.1.1.1.
转换类型 含义
d或i 带符号的十进制整数
f 带符号的十进制浮点数
o 不带符号的八进制
u 不带符号的十进制
x 不带符号的十六进制(字母小写)
X 不带符号的十六进制(字母大写)
s 字符串

更多用法与C语言类似
format方法格式化

"R1's ip address is {0},R2's ip address is {1}".format("1.1.1.1","2.2.2.2")
"interface {name} vlan id is {vlanid}".format(name='gi0/0',vlanid=10)

字符串的切片处理
与列表类似

>>> inf="interface GigaEthernet0/0/0/0"
>>> inf[10:]
'GigaEthernet0/0/0/0'
>>> version="17.1R2.8"
>>> version[:version.index("R")]
'17.1'

常用方法

>>> version
'17.1R2.8'
>>> version.split("R")#split默认参数为空格
['17.1', '2.8']
>>> version.split("R")[1]
'2.8'
>>> version.strip("17.1")#返回删除掉一部分内容后的值
'R2.8'
>>> version
'17.1R2.8'
  • 字典
    python中字典是一种可变的容器,它可以存储任意的数据对象。有时候字典也被称为哈希表,它是基于建值映射的数据结构。它通过"{key1:value1,key2:value2}";key值不能相同
    可变类型(如列表)不可作为key值
    可变值(如元组、字符串)可作为key值
    字典中的值顺序没有区别
>>> device={"hostname":"R1","IP":"1.1.1.1","Port":22}
>>> device
{'hostname': 'R1', 'IP': '1.1.1.1', 'Port': 22}

访问字典中的值

>>> device["hostname"]
'R1'
>>> device.get("Port")
22

修改字典

>>> device
{'hostname': 'R1', 'IP': '1.1.1.1', 'Port': 22}
>>> device["hostname"]="R2"
>>> device
{'hostname': 'R2', 'IP': '1.1.1.1', 'Port': 22}
>>> device
{'hostname': 'R2', 'IP': '1.1.1.1', 'Port': 22}
#如果对一个不存在的键值进行赋值操作,那么将添加一对新的键和值。由于键值是区分大小写的,“port”和“Port”不相等,所以字典中多了一个port元素
>>> device["port"]=23
>>> device
{'hostname': 'R2', 'IP': '1.1.1.1', 'Port': 22, 'port': 23}

删除字典中的元素

>>> device
{'hostname': 'R2', 'IP': '1.1.1.1', 'Port': 22}
>>> device["port"]=23
>>> device
{'hostname': 'R2', 'IP': '1.1.1.1', 'Port': 22, 'port': 23}
>>> del(device['port'])
>>> device
{'hostname': 'R2', 'IP': '1.1.1.1', 'Port': 22}
>>> device.pop("Port")
22
>>> device
{'hostname': 'R2', 'IP': '1.1.1.1'}

获取键和值

>>> device
{'hostname': 'R2', 'IP': '1.1.1.1'}
>>> device.keys()#返回的是一个列表
dict_keys(['hostname', 'IP'])
>>> device.values()
dict_values(['R2', '1.1.1.1'])
>>> device.items()#返回的是一个元组
dict_items([('hostname', 'R2'), ('IP', '1.1.1.1')])
  • 集合
    python中,集合的值是不重复的、没有顺序的
>>> ip_set1=set(["1.1.1.1","1.1.1.2","1.1.1.5","10.1.1.1","10.1.1.10"])
>>> ip_set2=set(["10.1.1.1","1.1.1.2","1.1.1.1","1.1.1.2","1.1.1.10"])
>>> ip_set1
{'1.1.1.1', '1.1.1.2', '10.1.1.1', '10.1.1.10', '1.1.1.5'}
>>> ip_set2
{'10.1.1.1', '1.1.1.2', '1.1.1.10', '1.1.1.1'}#重复的“1.1.1.2”被删除

集合中添加元素

>>> ip_set1
{'1.1.1.1', '1.1.1.2', '10.1.1.1', '10.1.1.10', '1.1.1.5'}
>>> ip_set1.add("1.1.1.3")
>>> ip_set1.add("1.1.1.1")#添加已存在元素不会发生变化也不会报错
>>> ip_set1
{'1.1.1.1', '1.1.1.2', '10.1.1.1', '1.1.1.3', '10.1.1.10', '1.1.1.5'}
#一次性添加多个元素,需要使用update方法
>>> ip_set1
{'1.1.1.1', '1.1.1.2', '10.1.1.1', '1.1.1.3', '10.1.1.10', '1.1.1.5'}
>>> ip_set1.update(["1.1.1.2","1.1.1.3","1.1.1.4"])
>>> ip_set1
{'1.1.1.1', '1.1.1.2', '10.1.1.1', '1.1.1.4', '1.1.1.3', '10.1.1.10', '1.1.1.5'}

删除元素

>>> ip_set1
{'1.1.1.1', '1.1.1.2', '10.1.1.1', '1.1.1.4', '1.1.1.3', '10.1.1.10', '1.1.1.5'}
>>> ip_set1.remove("1.1.1.2")
>>> ip_set1
{'1.1.1.1', '10.1.1.1', '1.1.1.4', '1.1.1.3', '10.1.1.10', '1.1.1.5'}
#移除不存在的元素会报错

其他操作

>>> ip_set1
{'1.1.1.1', '10.1.1.1', '1.1.1.4', '1.1.1.3', '10.1.1.10', '1.1.1.5'}
>>> ip_set2
{'10.1.1.1', '1.1.1.2', '1.1.1.10', '1.1.1.1'}
#交集
>>> ip_set1 & ip_set2
{'10.1.1.1', '1.1.1.1'}
#并集
>>> ip_set1 | ip_set2
{'1.1.1.1', '10.1.1.1', '1.1.1.2', '1.1.1.4', '1.1.1.10', '1.1.1.3', '10.1.1.10', '1.1.1.5'}
#差集
>>> ip_set1 - ip_set2
{'10.1.1.10', '1.1.1.4', '1.1.1.3', '1.1.1.5'}
>>> ip_set2 - ip_set1
{'1.1.1.2', '1.1.1.10'}

Python基本结构

  • 选择结构
#注意缩紧和冒号
if 条件语句:
    语句块
elif 条件语句:
    语句块
else:
    语句块

例子:

astral@tujihedeiMac ~ % cat /Volumes/C2KP/proto.py 
protocol = input("Please input protocol name:")
protocol = protocol.lower()
if protocol == "tcp" :
    print("TCP' s protocol id is 6" )
elif protocol == "udp" :
    print("UDP' s protocol id is 17" )
else:
    print (" UNKOWN protocol" )%                                            astral@tujihedeiMac ~ % python3 /Volumes/C2KP/proto.py 
Please input protocol name:tcp
TCP' s protocol id is 6
astral@tujihedeiMac ~ % python3 /Volumes/C2KP/proto.py 
Please input protocol name:TCP
TCP' s protocol id is 6
astral@tujihedeiMac ~ % python3 /Volumes/C2KP/proto.py 
Please input protocol name:udp
UDP' s protocol id is 17
astral@tujihedeiMac ~ % python3 /Volumes/C2KP/proto.py 
Please input protocol name:ospf
 UNKOWN protocol
运算符 说明
== 相等
< 小于
> 大于
<= 小于等于
>= 大于等于
!= 不等于
is 两个对象是相同的对象
is not 两个对象是不同的对象
in 成员关系
not in 非成员关系

in 运算符

>>> version="17.1R2.8"
>>> "R" in version
True
>>> ip_address=["1.1.1.1","1.1.1.2","1.1.1.4"]
>>> "1.1.1.1" in ip_address#字符串必须相等
True
>>> "1.1.1.3" in ip_address
  • 循环结构
    for
for 变量 in 序列(字符串、列表、元组、集合、字典等):
    语句
astral@tujihedeiMac C2KP % cat bgp_for.py 
#!/usr/bin/python
bgp_peers = ["10.1.1.100","10.1.1.101","10.1.1.102","10.1.1.103"]
print("router bgp 100")
for peer in bgp_peers:
    print(peer)
    print("neighbor %s remote-as 100" %peer)
    print("neighbor %s update-source 1o0" %peer)
print("exit")%                                                                  astral@tujihedeiMac C2KP % python bgp_for.py 
router bgp 100
10.1.1.100
neighbor 10.1.1.100 remote-as 100
neighbor 10.1.1.100 update-source 1o0
10.1.1.101
neighbor 10.1.1.101 remote-as 100
neighbor 10.1.1.101 update-source 1o0
10.1.1.102
neighbor 10.1.1.102 remote-as 100
neighbor 10.1.1.102 update-source 1o0
10.1.1.103
neighbor 10.1.1.103 remote-as 100
neighbor 10.1.1.103 update-source 1o0
exit

for else
这个结构可以认为是for结构等一个增强。对于退出for循环结构有三种可能:
第一种是正常退出,即遍历了整个序列中的元素后退出。
第二种是使用break语句在特定条件下强制退出
第三种是在函数中使用了return语句进行强制退出。
这里for…else语句适合第二种情况,即当正常退出的时候,再执行else中的语句,如果是break退出时,则不执行else内的语句

#!/usr/bin/python
ip_list = ["10.1.1.1","10.1.1.2","10.1.1.3","10.1.1.4"]
IP = input("Please input ip address: ")
for ip in ip_list:
    if ip == IP:
        print("IP address: %s be found in the ip list" %IP)
        break
else:
    print("Can't find the IP address in: %s in the ip lisr" %IP)

while

while 条件判断语句:
    语句

例子:

#!/usr/bin/python
devices={"R1":"1.1.1.1","R2":"1.1.1.2","R3":"1.1.1.3","R4":"1.1.1.4"}
while True:
    router_list = devices.keys()
    router_list = sorted(router_list)#基于ASCII码排序
    for router_name in router_list:
        print(router_name)
    print("input e to exit ")
    router_input = input("Please select one router: ")
    router_input = router_input.upper()#全大写
    if router_input in devices.keys():
        print("I will connect to router %s %s" %(router_input,devices[router_input]))
    elif router_input == "E" :
        break
    else:
        print("unkonwn host")

Python中函数与对象

函数的定义

def 名称(参数):#如果没有参数可以忽略,如果有多个参数需要用逗号分开
    语句
    return 值或表达式#不是必须的,但是推荐有返回值

例:

def ipv4_to_int(ipv4):
    ipv4 = [int(x) for x in ipv4.split(".")]#推导式:将ip地址以.切分后转换为int累心存储到列表ipv4中
    ipv4_int = (ipv4[0] << 24) +(ipv4[1] << 16) + (ipv4[2] << 8) +ipv4[3]#位移操作,位运算
    return ipv4_int

def int_to_ipv4(ip_int):
    ipv4 = []
    for x in (24,16,8,0):
        ipv4.append(str(ip_int >> x & 0xFF))#按位与只保留最后16位
    return ".".join(ipv4)#str.join(sequence) 返回通过指定字符连接序列中元素后生成的新字符串。
strat_ip = "10.1.1.253"
for x in range(0,4):#range(start, stop[, step]),start: 计数从 start 开始,stop: 计数到 stop 结束,但不包括 stop,step:步长,默认为1
    ip_address = ipv4_to_int(strat_ip) + x * 4
    ip_address = int_to_ipv4(ip_address)
    print("interface gigaEthern0/1/%d" %x)
    print(" ip address %s 255.255.255.252" %ip_address)
    print(" no shutdown")

函数的参数

def connect(hostname,username="admin",password="123",port=23):
    print("connect to %s ...port %d" %(hostname,port))
    print("username is %s" %username)
    print("password is %s" %password)
connect(*["r2","net_admin","net_admin",22])#将列表作为一个参数传入,*的作用是将列表展开
connect(**{"hostname":"r3","port":2202,"username":"net_ops"})#在字典中找到对应的值

python的对象
Python是完全面向对象的语言,在python中一切都是对象
对于一台路由器而言,通常有一些属性:设备的主机名、管理地址、管理的方式(telnet、ssh、console)、其运行的路由协议等。对路由器对还有一些常用的操作:重新启动、修改配置、保存配置、获取流量信息等。前者都是对象的属性,通常是一些值;后者是对对象操作的方法,这里有一个动作执行的过程
属性:router.hostname
方法:router.get_hostname()

创建对象

class 对象名:
    def 方法名():
        语句
#codeing=utf-8
class Router(object):
    ,,,
    路由器的定义
    参数为一个字典:
    {
    "hostname": "R1",
    "mgmt_ip":"1.1.1.1"
    "username":"admin"
    "password":"admin"
    "mgmt_type":"SSH"
    }
,,,

    def __init__(self,router_params={}):
        self.mgmt_ip = router_params.get("mgmt_ip",None)
        self.hostname = router_params.get("hostname",self.mgmt_ip)
        self.username = router_params.get("username",None)
        self.password = router_params.get("password",None)
        self.mgmt_type =router_params.get("mgmt_type","SSH")
    def connect(self):
        if not self.mgmt_ip:
            raise AttributeError("you miss mgmt ip address")
        if not self.username:
            raise AttributeError("you miss mgmt a username")
        if not self.password:
            raise AttributeError("you miss mgmt a password")
        print("start to connect %s" %self.hostname)

对象的继承

#codeing=utf-8
class Router(object):
    def __init__(self,params={}):#创建时就会被调用
        self.hostname = params.get("hostname",None)
        self.mgmt_ip = params.get("mgmt_ip",None)
    def connect(self):
        print("connect to %s, ip is %s" %(self.hostname,self.mgmt_ip))

class CiscolOS(Router):
    def __init__(self,params={}):
        super().__init__(params)
        self.vendor = params.get("vendor")
    def goto_enable(self):
        print("goto enable mode")
        

device_info = {"hostname":"R1","mgmt_ip":"10.1.1.1","vendor":"CiscolOS"}
device = CiscolOS(device_info)
print("device.vendor is :",device.vendor)
device.connect()
device.goto_enable()

Python中的模块

  • 任何python程序文件都可以作为模块导入到另外一个python程序中
  • 模块是比类和函数更大一些的功能组合,可以由一个或者多个文件来构成
  • 模块就是一段已经写好的完成一部分特定功能的程序
astral@tujihedeiMac python % cat module.py                
import sys
args = sys.argv
if len(args) == 1:
    print("please input destination IP address")
elif len(args) > 1:
    print("Let's test the host, IP is %s" %args)%                           
astral@tujihedeiMac python % python module.py                
please input destination IP address
astral@tujihedeiMac python % python module.py 1.1.1.1        
Let's test the host, IP is ['module.py', '1.1.1.1']
astral@tujihedeiMac python % python module.py 1.1.1.1 2.2.2.2
Let's test the host, IP is ['module.py', '1.1.1.1', '2.2.2.2']

后续的内容主要是介绍一些第三方模块的使用

Python Web编程

Web的组成

什么是Web?

实现整个Web需要什么
客户端
1.发送请求并接收返回消息(通用技术:浏览器)
2.触发发出请求的动作(业务逻辑;url,ajax…)
3.处理返回的数据(业务逻辑;html,jquery…)
服务端
1.接收请求,将其翻译为程序可识别数据并转交特定的程序处理(通用技术;)
2.按照请求数据进行处理(业务逻辑:python)
3.将结果翻译为可传输的语言,返回给请求者(通用技术;)

实现一个Web应用,需要处理以上的所有问题点。但是这些问题点设计的内容非常复杂而且深奥。所以我们可以寻求成熟的框架,帮我买将可以通用的功能实现。
针对这些通用功能,此类框架至少需要帮助我们实现一下几点:
1.建立监听请求的服务程序
2.可以解析请求成python的数据格式
3.根据请求内容,调用对应的处理方法
4.将处理结果返回

Django框架

Django是一个由python编写的开放源代码Web应用框架!采用了MTV的框架模式,分别为模型M、模版T、视图V。

  • 用于创建模型的对象关系映射
  • 为最终用户设计的完美管理界面
  • 一流的URL设计
  • 设计者友好的模版语言
  • 缓存系统
  • 表单处理
  • 国际化
  • 优秀的中间件设计
  • 静态文件处理

django实现了web server(WSGI:监听、解析、返回)和web app(业务逻辑处理)的功能;使用django框架,我们只需要实现业务逻辑处理部分即可
Web app(业务逻辑处理)包含:URL分发、业务处理、模版支持、数据库处理、服务发布等部分

Django流程


需要开发什么?
浏览器需要可静态展示,动态操作!那么就至少需要返回的结果内包含一种或多种以下数据:可做页面渲染的html数据;可解析的数据格式(如json)
路由对不同的请求应该对应不同的方法,使用完美要建立请求和方法的映射关系
方法主要负责处理业务逻辑并与数据库打交道,可对数据进行增删改查等操作。最后返回请求所需要的内容

Django的搭建

准备环境:

  1. 准备好python环境
  2. 安装pip工具包
  3. 安装virtualenvwrapper(建议使用虚拟环境)
  4. 安装django

搭建django项目:

  1. 创建project:django-admin.py startproject project_name
  2. 新建app:python manage.py startapp app_name
    该命令会创建一个app_name的文件夹,一般主要关注views.pymodels.py
    运行项目
    python manage.py runserver 0.0.0.0:8081
#macOS安装和使用virtualenvwrapper

首先安装好python环境,这里使用brew install python3完成安装

pip3 install virtualenv
pip3 install virtualenvwrapper
mkdir ~/pyenv
vim ~/.bash_profile

输入以下内容

export WORKON_HOME=~/pyenv
VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

完成后执行source ~/.bash_profile生效

使用方法:
创建虚拟环境mkvirtualenv envname
列出虚拟环境列表lsvirtualenv
删除虚拟环境rmvirtualenv envname
转换虚拟环境workon envname
退出虚拟环境deactivate
指定python路径mkvirtualenv -p /Users/astral/.localpython/bin/python3.7 env378

macOS安装任意版本python的方法(只安装不配置到系统环境变量中):

  1. 官网下载源码并解压,cd到对应目录
  2. ./configure --prefix=path
  3. make
  4. make install

待解决的问题:进入虚拟环境后pip的使用存在问题
完整构建自己的项目前端需要了解htmljsvue;后端需要了解pythondjangofunctionsqlormtemplates->html
Django还有很多强大的集成功能,包括数据库、超级管理员、表单、安全、国际化等,有兴趣可以逐步研究

SDN理论基础

SDN的前世今生

SDN是什么

SDN前的网络架构:

  • 垂直集成的封闭系统:厂商负责制
  • 网络功能的简单堆砌:一个问题,一个协议
  • 行业创新基本停滞:设备厂商独家话语权

SDN:
三者通过南、北向接口连接

  • 专用网络功能->SDN应用
  • 专用操作系统->SDN控制器
  • 专用硬件->SDN数据面(通用硬件)

两个权威组织的定义:

  • ONF:SDN architecture decouples(解耦合) the network and forwarding functions enabling the network control to become directly programmable and the underlying infrastructure to be abstracted for applications and network services. The OpenFlow® protocol is a foundational element for building SDN solutions.
  • ONLAB:SDN's key attributes include:separation of the data and control planes; a uniform, vendor-agnostic interface(called OpenFlow) between control and data planes; a logically centralized(逻辑上集中) control plane that offers a consistent, network-wid view to programmers or operators.

一段话描述SDN:
SDN是一种新的网络体系结构(不是一种新的网络概念),给传统网络带来最大的改变是网络可编程(包括整个网络和单个网络设备)和开放性。网络用户追逐SDN的关键是想获得更多的网络可编程能力,获得更多的网络定制开发能力和自主权。SDN的开放分层架构加速了网络产业的参与度,越来越多的网络用户、网络软件公司和初创公司都加入到网络产业中来,这种开放竞争进一步加速整个产业的创新。

SDN的解读:

  • 网络开放可编程
  • 数控分离
  • 逻辑上集中控制

网络开放可编程的阶段:

  1. 网络设备可配置:网络设备管理——(CLI、Web GUI、SNMP、NETCONIF)——>数据平面和数据平面
  2. 数控分离:控制平面和数据平面分离,网络应用控制控制平面,通过数据面接口控制数据平面
  3. OpenFlow:网络应用程序通过OpenFlow控制器控制可编程数据平面
  4. SDN控制器:SDN控制器通过南北向接口与SDN应用、数据平面交互
  5. NPL:SDN应用通过网络编程语言操作SDN控制器和NPL编译器;然后通过OpenFlow2.0与通用可编程数据平面交互(注意网络编程语言和P4的区别)

    SDN体系结构

    SDN的分层体系结构:应用平面、控制平面、数据平面

    SDN数据平面:可编程化,最理想状态下,用户可以定义设备的功能(如路由器、交换机、防火墙)(Openflow还并不能实现)
    SDN控制平面:

    SDN典型应用

    过去的杀手级应用

  • 数据中心网络虚拟化——Open vSwitch 改造办法:vSwitch升级到Open vSwitch或者升级硬件网络设备到SDN的设备。举例:VMware NSX方案
  • SD-WAN软件定义广域网:降低走专线成本。通过统一的控制器收集各数据中心之间的流量需求,从而进行统一的流量调度,对带宽灵活地按需分配,最大程度地优化WAN的网络利用率。

    关键点:用户在这个网络应用中是否需要足够的可编程能力
    其他应用场景:
  • 软件定义园区网
  • 提升内部运营的智能化和灵活性
  • 软件定义局域网
  • 无线蜂窝网
    ###OpenFlow概述
    OpenFlow是实践SDN的首选
    OpenFlow是主流南向接口

OpenFlow最初是为了给研究人员提供一个开放平台,鼓励设备厂商在产品中支持OpenFlow,研究人员就可以在真是网络环境中验证新网络协议、架构和算法的原型。
《OpenFlow:Enabling Innovation in Campus Networks》

OpenFlow是什么

SDN≠OpenFlow

  • ONF:OpenFlow® is the first standard communications interface defined between the control and forwarding layers of an SDN architecture. OpenFlow® allows direct access to and manipulation of the forwarding plane of network devices.
  • Wikipedia:OpenFlow is a communications protocol that gives access to the forwarding plane of a network switch or router over the network. OpenFlow enables network controllers to determine the path of network packets across a network of switches.

  • 开放的南向接口(API)

  • 通用转发抽象模型


    Flow Table的具体功能可由用户自行定义

  • 网络x86指令集

历史意义

  • 网络设备的API
  • 网络x86指令集的尝试
  • 经验教训

SDN控制器编程

SDN及ODL介绍

  • SDN概念介绍
  • OpenDayLight产生背景、发布版本、架构特点、主要功能及子项目间关系
    OpenDaylight是SDN开发及运行平台,版本号根据元素周期表命名。
    基于OSGi的模块化设计
    多南向协议——OpenFlow,Netconf,OVSDB
    模型驱动的业务抽象层(MD-SAL)是ODL的核心
    全分布式的消息及存储机制
  • 操作实验——实验环境介绍,ODL版本下载,启动,功能安装,基本配置
  • 背景知识——OSGi及Karaf基本概念
    ###ODL初体验实验
    了解并掌握ODL的启动方式,JVM的
    ###OSGi及Karaf
    ###OFPlugin&Mininet
    ###RESTCONF介绍
    ###Maven基础
    ###ODL APP开发思路
    ###ODL Hello
    ###子项目L2Switch

作者 Assaultcore

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注