LBaaS v1与v2区别

LBaaS v1: introduced in Juno (deprecated in Liberty)(lbaasv1在liberty版本开始被废除)
LBaaS v2: introduced in Kilo(v2在kilo版本开始使用)

两种方式都是使用了agents,这个agent获取HAProxy的配置然后处理HAProxy的damon。
LBaaS v2增加了listeners的功能。LBaaS v2允许在一个load balancer的IP上配置多个listener的port
另一种实现方式是名为Octavia,它用独立的API和线程,通过在compute服务生成的虚拟机中来创建load balancer,而且不需要为Octavia创建agent

目前没有lbaas从v1迁移到v2的机制,如果选择从v1迁移到v2,那么据必须重新创建loadblancers,pools和health monitors。

这是loadbalancer的新的架构图

image

Load balancer

load balancer占用一个neutron network的port,然后将将一个IP绑在load balancer上

Listener

Load balancers可以被从多个port上获取requests。每一个port被一个listener监听

Pool

一个pool中有多个Member

Member

Member就是具体接收流量的虚拟机

Health monitor

Members虚拟机可能会时不时的下线,monitor的工作是将流量引导到健康的member上去。Health monitor与pools绑定在一起。

LBaaS v2

LBaaS v2 可以通过service plug-ins实现多种操作方式。两种常用的方式是通过agent或者Octavia services. 两种实现方式都采用了 LBaaS v2 API.

配置

配置可以参考 https://docs.openstack.org/mitaka/networking-guide/config-lbaas.html git checkout stable/ocata
yum install -y openstack-neutron-lbaas

image

注意点

my configuration is as follows: /etc/neutron/neutron.conf: service_plugins=networking_odl.l3.l3_odl.OpenDaylightL3RouterPlugin,neutron_lbaas.services.loadbalancer.plugin.LoadBalancerPluginv2 /etc/neutron/neutron_lbaas.conf: service_provider=LOADBALANCERV2:opendaylight:networking_odl.lbaas.driver_v2.OpenDaylightLbaasDriverV2:default Then I run the command “service neutron-server restart “,

测试

image

image

opendaylight_v1与v2区别

在Neutron服务节点更改配置

1
2
3
4
5
6
7
8
9
10
openstack@network:~$ sudo vi /etc/neutron/plugins/ml2/ml2_conf.ini
[ml2]
type_drivers = flat,vxlan
tenant_network_types = vxlan
mechanism_drivers = opendaylight 

[ml2_odl]
password = admin
username = admin
url = http://192.168.51.110:8080/controller/nb/v2/neutron

安装odl-networking还是比较推荐使用源码安装,这样的话比较好控制。在使用
python setup.py install
的时候可能出现以下问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@controller(keystone_admin)]# python setup.py install
Traceback (most recent call last):
  File "setup.py", line 29, in <module>
    pbr=True)
  File "/usr/lib64/python2.7/distutils/core.py", line 112, in setup
    _setup_distribution = dist = klass(attrs)
  File "/usr/lib/python2.7/site-packages/setuptools/dist.py", 
  line 265, in __init__
    self.fetch_build_eggs(attrs.pop('setup_requires'))
  File "/usr/lib/python2.7/site-packages/setuptools/dist.py", line 289
  , in fetch_build_eggs
    parse_requirements(requires), installer=self.fetch_build_egg
  File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 630, 
  in resolve
    raise VersionConflict(dist,req) # XXX put more info here
pkg_resources.VersionConflict: 
(pbr 1.10.0 (/usr/lib/python2.7/site-packages),
Requirement.parse('pbr>=2.0.0'))

这种情况的话只需要把git branch调整为当前的openstack环境的版本就可以了

配置步骤

odl-networking安装完成之后需要同步数据库,在数据库中增加journal和maintenance数据,否则会报错

  
[root@localhost ~(keystone_admin)]# sudo su -s /bin/sh -c "neutron-db-manage --config-file /etc/neutron/neutron.conf  --config-file /etc/neutron/plugins/ml2/ml2_conf.ini upgrade head" neutron
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
  Running upgrade for neutron ...
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
  OK
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
  Running upgrade for networking-odl ...
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> b89a299e19f9, Initial odl db, branchpoint
INFO  [alembic.runtime.migration] Running upgrade b89a299e19f9 -> 247501328046, Start of odl expand branch
INFO  [alembic.runtime.migration] Running upgrade 247501328046 -> 37e242787ae5, Opendaylight Neutron mechanism driver refactor
INFO  [alembic.runtime.migration] Running upgrade 37e242787ae5 -> 703dbf02afde, Add journal maintenance table
INFO  [alembic.runtime.migration] Running upgrade 703dbf02afde -> 3d560427d776, add sequence number to journal
INFO  [alembic.runtime.migration] Running upgrade b89a299e19f9 -> 383acb0d38a0, Start of odl contract branch
INFO  [alembic.runtime.migration] Running upgrade 383acb0d38a0 -> fa0c536252a5, update opendayligut journal
  OK
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
  Running upgrade for neutron-lbaas ...
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
  OK

这步骤操作完成之后需要重启neutron-server

odl-networking数据库

数据库中含有journal和maintenance两张表

image

journal表结构

image

maintenance表结构

image

复用代码

如果想使用这个脚本为自己的sdn控制器服务,而不是opendaylight的话,可以删除一些代码就可以使用,如果你的控制器并没有自己的数据库的话,那么就不需要这个odl-networking程序去进行数据库同步,可以注释掉一些代码(基于newton branch)

image

image

简要:

由于odl-networking中有很多脚本,这个脚本只针对配置qos service

QoS 被定义为保证某些网络要求像带宽、 延迟、 抖动和可靠性以满足应用程序供应商和最终用户之间的服务级别协议 (SLA) 的能力。
交换机和路由器等网络设备可以标记流量以便处理具有较高的优先级,以满足的 QoS 在 SLA 下商定的条件。在其他情况下,某些网络的流量,如语音 IP (VoIP) 和视频流需要传输的最小带宽的保障。在没有网络 QoS 管理系统上,所有交通将都转交”尽力”的方式,使它无法保证向客户提供服务。 QoS 是一种先进的服务插件。 QoS组件与其余 OpenStack neutron其他功能解耦,它可通过 ml2 扩展驱动程序。

配置

To enable the service, follow the steps below:

On network nodes:

Add the QoS service to the service_plugins setting in /etc/neutron/neutron.conf. For example:


service_plugins = \
neutron.services.l3_router.l3_router_plugin.L3RouterPlugin,
neutron.services.metering.metering_plugin.MeteringPlugin,
neutron.services.qos.qos_plugin.QoSPlugin
Optionally, set the needed notification_drivers in the [qos] section in /etc/neutron/neutron.conf (message_queue is the default).


[qos]
...
notification_drivers = odl-qos

image

In /etc/neutron/plugins/ml2/ml2_conf.ini, add qos to extension_drivers in the [ml2] section. For example:


[ml2]
extension_drivers = port_security, qos
If the Open vSwitch agent is being used, set extensions to qos in the [agent] section of /etc/neutron/plugins/ml2/openvswitch_agent.ini. For example:  


[agent]
extensions = qos

On compute nodes:

In /etc/neutron/plugins/ml2/ml2_conf.ini, add qos to the extensions setting in the [agent] section. For example:


[agent]
extensions = qos

QoS currently works with ml2 only (SR-IOV, Open vSwitch, and linuxbridge are drivers that are enabled for QoS in Mitaka release).

Trusted tenants policy.json configuration
If tenants are trusted to administrate their own QoS policies in your cloud, neutron’s file policy.json can be modified to allow this.

Modify /etc/neutron/policy.json policy entries as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"get_policy": "rule:regular_user",
"create_policy": "rule:regular_user",
"update_policy": "rule:regular_user",
"delete_policy": "rule:regular_user",
To enable bandwidth limit rule:

"get_policy_bandwidth_limit_rule": "rule:regular_user",
"create_policy_bandwidth_limit_rule": "rule:admin_only",
"delete_policy_bandwidth_limit_rule": "rule:admin_only",
"update_policy_bandwidth_limit_rule": "rule:admin_only",
"get_rule_type": "rule:regular_user",
To enable DSCP marking rule:

"get_policy_dscp_marking_rule": "rule:regular_user",
"create_dscp_marking_rule": "rule:admin_only",
"delete_dscp_marking_rule": "rule:admin_only",
"update_dscp_marking_rule": "rule:admin_only",
"get_rule_type": "rule:regular_user",

用户操作

QoS policies are only created by admins with the default policy.json. Therefore, you should have the cloud operator set them up on behalf of the cloud tenants.

If tenants are trusted to create their own policies, check the trusted tenants policy.json configuration section.

First, create a QoS policy and its bandwidth limit rule:


$ neutron qos-policy-create bw-limiter

Created a new policy:
+-------------+--------------------------------------+
| Field       | Value                                |
+-------------+--------------------------------------+
| description |                                      |
| id          | 0ee1c673-5671-40ca-b55f-4cd4bbd999c7 |
| name        | bw-limiter                           |
| rules       |                                      |
| shared      | False                                |
| tenant_id   | 85b859134de2428d94f6ee910dc545d8     |
+-------------+--------------------------------------+

$ neutron qos-bandwidth-limit-rule-create bw-limiter --max-kbps 3000 \
  --max-burst-kbps 300

Created a new bandwidth_limit_rule:
+----------------+--------------------------------------+
| Field          | Value                                |
+----------------+--------------------------------------+
| id             | 92ceb52f-170f-49d0-9528-976e2fee2d6f |
| max_burst_kbps | 300                                  |
| max_kbps       | 3000                                 |
+----------------+--------------------------------------+

验证

抓到了qos操作的rest请求

image

参考文献:

https://docs.openstack.org/mitaka/networking-guide/config-qos.html https://docs.openstack.org/networking-odl/ocata/installation.html

JSON概述

  • JSON: JavaScript 对象表示法( JavaScript Object Notation) 。是一种轻量级的数据交换格式。 它基于ECMAScript的一个子集。 JSON采用完全独立于语言的文本格式, 但是也使用了类似于C语言家族的习惯( 包括C、 C++、 C#、 Java、 JavaScript、 Perl、 Python等) 。这些特性使JSON成为理想的数据交换语言。 易于人阅读和编写, 同时也易于机器解析和生成(一般用于提升网络传输速率)
  • JSON 解析器和 JSON 库支持许多不同的编程语言。 JSON 文本格式在语法上与创建 JavaScript 对象的代码相同。 由于这种相似性, 无需解析器, JavaScript 程序能够使用内建的 eval() 函数, 用 JSON 数据来生成原生的 JavaScript 对象
  • JSON 是存储和交换文本信息的语法。 类似 XML。 JSON 比 XML 更小、 更快, 更易解析
  • JSON 具有自我描述性, 语法简洁, 易于理解

JSON语法

  • JSON 语法是 JavaScript 对象表示法语法的子集。数据在键/值对中;数据由逗号分隔;花括号保存对象, 也称一个文档对象;方括号保存数组, 每个数组成员用逗号隔开, 并且每个数组成员可以是文档对象或者数组或者键值对

JSON基于两种结构:

  • “名称/值”对的集合(A collection of name/value pairs),不同的编程语言中,它被理解为对象(object),纪录(record),结构(struct),字(dictionary),哈希表(hashtable),有键列表(keyed list),或者关联数组 (associative array)
  • 值的有序列表(An ordered list of values),在大部分语言中,它被实现为数组(array),矢量(vector),列表(list),序列(sequence)

JSON的三种语法:

  • 键/值对 key:value,用半角冒号分割。 比如 “name”:”Faye”
  • 文档对象 JSON对象写在花括号中,可以包含多个键/值对。比如{ “name”:”Faye” ,”address”:”北京” }。
  • 数组 JSON 数组在方括号中书写: 数组成员可以是对象,值,也可以是数组(只要有意义)。 {“love”: [“乒乓球”,”高尔夫”,”斯诺克”,”羽毛球”,”LOL”,”撩妹”]}

Json取数

根据OpenStack LB的API进行数据解析,提供的API如下:

image

实现的代码如下:


/**
The JSON document tree node, which is a basic JSON type
**/
	typedef struct json_value
	{
		enum json_value_type type;	/*!< the type of node */
		char *text;	/*!< The text stored by the node. It stores UTF-8 strings and is used exclusively by the JSON_STRING and JSON_NUMBER node types */

		/* FIFO queue data */
		struct json_value *next;	/*!< The pointer pointing to the next element in the FIFO sibling list */
		struct json_value *previous;	/*!< The pointer pointing to the previous element in the FIFO sibling list */
		struct json_value *parent;	/*!< The pointer pointing to the parent node in the document tree */
		struct json_value *child;	/*!< The pointer pointing to the first child node in the document tree */
		struct json_value *child_end;	/*!< The pointer pointing to the last child node in the document tree */
	} json_t;

INT4 createOpenstackClbaaslistener(char* jsonString, void* param)
{
	const char *tempString = jsonString;
	INT4 parse_type = 0;
	json_t *json=NULL,*temp=NULL;
	char tenant_id[48] ={0};
	char lb_method[48] = {0};
	char protocol[48] = {0};
	char lb_pool_id[48] = {0};
	char lb_listener_id[48] = {0};
	INT4 client_timeout = 0;
	INT4 lb_port = 0;
	t_lb_health_check lb_health_check ={0};
	t_lb_session_persistence lb_session_persistence ={0};
	
	parse_type = json_parse_document(&json,tempString);
	if((parse_type != JSON_OK)||(NULL == json))	
	{
		LOG_PROC("INFO", "%s failed", FN);
		return	GN_ERR;
	}
	
	json_t *lbaas_listeners = json_find_first_label(json, "listeners");
	if(lbaas_listeners&&lbaas_listeners->child){
		json_t *lbaas_listener = lbaas_listeners->child->child;
		while(lbaas_listener){
			temp = json_find_first_label(lbaas_listener, "lb_method");
			strcpy(lb_method,"");
			if(temp){
				if(temp->child->text){
					strcpy(lb_method,temp->child->text);
					json_free_value(&temp);
				}
			}
			temp = json_find_first_label(lbaas_listener, "protocol");
			strcpy(protocol,"");
			if(temp){
				if(temp->child->text){
					strcpy(protocol,temp->child->text);
					json_free_value(&temp);
				}
			}
			temp = json_find_first_label(lbaas_listener, "tenant_id");
			strcpy(tenant_id,"");
			if(temp){
				if(temp->child->text){
					strcpy(tenant_id,temp->child->text);
					json_free_value(&temp);
				}
			}
			temp = json_find_first_label(lbaas_listener, "client_timeout");
			if(temp){
				if(temp->child->text){
					//strcpy(client_timeout,temp->child->text);
					client_timeout = atoi(temp->child->text);
					json_free_value(&temp);
				}
			}
			temp = json_find_first_label(lbaas_listener, "lb_pool_id");
			strcpy(lb_pool_id,"");
			if(temp){
				if(temp->child->text){
					strcpy(lb_pool_id,temp->child->text);
					json_free_value(&temp);
				}
			}
			temp = json_find_first_label(lbaas_listener, "port");
			//strcpy(lb_port,"");
			if(temp){
				if(temp->child->text){
					//strcpy(lb_port,temp->child->text);
					lb_port = atoi(temp->child->text);
					json_free_value(&temp);
				}
			}
			
			json_t *health_checks = json_find_first_label(lbaas_listener, "health_check");
			if(health_checks)
			{//ip = temp->child->text;
				json_t *health_check = health_checks->child;
				while(health_check){
					temp = json_find_first_label(health_check, "delay");
					if(temp){
						//strcpy(lb_health_check.delay,temp->child->text);
						lb_health_check.delay = atoi(temp->child->text);
						json_free_value(&temp);
					}
					temp = json_find_first_label(health_check, "timeout");
					if(temp){
						lb_health_check.timeout = atoi(temp->child->text);
						json_free_value(&temp);
					}
					temp = json_find_first_label(health_check, "fail");
					if(temp){
						lb_health_check.fail = atoi(temp->child->text);
						json_free_value(&temp);
					}
				    temp = json_find_first_label(health_check, "type");
					if(temp){
						strcpy(&lb_health_check.type,temp->child->text);
						json_free_value(&temp);
					}
					temp = json_find_first_label(health_check, "rise");
					if(temp){
						lb_health_check.rise = atoi(temp->child->text);
						json_free_value(&temp);
					}
					temp = json_find_first_label(health_check, "enabled");
					if(temp){
						lb_health_check.enabled =  (JSON_TRUE == temp->child->type)?1:0;
						//strcpy(lb_health_check.enabled,temp->child->text);
						json_free_value(&temp);
				    }
					health_check = health_check->next;
				}
				json_free_value(&health_checks);
			}
			json_t *session_persistences = json_find_first_label(lbaas_listener, "session_persistence");
			if(session_persistences)
			{//ip = temp->child->text;
				json_t *session_persistence = session_persistences->child;
				while(session_persistence){
					temp = json_find_first_label(session_persistence, "type");
					if(temp){
						strcpy(lb_session_persistence.type,temp->child->text);
						json_free_value(&temp);
					}
					session_persistence = session_persistence->next;
				}
			json_free_value(&session_persistences);
			}
			temp = json_find_first_label(lbaas_listener, "lb_listener_id");
			strcpy(lb_listener_id,"");
			if(temp){
				if(temp->child->text){
					strcpy(lb_listener_id,temp->child->text);
					json_free_value(&temp);
				}
			}
			lbaas_listener = lbaas_listener->next;
		}
		json_free_value(&lbaas_listeners);
	}
	return  GN_OK;
}

指针与指针变量

在程序中声明变量后,编译器就会为改变量分配相应的内存单元。也就是说,每个变量在内存中会有相应固定的位置,有具体的地址。由于变量的数据类型不同,它所占的内存单元数也不相同。如下例声明了一些变量和数组。


int i = 18;                     //声明整形变量i并赋值
char c[5] = {89,90,91,92,93};   //声明字符型数组c并初始化
float f = 12.89;                //声明单精度浮点型变量f并赋值
double d = 1.414213;            //声明双精度浮点型变量d并赋值

在程序编译时,编译器讲指定这些变量和数组所需要的存储空间长度。程序运行中,由操作系统为这些变量和数组分配内存单元。整型变量所占用的内存为2字节,长度为5的字符型数组所占用的内存为5字节,单精度浮点型变量所占用的内存为4字节,双精度浮点型所占用的内存为8字节。由于计算机内存是最小的寻址单位是字节,设变量的存放从3000单元开始,则操作系统为这些变量和数组分配内存单元,如图所示。

1

变量在内存中按照数据类型的不同所占内存的大小也不同,每个变量都有具体的内存单元地址,如变量i在内存的地址是3000,占据2个字节后,数据c的内存首地址就为3002,变量f的内存地址为3008等。对内存中变量的访问,过去用”scanf(“%d”,&a)”表达式将数据输入变量的地址所指示的内存单元。那么访问变量首先应找到其内存的地址,或者说,一个地址唯一指向一个内存变量,称这个地址为变量的指针,如果将变量的地址保存在特定区域,用变量来存放这些地址,这样的变量就是指针变量,通过指针所指向变量的访问,也就是一种对变量的间接访问。
假设一组指针变量pi、pc、pf和pd分别指向上述的变量或数组i、c[]、f和d,指针变量也同样被存放在内存,二者的关系如下。指针变量的存储空间中存放的数据为对应变量或数组的内存地址,通过地址就可以访问对应的变量或数组。

2
图 指针变量与所指向变量的映射关系

指针变量的定义与引用

指针变量时包含内存地址的变量。一般的变量直接包含一个特定的值,而指针变量包含的是某一特定数据类型的内存地址,普通变量直接引用其中的值,普通变量直接引用其中的值,指针变量则间接引用所指向内存地址中的值。指针变量在使用前需要声明和初始化。

指针变量定义时,需要指定所指向的数据类型,声明指针变量的一般形式为:


数据类型  *变量名

“*” 运算符通常称为间接运算符或间接引用运算符,在声明中以这种方式使用间接运算符时,它用以表明的变量是指针变量。如下列源代码所示:


int *pi;    //声明一个整形指针变量
char *pc;   //声明一个字符型指针变量
float *pf;  //声明一个单精度度浮点型指针变量

声明为整形的指针变量*pi只能指向整形变量或者整形变量数组元素。声明为字符型的指针变只能指向字符型数据。指针变量声明后,才可以写入指向某种数据类型的变量的地址,或者说是为指针变量的初始化。如下列源代码所示:

1
2
3
4
5
6
7
int *pi,i=290;   //声明整型指针变量*pi和整型变量i,并为i赋初值
char *pc,c=65;   //声明字符型指针变量*pc和字符型变量c,并为c赋初值
float *pf,f=1.414;
//声明单精度浮点型指针变量*pf和单精度浮点型变量f,并为f赋初值
pi=&i;           //将整形指针变量*pi指向整型变量i
pc=&c;           //将字符形指针变量*pc指向字符型变量c
pf=&f;           //将单精度浮点型指针变量*pf指向单精度浮点型变量f

上述赋值语句pi=&i表示将变量i的地址赋给指针变量pi,此时pi就指向了i。3条赋值语句产生的效果是pi指向i,pc指向c,pf指向f,如如下图所示。&运算符称为取地址运算符,作用是取得变量的内存地址。

3

利用指针变量可以改变内存中某一单元的值,这是一种对系统底层的访问。指针变量为复杂的操作带来便利的同时,也存在很大的安全隐患,使用不当时极容易引起程序的终止甚至系统死机。利用指针变量间接引用变量的形式为:


*指针变量

间接运算符在这里的作用是访问指针变量所指向的内存单元的值,如下例:

1
2
3
4
int *pi,i=100;    //声明整型指针变量*pi和整型变量i,并为i赋初值
pi=&i;            //将整型指针变量pi指向整形变量i
*pi++;            //间接访问变量i,使变量i的值自增
printf("%d",i);   //输出i的值

该程序的输出为101,因为指针变量pi间接引用变量i,修改pi的值等同于修改变量i的值。如果将代码第4行改为


printf("%d",*pi);   //输出pi的值

该程序的输出还是101,*pi与i的作用是等同的。很多函数都需要取得变量的地址用以修改变量的值,如scanf()函数。在上例中,如果使用指针变量修改i的值,则可以用以下代码来实现。


scanf("%d",pi);     //从键盘输入获取数据,保存在变量i中

这条语句的作用等同于scanf(“%d”,&i),因为指针pi的值为变量i的地址,所以不能使用间接运算符。 C语言有两个指针运算符,分别是取地址运算符与间接引用运算符,这两个运算符都是一元运算符,它们的优先级仅次于一元算术运算符。