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 名を指定したい場合は以下のオプションを指定します。
各オプションを指定した場合の実行例は以下の通りです。
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 が設定されていることが分かります。