声明:
本博客欢迎转载,但请保留原作者信息!
作者:柯晓东
团队:华为杭州OpenStack团队
转载自http://blog.youkuaiyun.com/canxinghen/article/details/61615823
最近在用Murano来部署应用,关于Murano包及PL语言的使用,这里总结一下,便于后来者。
先说Murano包,典型的Murano包如下图所示。含有UI的称为Application,它能继承library,但是不能被其他Application继承了。library不含UI,一般是平台提供,里面含有丰富的公共方法和属性。
UI目录 下放能自动生成表单的yaml文件。Murano之所以好用,是因为含有用文本描述的UI,以此可以在新接入一个组件时UI的活由新组件提供,非常适合平台和服务解耦。Murano通过yaml描述的UI也不是什么都能体现。指定某个输入框一定要输入字符串或一定要有值,这它是能做到的。但是下面这种场景它做不到:某radio button的值,为A时,几个输入框必须输入;为B时,同样的几个输入框可以不输入。
UI支持数字、字符串、布尔型,是否必选等基本类型,还支持通过glance、neutron选择镜像、虚拟网络,但是不支持选择物理主机。它支持的类型如下:
类型 | 说明 |
---|---|
string | Django 字符串类型 |
boolean | Django 布尔类型 |
text | Django 文本类型 |
integer | Django 数字类型 |
password | 秘密类型 |
clusterip | 集群IP |
floatingip | 外网Ip |
domain | 域名 |
table | 表格 |
flavor | 规格 |
keypair | 密钥对 |
image | 镜像 |
azone | availability zoom |
imageType | 仅为镜像提供的属性 |
Resource目录 下放的是各式各样的脚本,这部分基本都和服务的实现有关。例如运行态的动态配置脚本,数据转换脚本等。
Classes目录 下放的是核心的yaml描述的部署文件。这些yaml就是用murano PL语言描述的类。这是这次介绍的核心部分。
Murano PL的类分为这么几部分:
1. 类名声明
2. 继承声明
3. 属性声明
4. 方法实现
前两部分和其他语言一样,就略过了。
在属性声明的地方,主要涉及给变量起了名字后,要写什么类型,作用范围。如下图是典型的属性声明的代码段:
这里的Contract表示类型声明,下表是所有可用的类型:
类型 | 说明 |
---|---|
$.int() | 整形 |
$.string() | 字符串 |
$.string().notNull() | 非空字符串 |
$.bool() | 布尔类型(true/false) 0会被转为false,其他转为true |
$.class(ns:ClassName) | 类 |
$.class(ns:ClassName).notNull() | 非空类 |
$.class(ns:Name).check($.p = 12) | 类,并且含有名为p的属性,且值为12 |
$.template(ns:ClassName) | 模板声明,一般用于脚本调用使用 |
[$.int()] | 数组 |
[$.int().notNull()] | 数组 |
[$.string()] | 数组 |
[$.int().check($ > 0)] | 正数的数组 |
[$.int(), 2] | 数组长度至少为2 |
[$.int(), 2, 5] | 数组长度至少为2,至多为5 |
{ A: $.int(), B: [$.string()] } | 字典,key是int,value是字符串 |
Usage是用来声明作用域,可填的值如下:
属性类型 | 说明 |
---|---|
In | 输入参数,该参数只能通过UI设置,不能在MuranoPL代码里面修改。这是类属性的默认类型。 |
Out | 输出参数,该参数只能在MuranoPL代码里面修改,不能通过UI设置。 |
InOut | 既能由UI设置,又能在MuranoPL代码修改的参数。 |
Const | 和In类似,不同的是只能被用户设置一次。 |
Runtime | 和Out类似,不同的是不会被序列化。 |
然后就来到方法实现,Murano PL是一种用python解释的类yaml的语言,它有如下的约束:
1. 赋值用冒号,且冒号后要跟空格
2. 等号表示判断
3. 逗号不能在字符串内使用
4. 变量要用$作为前缀
5. 单个$ 就是java里面的this,而$xxx 表示变量xxx,$.xxx 表示类变量xxx
基本的代码片段如下:
(1) if语句
- If: not $.getAttr(deployed, false)
Then:
- $.setAttr(deployed, true)
Else:
- $._environment.reporter.report($, "application has deployed")
- 1
- 2
- 3
- 4
- 5
(2) for循环
- For: applicationPort
In: $applicationPorts.where($.scope != host)
Do:
- $._environment.reporter.report($, 'Configuring etcd node {0}'.format($applicationPort))
- 1
- 2
- 3
- 4
(3) while循环
- $port: 1025
- While: not $._checkIfPortIsNotUsed($port, $protocol)
Do:
$port: $port + 1
- 1
- 2
- 3
- 4
(4) 并发执行(可指定并发数)
- Parallel:
- $.node1.deployInstance()
- $.node2.deployInstance()
- $.node3.deployInstance()
- $.node4.deployInstance()
- $.node5.deployInstance()
- $.node6.deployInstance()
- Limit: 2
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
(5) switch语句
Switch:
$predicate1():
- code
- block
$predicate2():
- code
- block
Default:
- code
- block
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
(6)异常处理
Try:
- code
- block
Catch:
With: keyError
As: e
Do:
- code
- block
Else:
- code
- block
Finally:
- code
- block
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
(7)调用脚本
- $resources: new(sys:Resources)
- $template: $resources.yaml('DeployOrionChef.template').bind(dict( port => $.port))
- $result: $.instance.agent.call($template, $resources)
- 1
- 2
- 3
- 4
(8)方法定义
getTitle:
Body:
- Return: $.title
checkIfPortUsed:
Arguments:
- port:
Contract: $.int().notNull()
- protocol:
Contract: $.string().notNull()
Body:
- Return: len(list($.serviceEndpoints.where($.port = $port).where($.protocol = $protocol))) = 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
常用的公共方法 如下:
方法 | 说明 |
---|---|
sleep(5) | 暂停5秒 |
now().year | 获取当前年份 |
id(xxx) | 取类实例的id |
super(xxx) | 取类实例的父类 |
xxx.name | 获取组件名 |
typeinfo(xxx) | 获取实例类型 |
typeinfo(xxx).type | 获取类名 |
typeinfo(xxx).version | 获取版本 |
typeinfo(xxx).package | 获取包 |
typeinfo(xxx).ancestors | 获取类的父类 |
typeinfo(xxx).properties | 获取类所有属性。并可通过typeinfo(xxx).properties[i].getValue(xxx) 获取xxx对象的第i个属性的值 |
typeinfo(xxx).methods | 获取类所有方法,并可通过typeinfo(xxx).methods[i].invoke(xxx, ‘bar’, 2) 调用xxx对象第i个方法 |
常用的数组方法如下:
方法 | 描述 |
---|---|
[0, 1, 2].first() | 取数组的第一个元素 |
[0, 1, 2].last() | 取数组的最后一个元素 |
[0, 1, 2].len() | 查询数组长度 |
range(5).toList() | 产生0到4的数组 |
dict(a => 1, b => 2) | 定义字典 |
[1, 2, 3].append(4, 5) | 数组相加 |
{“a” => 1, “b” => 2}.items() | 字典转数组 |
[1, 2, 3, 4, 5].where($ > 3) | 取数组第3位开始的子集 |
isList([1, 2]) | 判断是否是列表 |
isInteger(12.0) | 判断是否是整数 |
isNumber(12) | 判断是否数字 |
max(8, 2) | 取两者大者 |
min(8, 2) | 取两者小者 |
常用的字符串方法如下:
方法 | 描述 |
---|---|
str(123) | 数字转字符串 |
isString(1) | 判断是否是字符串 |
concat(“abc”, “de”, “f”) | 多个字符串合并 |
[“abc”, “de”, “f”].join(“|”) | 多个字符串合并 |
“|”.join([“abc”, “de”, “f”]) | 多个字符串合并 |
“abc”.len() | 字符串长度 |
“aB1c”.toUpper() | 转大写 |
“AB1c”.toLower() | 转小写 |
“abc de f”.split() | 分割字符串 |
” abcd “.trim() | 去掉前后空格 |
“abaab”.replace(“ab”, “cd”) | 替换字符串 |
“abc{foo}ab{bar}abc”.format(foo => ” “, bar => “,”) | 用参数填充字符串 |
“cabcdab”.indexOf(“ab”, 2) | 从前往后查关键字 |
“cabcdab”.lastIndexOf(“ab”) | 从后往前查关键字 |
“abc de”.toCharArray() | 字符串转数组 |
“abcd”.startsWith(“ab”, “xx”) | 是否以特定字符串开头 |
“abcd”.endsWith(“cd”, “xx”) | 是否以特定字符串结尾 |
在预定义的基础公共类(core-library)中,最重要的是environment和instance。他们分别代表着运行环境和虚拟机对象。这两个类的常用属性如下:
(1) 环境(std:Environment)的属性
属性 | 描述 | 默认类型 |
---|---|---|
name | 名字 | In |
applications | UI界面上能够显示出来的Application的列表。如果是new出来的对象,不在这个列表内。 | In |
agentListener | 和Murano Agent通信的属性, io.murano.system.AgentListener | Runtime |
stack | 和Heat交互用,常用的操作有current()、push()、updateTemplate($template)、setTemplate($template) | Runtime |
instanceNotifier | 获取部署实例时信息的类, io.murano.system.InstanceNotifier | Runtime |
defaultNetworks | 网络信息(io.murano.resources.Network). | In |
securityGroupManager | 安全组信息 | Runtime |
(2) 实例(res:Instance)的属性
属性 | 描述 | 默认类型 |
---|---|---|
name | nova的实例名 | In |
flavor | nova的套餐 | In |
image | glance的景象 | In |
keyname | nova的keypair | In |
agent | 和Murano Agent通信的属性io.murano.system.Agent. | Runtime |
ipAddresses | IP地址列表的数组,如果有浮动IP,放置在列表末尾 | Out |
networks | 虚拟机使用的网络 | In |
volumes | 虚拟机使用的卷信息 | In |
blockDevices | 如果从卷启动,需要指定(Volume class, device name, device type, boot order) | In |
assignFloatingIp | 是否需要指定浮动ip,默认False. | In |
floatingIpAddress | 浮动ip地址 | Out |
securityGroupName | 安全组名 | In |
其余还想知道的,可参看手册:http://murano.readthedocs.io/en/stable-liberty/appdev-guide/murano_pl.html
附1:用devstack在ubuntu安装murano。
(1)下载devstack
git clone https://git.openstack.org/openstack-dev/devstack
(2)配置apt-get使用阿里云的源(/etc/apt/sources.list)
deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
##测试版源
deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
# 源码
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
##测试版源
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
(3)在devstack目录新建local.conf
[[local|localrc]]
LOGFILE=stack.sh.log
LOG_COLOR=False
DATABASE_PASSWORD=stack
RABBIT_PASSWORD=stack
SERVICE_TOKEN=stack
SERVICE_PASSWORD=stack
ADMIN_PASSWORD=stack
disable_service n-net
enable_service q-svc
enable_service q-agt
enable_service q-dhcp
enable_service q-l3
enable_service q-meta
enable_service q-lbaas
enable_service q-vpn
Q_PLUGIN=ml2
ENABLE_TENANT_VLANS=True
IP_VERSION=4
GIT_BASE=https://github.com
IMAGE_URLS=http://download.cirros-cloud.net/0.3.4/cirros-0.3.4-x86_64-disk.img
# Enable Horizon
disable_service tempest
enable_service horizon
# Enable Heat
enable_service heat h-api h-api-cfn h-api-cw h-eng
# Enable Murano
enable_plugin murano https://git.openstack.org/openstack/murano
enable_service murano murano-api murano-engine
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
(4)在devstack目录下,运行stack.sh安装murano
附2:murano PL调试方法
murano会在 /tmp/murano-package-cache目录下,为已经上传的包弄缓存。通过直接修改该目录下的文件,可以免除重新上传包的麻烦动作,改之后直接在UI上测试即可。