Cisco ACI で Contract 設定用 XML を出力する Python スクリプトサンプル
Cisco ACI 上で普段の運用は Web GUI が使えれば十分です。しかし、「大量に設定を行いたい」といった場合は CLI や REST API を使いこなせると効率が上がります。今回は Python スクリプトを使って大量の Contract を設定するサンプルを示します。
Contract を表現する XML / Json
Contract を表現する XML / Json は以下のようになります。これらの XML と Json は全く同じ意味を持ちます。
XML
| <?xml version="1.0" encoding="UTF-8"?>
<imdata totalCount="1">
<vzBrCP descr="" name="ExtEpg1-1_ExtEpg2-1" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-1" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="PermitIP"/>
</vzSubj>
</vzBrCP>
</imdata>
|
Json
Json では値毎に改行している為、行数が長くなっています。
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
34
35
36
37
38
39
40
41
42
43
44
45 | {
"totalCount": "1",
"imdata": [
{
"vzBrCP": {
"attributes": {
"descr": "",
"name": "ExtEpg1-1_ExtEpg2-1",
"nameAlias": "",
"ownerKey": "",
"ownerTag": "",
"prio": "unspecified",
"scope": "context",
"targetDscp": "unspecified"
},
"children": [
{
"vzSubj": {
"attributes": {
"consMatchT": "AtleastOne",
"descr": "",
"name": "Subject-1",
"nameAlias": "",
"prio": "unspecified",
"provMatchT": "AtleastOne",
"revFltPorts": "yes",
"targetDscp": "unspecified"
},
"children": [
{
"vzRsSubjFiltAtt": {
"attributes": {
"directives": "",
"tnVzFilterName": "PermitIP"
}
}
}
]
}
}
]
}
}
]
}
|
Contract を出力する Python スクリプト
Contract を出力する Python スクリプト例は以下の通りです。 引数の解析で docopt に依存している為、事前に docopt をインストールしておく必要があります。
スクリプトは以下の通りです。名前は contract-builder.py
としました。
contract-builder.py
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 | #!/usr/bin/env python
"""Usage:
contract-builder.py [-f <FILTER>] [-s <SUBJECT>] [-t <TENANT>] <INPUTFILE>
contract-builder.py -h | --help
options:
<INPUTFILE> Specify input xml file.
-f, --filter <FILTER> Specify filter name [default: Filter-1].
-h, --help Show this help message and exit.
-s, --subject <SUBJECT> Specify subject name [default: Subject-1].
-t, --tenant <TENANT> Specify tenant name [default: Tenant-1].
"""
from docopt import docopt
import xml.dom.minidom as minidom
import xml.etree.ElementTree as ET
if __name__ == '__main__':
args = docopt(__doc__)
inputfile = open(args.get('<INPUTFILE>'))
line = inputfile.readline()
imdata = ET.Element('imdata')
imdata.set('totalCount', '1')
fvTenant = ET.SubElement(imdata, 'fvTenant')
fvTenant.set('descr', '')
fvTenant.set('dn', 'uni/tn-' + args['--tenant'])
fvTenant.set('name', args['--tenant'])
fvTenant.set('ownerKey', '')
fvTenant.set('ownerTag', '')
while line:
contract_name = line.rstrip()
vzBrCP = ET.SubElement(fvTenant, 'vzBrCP')
vzBrCP.set('descr', '')
vzBrCP.set('dn', 'uni/tn-' + args['--tenant'] + '/brc-' + contract_name)
vzBrCP.set('name', contract_name)
vzBrCP.set('nameAlias', '')
vzBrCP.set('ownerKey', '')
vzBrCP.set('ownerTag', '')
vzBrCP.set('prio', 'unspecified')
vzBrCP.set('scope', 'context')
vzBrCP.set('targetDscp', 'unspecified')
vzSubj = ET.SubElement(vzBrCP, 'vzSubj')
vzSubj.set('consMatchT', 'AtleastOne')
vzSubj.set('descr', '')
vzSubj.set('name', args['--subject'])
vzSubj.set('nameAlias', '')
vzSubj.set('prio', 'unspecified')
vzSubj.set('provMatchT', 'AtleastOne')
vzSubj.set('revFltPorts', 'yes')
vzSubj.set('targetDscp', 'unspecified')
vzRsSubjFiltAtt = ET.SubElement(vzSubj, 'vzRsSubjFiltAtt')
vzRsSubjFiltAtt.set('directives', '')
vzRsSubjFiltAtt.set('tnVzFilterName', args['--filter'])
line = inputfile.readline()
inputfile.close()
string = ET.tostring(imdata, 'utf-8')
pretty_string = minidom.parseString(string).toprettyxml(indent=' ')
print(pretty_string)
|
contract-builder.py 実行例
事前に作成したい Contract 名を羅列したファイルを用意しておきます。
| # cat CONTRACTS
Epg1_EpgA
Epg2_EpgB
Epg3_EpgC
|
contract-builder.py
の引数に事前に用意したファイル名を指定すると標準出力へ Contract を表現する XML を出力します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | # python contract-builder.py CONTRACTS
<?xml version="1.0" ?>
<imdata totalCount="1">
<fvTenant descr="" dn="uni/tn-Tenant-1" name="Tenant-1" ownerKey="" ownerTag="">
<vzBrCP descr="" dn="uni/tn-Tenant-1/brc-Epg1_EpgA" name="Epg1_EpgA" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-1" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="Filter-1"/>
</vzSubj>
</vzBrCP>
<vzBrCP descr="" dn="uni/tn-Tenant-1/brc-Epg2_EpgB" name="Epg2_EpgB" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-1" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="Filter-1"/>
</vzSubj>
</vzBrCP>
<vzBrCP descr="" dn="uni/tn-Tenant-1/brc-Epg3_EpgC" name="Epg3_EpgC" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-1" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="Filter-1"/>
</vzSubj>
</vzBrCP>
</fvTenant>
</imdata>
|
Tenant、Subject、Filter 名を指定したい場合は以下のオプションを指定します。
オプション |
意味 |
デフォルト値 |
-t または --tenant |
Tenant 名 |
Tenant-1 |
-s または --subject |
Subject 名 |
Subject-1 |
-f またh --filter |
Filter 名 |
Filter-1 |
各オプションを指定した場合の実行例は以下の通りです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | # python contract-builder.py CONTRACTS --tenant Z --subject Subject-Z --filter Filter-Z
<?xml version="1.0" ?>
<imdata totalCount="1">
<fvTenant descr="" dn="uni/tn-Z" name="Z" ownerKey="" ownerTag="">
<vzBrCP descr="" dn="uni/tn-Z/brc-Epg1_EpgA" name="Epg1_EpgA" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-Z" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="Filter-Z"/>
</vzSubj>
</vzBrCP>
<vzBrCP descr="" dn="uni/tn-Z/brc-Epg2_EpgB" name="Epg2_EpgB" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-Z" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="Filter-Z"/>
</vzSubj>
</vzBrCP>
<vzBrCP descr="" dn="uni/tn-Z/brc-Epg3_EpgC" name="Epg3_EpgC" nameAlias="" ownerKey="" ownerTag="" prio="unspecified" scope="context" targetDscp="unspecified">
<vzSubj consMatchT="AtleastOne" descr="" name="Subject-Z" nameAlias="" prio="unspecified" provMatchT="AtleastOne" revFltPorts="yes" targetDscp="unspecified">
<vzRsSubjFiltAtt directives="" tnVzFilterName="Filter-Z"/>
</vzSubj>
</vzBrCP>
</fvTenant>
</imdata>
|
生成された XML から Contract を設定してみる
最後に、Python スクリプトから生成された XML を使って実際に Contract を設定してみます。Web UI のテナント設定画面等を右クリックし、Post
をクリックします。
BROWSE
をクリックし、Python スクリプトから生成された XML ファイルを選択します。
Parent DN:
には Contract を設定したい Tenant の DN 名を指定します。例えば A というテナントに Contract を設定したい場合は uni/tn-A を指定します (B というテナントなら uni-/tn-B、C というテナントなら uni/tn-C になります)。Parent DN:
を指定したら POST
をクリックします。
エラーが出ずに Web UI へ戻れば、正しく設定されたはずです(上手くいかなかった場合は失敗の原因を示すエラーが表示されます)。 Web UI から Tenant の Contract 設定を表示すると、確かに XML ファイル中で定義されていた Contract が設定されていることが分かります。