Constructs ========== PyACI tries to provide a minimal set of constructs, that should be enough to achieve most of the tasks that would otherwise be achieved using the REST API. Node ---- A Node represents the ACI fabric node that one would be communicating with. This could be a controller, leaf or spine. A Node object can be instantiated by specifying the REST URL for communicating with the underlying node. >>> from pyaci import Node >>> apic = Node('https://192.168.10.1') For more information, refer to Node documentation. API Objects ----------- Once a Node has been instantiated, a variety of API objects can be spawned from that. There are two classes of these objects: - Methods - Managed objects All these objects support one, or many REST operations. The objects don't interact with the underlying physical node by themeselves. In order to interact, one of the supporte REST construct has to be invoked on them. There are utmost three REST constructs supported on these objects: - GET - POST - DELETE Methods ------- Any REST request that does not operate on managed objects is modeled as a method. The following are some of the methods that are currently supported: Login ~~~~~ This method supports a simple password based login. >>> apic.methods.Login('admin', 'password').POST() LoginRefresh ~~~~~~~~~~~~ This method refreshes an existing session. >>> apic.methods.LoginRefresh().GET() ChangeCert ~~~~~~~~~~ This method is used to add or change X509 certificate that is used for authenticating a user. >>> apic.methods.ChangeCert('admin', 'it', '/path/to/certificate').POST() UploadPackage ~~~~~~~~~~~~~ This method is used to upload an Layer 4 - Layer 7 services package. >>> apic.methods.UploadPackage('/path/to/package/file') Managed Objects --------------- ACI managed objects can be instantiated fromt the Node object using a local Managed Information Tree (MIT). Each invocation of `mit` property on the Node object will result in a different local MIT which can be used as a local cache. For instance, a local polUni object can be instantiated as follows: >>> mit = apic.mit >>> mit.polUni() Please note that at this point this object is only locally instantiated. No communication with the underlying node is involved yet. At this point, you would have noticed that . notation is used to chain object containment hierarchy. The same . notation is also used for accessing properties of an object. >>> ctx = apic.mit.polUni().fvTenant('test').fvCtx('lab') >>> ctx.descr = 'Lab network' >>> print ctx.descr Lab network >>> print ctx.Xml When using . notation for a child object, one should specify the class name, followed by a paranthesis that takes naming properties as either arguments, or keyword arguments. One can also specify non naming properties as keyword arguments. >>> uni = apic.mit.polUni() >>> tenant = uni.fvTenant('demo') >>> print tenant.Xml >>> tenant = uni.fvTenant(name='demo') >>> print tenant.Xml >>> tenant = uni.fvTenant('demo', descr='Demo tenant') >>> print tenant.Xml POST ~~~~ Local managed objects are posted to underlying node using POST() method on the object. The following example shows posting of a new tenant to APIC. >>> apic.mit.polUni().fvTenant('test').POST() This method posts the object, and the entire subtree under that object. For instance, a tenant, private network, bridge domain, and an end-point group can all be created in a single shot and the subtree can be posted as follows. >>> uni = apic.mit.polUni() >>> (uni.fvTenant('demo').fvCtx('test').Up(). ... fvBD('lab').fvRsCtx(tnFvCtxName='test').Up(2). ... fvAp('hadoop').fvAEPg('hbase').fvRsBd(tnFvBDName='lab')) >>> print uni.Xml >>> uni.POST() DELETE ~~~~~~ Local managed objects can be deleted from the underlying node using DELETE() method on that object. A fvCtx object can be deleted as shown below: >>> apic.mit.polUni().fvTenant('demo').fvCtx('test').DELETE() Please note that the local cached managed objects still remain even though it is deleted from APIC. GET ~~~ Local managed objects can be fetch from the underlying node using GET() method on that object. GET() takes other option to affect the scope of the query. We'll look at them later. To begin with, a fvBD can be fetched as follows: >>> bd = apic.mit.polUni().fvTenant('common').fvBD('default') >>> result = bd.GET() >>> type(result) >>> print len(result) 1 >>> print result[0].Dn uni/tn-common/BD-default Please note that the GET() method returs a list. The monadic nature of list is taken advantage to represent the result of a query that can fetch 0, 1 or more objects. It should also be noted that the local managed object is automatically updated with the fetched values. >>> print bd.Xml GET() method can be combined with various options to result in more powerful queries like fetching objects of a certain class, or subtree, etc. For instance, all tenants can be queries as follows: >>> from pyaci import options >>> result = apic.mit.GET(**options.subtreeClass('fvTenant')) >>> for tenant in result: ... print tenant.Dn ... uni/tn-common uni/tn-infra uni/tn-cokecorp uni/tn-mgmt The entire subtree of management tenant can be queried as follows: >>> result = apic.mit.polUni().fvTenant('mgmt').GET(**options.subtree) >>> for tenant in result: ... print tenant.Dn ... uni/tn-mgmt/domain-mgmt uni/tn-mgmt/BD-inb/rsBDToNdP uni/tn-mgmt/BD-inb/rsbdToEpRet uni/tn-mgmt/BD-inb/rsctx uni/tn-mgmt/BD-inb/rsigmpsn uni/tn-mgmt/BD-inb uni/tn-mgmt/ctx-oob/rsbgpCtxPol uni/tn-mgmt/ctx-oob/rsctxToEpRet uni/tn-mgmt/ctx-oob/rsctxToExtRouteTagPol uni/tn-mgmt/ctx-oob/rsospfCtxPol uni/tn-mgmt/ctx-oob/rtmgmtOoBCtx-[uni/tn-mgmt/mgmtp-default/oob-default] uni/tn-mgmt/ctx-oob/any uni/tn-mgmt/ctx-oob uni/tn-mgmt/ctx-inb/rsbgpCtxPol uni/tn-mgmt/ctx-inb/rsctxToEpRet uni/tn-mgmt/ctx-inb/rsctxToExtRouteTagPol uni/tn-mgmt/ctx-inb/rsospfCtxPol uni/tn-mgmt/ctx-inb/rtctx-[uni/tn-mgmt/BD-inb] uni/tn-mgmt/ctx-inb/any uni/tn-mgmt/ctx-inb uni/tn-mgmt/rsTenantMonPol uni/tn-mgmt/extmgmt-default uni/tn-mgmt/mgmtp-default/oob-default/rsooBCtx uni/tn-mgmt/mgmtp-default/oob-default uni/tn-mgmt/mgmtp-default uni/tn-mgmt Audit logs for an object can be fetch as follows: >>> tenant = apic.mit.polUni().fvTenant('demo') >>> tenant.descr = 'Test 1' >>> tenant.POST() >>> tenant.descr = 'Test 2' >>> tenant.POST() >>> result = tenant.GET(**options.auditLogs) >>> for change in result: ... print change.created, change.descr, change.changeSet ... 2015-05-28T01:08:26.627+00:00 Tenant demo created descr:Test 1, name:demo 2015-05-28T01:08:37.627+00:00 Tenant demo modified descr (Old: Test 1, New: Test 2) Multiple options can be combined with & operator, and filters can be used as follows: >>> for node in apic.mit.GET( ... **options.subtreeClass('fabricNode') & ... options.filter(filters.Eq('fabricNode.role', 'leaf') | ... filters.Eq('fabricNode.role', 'spine'))): ... print node.name, node.role ... leaf1 leaf spine2 spine spine1 spine leaf2 leaf Managed Object Iterators ------------------------ Local MIT provides a constructe of object iterators. On a given object, . notation can be used with (immediate) child class name without a following paranthesis to access all children of that class. For instance: >>> mit = apic.mit >>> mit.polUni().fvTenant('test') >>> mit.polUni().fvTenant('demo') >>> mit.polUni().fvTenant('finance') >>> for tenant in mit.polUni().fvTenant: ... print tenant.Dn ... uni/tn-demo uni/tn-test uni/tn-finance The use of iterators becomes more obvious when one is walking through a subtree that is fetched from a node. >>> pod = apic.mit.fabricTopology().fabricPod('1') >>> pod.GET(**options.subtree) >>> for node in pod.fabricNode: ... print node.name, node.role, node.fabricSt ... leaf2 leaf active spine1 spine active leaf1 leaf active apic1 controller unknown spine2 spine active There is also a way to access all the children of a given object using Children property. >>> pod = apic.mit.fabricTopology().fabricPod('1') >>> pod.GET(**options.subtree) >>> for child in pod.Children: ... print child.Dn ... topology/pod-1/lnkcnt-102 topology/pod-1/node-102 topology/pod-1/paths-102 topology/pod-1/paths-101 topology/pod-1/node-101 topology/pod-1/node-104 topology/pod-1/paths-104 topology/pod-1/lnkcnt-1 topology/pod-1/lnkcnt-103 topology/pod-1/path-101-102 topology/pod-1/lnkcnt-101 topology/pod-1/node-1 topology/pod-1/lnkcnt-104 topology/pod-1/node-103 topology/pod-1/health topology/pod-1/paths-103