F5 ASM
Hey I'm trying to get to know the ASM feature in Big-IP. I want to be able to block request based on specific ips or headers (+values) and more. I also want to configure it using an API, I saw there is something called iControl. Is there any docs that contain all the paths in iControl so I could search everything I need? It would also help me get more familiar with the feature53Views0likes2CommentsGenerate private key w/ CSR via iControl REST
Problem this snippet solves: Generate a private key w/ CSR How to use this snippet: To create a private key with a CSR via iControl REST: POST URL:https://10.1.1.165/mgmt/tm/sys/crypto/key Use the data below as your payload. For the name field, it must end in .key or you will get a false 404! Code : { "name":"www.testing.com.key", "commonName":"www.testing.com", "keySize":"4096", "keyType":"rsa-private", "options":[{"gen-csr":"www.testing.com"}], "organization":"Let It Snow Corp.", "ou":"Ice Engineering", "city":"Calhoun", "state":"AZ", "admin-email-address":"jerry@letit.snow", "email-address":"beth@letit.snow", "subject-alternative-name":"DNS:www.testing.com", "challenge-password":"myP4ssword" } Tested this on version: 13.01.9KViews3likes11CommentsBIG-IP Report
Problem this snippet solves: Overview This is a script which will generate a report of the BIG-IP LTM configuration on all your load balancers making it easy to find information and get a comprehensive overview of virtual servers and pools connected to them. This information is used to relay information to NOC and developers to give them insight in where things are located and to be able to plan patching and deploys. I also use it myself as a quick way get information or gather data used as a foundation for RFC's, ie get a list of all external virtual servers without compression profiles. The script has been running on 13 pairs of load balancers, indexing over 1200 virtual servers for several years now and the report is widely used across the company and by many companies and governments across the world. It's easy to setup and use and only requires auditor (read-only) permissions on your devices. Demo/Preview Interactive demo http://loadbalancing.se/bigipreportdemo/ Screen shots The main report: The device overview: Certificate details: How to use this snippet: Installation instructions BigipReport REST This is the only branch we're updating since middle of 2020 and it supports 12.x and upwards (maybe even 11.6). Downloads (two latest versions): https://loadbalancing.se/downloads/bigipreport-v5.7.11.zip https://loadbalancing.se/downloads/bigipreport-v5.7.10.zip Documentation, installation instructions and troubleshooting:https://loadbalancing.se/bigipreport-rest/ Docker support https://loadbalancing.se/2021/01/05/running-bigipreport-on-docker/ Kubernetes support https://loadbalancing.se/2021/04/16/bigipreport-on-kubernetes/ BIG-IP Report (Legacy) Older version of the report that only runs on Windows and is depending on a Powershell plugin originally written by Joe Pruitt (F5) BIG-IP Report (only download this if you have v10 devices): https://loadbalancing.se/downloads/bigipreport-5.4.0-beta.zip iControl Snapin https://loadbalancing.se/downloads/f5-icontrol.zip Documentation and Installation Instructions https://loadbalancing.se/bigip-report/ Upgrade instructions Protect the report using APM and active directory Written by DevCentral member Shann_P: https://loadbalancing.se/2018/04/08/protecting-bigip-report-behind-an-apm-by-shannon-poole/ Got issues/problems/feedback? Still have issues? Drop a comment below. We usually reply quite fast. Any bugs found, issues detected or ideas contributed makes the report better for everyone, so it's always appreciated. --- Join us on Discord: https://discord.gg/7JJvPMYahA Code : BigIP Report Tested this on version: 12, 13, 14, 15, 1613KViews20likes94CommentsControlling a Pool Members Ratio and Priority Group with iControl
A Little Background A question came in through the iControl forums about controlling a pool members ratio and priority programmatically. The issue really involves how the API’s use multi-dimensional arrays but I thought it would be a good opportunity to talk about ratio and priority groups for those that don’t understand how they work. In the first part of this article, I’ll talk a little about what pool members are and how their ratio and priorities apply to how traffic is assigned to them in a load balancing setup. The details in this article were based on BIG-IP version 11.1, but the concepts can apply to other previous versions as well. Load Balancing In it’s very basic form, a load balancing setup involves a virtual ip address (referred to as a VIP) that virtualized a set of backend servers. The idea is that if your application gets very popular, you don’t want to have to rely on a single server to handle the traffic. A VIP contains an object called a “pool” which is essentially a collection of servers that it can distribute traffic to. The method of distributing traffic is referred to as a “Load Balancing Method”. You may have heard the term “Round Robin” before. In this method, connections are passed one at a time from server to server. In most cases though, this is not the best method due to characteristics of the application you are serving. Here are a list of the available load balancing methods in BIG-IP version 11.1. Load Balancing Methods in BIG-IP version 11.1 Round Robin: Specifies that the system passes each new connection request to the next server in line, eventually distributing connections evenly across the array of machines being load balanced. This method works well in most configurations, especially if the equipment that you are load balancing is roughly equal in processing speed and memory. Ratio (member): Specifies that the number of connections that each machine receives over time is proportionate to a ratio weight you define for each machine within the pool. Least Connections (member): Specifies that the system passes a new connection to the node that has the least number of current connections in the pool. This method works best in environments where the servers or other equipment you are load balancing have similar capabilities. This is a dynamic load balancing method, distributing connections based on various aspects of real-time server performance analysis, such as the current number of connections per node or the fastest node response time. Observed (member): Specifies that the system ranks nodes based on the number of connections. Nodes that have a better balance of fewest connections receive a greater proportion of the connections. This method differs from Least Connections (member), in that the Least Connections method measures connections only at the moment of load balancing, while the Observed method tracks the number of Layer 4 connections to each node over time and creates a ratio for load balancing. This dynamic load balancing method works well in any environment, but may be particularly useful in environments where node performance varies significantly. Predictive (member): Uses the ranking method used by the Observed (member) methods, except that the system analyzes the trend of the ranking over time, determining whether a node's performance is improving or declining. The nodes in the pool with better performance rankings that are currently improving, rather than declining, receive a higher proportion of the connections. This dynamic load balancing method works well in any environment. Ratio (node): Specifies that the number of connections that each machine receives over time is proportionate to a ratio weight you define for each machine across all pools of which the server is a member. Least Connections (node): Specifies that the system passes a new connection to the node that has the least number of current connections out of all pools of which a node is a member. This method works best in environments where the servers or other equipment you are load balancing have similar capabilities. This is a dynamic load balancing method, distributing connections based on various aspects of real-time server performance analysis, such as the number of current connections per node, or the fastest node response time. Fastest (node): Specifies that the system passes a new connection based on the fastest response of all pools of which a server is a member. This method might be particularly useful in environments where nodes are distributed across different logical networks. Observed (node): Specifies that the system ranks nodes based on the number of connections. Nodes that have a better balance of fewest connections receive a greater proportion of the connections. This method differs from Least Connections (node), in that the Least Connections method measures connections only at the moment of load balancing, while the Observed method tracks the number of Layer 4 connections to each node over time and creates a ratio for load balancing. This dynamic load balancing method works well in any environment, but may be particularly useful in environments where node performance varies significantly. Predictive (node): Uses the ranking method used by the Observed (member) methods, except that the system analyzes the trend of the ranking over time, determining whether a node's performance is improving or declining. The nodes in the pool with better performance rankings that are currently improving, rather than declining, receive a higher proportion of the connections. This dynamic load balancing method works well in any environment. Dynamic Ratio (node) : This method is similar to Ratio (node) mode, except that weights are based on continuous monitoring of the servers and are therefore continually changing. This is a dynamic load balancing method, distributing connections based on various aspects of real-time server performance analysis, such as the number of current connections per node or the fastest node response time. Fastest (application): Passes a new connection based on the fastest response of all currently active nodes in a pool. This method might be particularly useful in environments where nodes are distributed across different logical networks. Least Sessions: Specifies that the system passes a new connection to the node that has the least number of current sessions. This method works best in environments where the servers or other equipment you are load balancing have similar capabilities. This is a dynamic load balancing method, distributing connections based on various aspects of real-time server performance analysis, such as the number of current sessions. Dynamic Ratio (member): This method is similar to Ratio (node) mode, except that weights are based on continuous monitoring of the servers and are therefore continually changing. This is a dynamic load balancing method, distributing connections based on various aspects of real-time server performance analysis, such as the number of current connections per node or the fastest node response time. L3 Address: This method functions in the same way as the Least Connections methods. We are deprecating it, so you should not use it. Weighted Least Connections (member): Specifies that the system uses the value you specify in Connection Limit to establish a proportional algorithm for each pool member. The system bases the load balancing decision on that proportion and the number of current connections to that pool member. For example,member_a has 20 connections and its connection limit is 100, so it is at 20% of capacity. Similarly, member_b has 20 connections and its connection limit is 200, so it is at 10% of capacity. In this case, the system select selects member_b. This algorithm requires all pool members to have a non-zero connection limit specified. Weighted Least Connections (node): Specifies that the system uses the value you specify in the node's Connection Limitand the number of current connections to a node to establish a proportional algorithm. This algorithm requires all nodes used by pool members to have a non-zero connection limit specified. Ratios The ratio is used by the ratio-related load balancing methods to load balance connections. The ratio specifies the ratio weight to assign to the pool member. Valid values range from 1 through 100. The default is 1, which means that each pool member has an equal ratio proportion. So, if you have server1 a with a ratio value of “10” and server2 with a ratio value of “1”, server1 will get served 10 connections for every one that server2 receives. This can be useful when you have different classes of servers with different performance capabilities. Priority Group The priority group is a number that groups pool members together. The default is 0, meaning that the member has no priority. To specify a priority, you must activate priority group usage when you create a new pool or when adding or removing pool members. When activated, the system load balances traffic according to the priority group number assigned to the pool member. The higher the number, the higher the priority, so a member with a priority of 3 has higher priority than a member with a priority of 1. The easiest way to think of priority groups is as if you are creating mini-pools of servers within a single pool. You put members A, B, and C in to priority group 5 and members D, E, and F in priority group 1. Members A, B, and C will be served traffic according to their ratios (assuming you have ratio loadbalancing configured). If all those servers have reached their thresholds, then traffic will be distributed to servers D, E, and F in priority group 1. he default setting for priority group activation is Disabled. Once you enable this setting, you can specify pool member priority when you create a new pool or on a pool member's properties screen. The system treats same-priority pool members as a group. To enable priority group activation in the admin GUI, select Less than from the list, and in the Available Member(s) box, type a number from 0 to 65535 that represents the minimum number of members that must be available in one priority group before the system directs traffic to members in a lower priority group. When a sufficient number of members become available in the higher priority group, the system again directs traffic to the higher priority group. Implementing in Code The two methods to retrieve the priority and ratio values are very similar. They both take two parameters: a list of pools to query, and a 2-D array of members (a list for each pool member passed in). long [] [] get_member_priority( in String [] pool_names, in Common__AddressPort [] [] members ); long [] [] get_member_ratio( in String [] pool_names, in Common__AddressPort [] [] members ); The following PowerShell function (utilizing the iControl PowerShell Library), takes as input a pool and a single member. It then make a call to query the ratio and priority for the specific member and writes it to the console. function Get-PoolMemberDetails() { param( $Pool = $null, $Member = $null ); $AddrPort = Parse-AddressPort $Member; $RatioAofA = (Get-F5.iControl).LocalLBPool.get_member_ratio( @($Pool), @( @($AddrPort) ) ); $PriorityAofA = (Get-F5.iControl).LocalLBPool.get_member_priority( @($Pool), @( @($AddrPort) ) ); $ratio = $RatioAofA[0][0]; $priority = $PriorityAofA[0][0]; "Pool '$Pool' member '$Member' ratio '$ratio' priority '$priority'"; } Setting the values with the set_member_priority and set_member_ratio methods take the same first two parameters as their associated get_* methods, but add a third parameter for the priorities and ratios for the pool members. set_member_priority( in String [] pool_names, in Common::AddressPort [] [] members, in long [] [] priorities ); set_member_ratio( in String [] pool_names, in Common::AddressPort [] [] members, in long [] [] ratios ); The following Powershell function takes as input the Pool and Member with optional values for the Ratio and Priority. If either of those are set, the function will call the appropriate iControl methods to set their values. function Set-PoolMemberDetails() { param( $Pool = $null, $Member = $null, $Ratio = $null, $Priority = $null ); $AddrPort = Parse-AddressPort $Member; if ( $null -ne $Ratio ) { (Get-F5.iControl).LocalLBPool.set_member_ratio( @($Pool), @( @($AddrPort) ), @($Ratio) ); } if ( $null -ne $Priority ) { (Get-F5.iControl).LocalLBPool.set_member_priority( @($Pool), @( @($AddrPort) ), @($Priority) ); } } In case you were wondering how to create the Common::AddressPort structure for the $AddrPort variables in the above examples, here’s a helper function I wrote to allocate the object and fill in it’s properties. function Parse-AddressPort() { param($Value); $tokens = $Value.Split(":"); $r = New-Object iControl.CommonAddressPort; $r.address = $tokens[0]; $r.port = $tokens[1]; $r; } Download The Source The full source for this example can be found in the iControl CodeShare under PowerShell PoolMember Ratio and Priority.28KViews0likes3CommentsUpload SSL certificate/key via REST API
Hello All, Looking to see if anyone knows of a method of uploading certs and keys to a BIGIP unit, using a method similar to the following example, but using REST instead of the SOAP API. Example: puts bigip["Management.KeyCertificate"].certificate_import_from_pem('MANAGEMENT_MODE_DEFAULT', [ cert['cert_name'] ], [ File.open(cert['cert_file']).read ], true) puts bigip["Management.KeyCertificate"].key_import_from_pem('MANAGEMENT_MODE_DEFAULT', [ cert['cert_name'] ], [ File.open(cert['key_file']).read ], true) Thanks!2.8KViews0likes10CommentsiControl REST Python Requests module
While iControl REST is great and pretty robust given how much we use it. But I come to the forum today to see if anyone has any experience with the Python requests module and the underlying urllib3 module. When I do have problems with iControl its often with things like the following: Here I've increased the timeout to 5.0 seconds but still get read timeouts. HTTPSConnectionPool(host='redacted', port=443): Read timed out. (read timeout=5.0) I also often see this message and, no I don't have a proxy in front of the F5. However, I know the the F5 proxies the REST call (Caused by ProxyError('Cannot connect to proxy.', OSError('Tunnel connection failed: 503 Service Unavailable', Thanks!1.3KViews0likes1CommentSelf-Contained BIG-IP Provisioning with iRules and pyControl – Part 2
As I stated last week in Part 1, iRules work on the live data between client and server, and iControl works on the management plane out of band to collect information or to modify or create configuration objects. However, what if you could combine forces, wholly contained on your BIG-IP LTM? That’s the scenario I started tackling last week. In this second part, I’ll address the iRule and iControl components necessary to complete a provisioning task. Self-Contained BIG-IPProvisioning with iRules and pyControl - Part 1 The iRule code I’ll get the solution working prior to making it extensible and flashy. First thing I’ll do to enhance the test iRule from last week is to utilize the restful interface approach I used to access table information in this tech tip on Restful Access to BIG-IP Subtables - DevCentral . I want to pass an action, a pool name, and a pool member through the URL to the system. In this case, the action will be enable or disable, and the pool and pool member should be valid entries. In the event a pool or pool member is passed that is not valid, there is simple error checking in place, which will alert the client. Note also in the code below that I incorporated code from George’s basic auth tech tip. 1: when HTTP_REQUEST { 2: binary scan [md5 [HTTP::password]] H* password 3: 4: if { ([HTTP::path] starts_with "/provision") } { 5: if { [class lookup [HTTP::username] provisioners] equals $password } { 6: scan [lrange [split [URI::path [HTTP::uri]] "/"] 2 end-1] %s%s%s action pname pmem 7: if { [catch {[members $pname]} errmsg] && !($errmsg equals "")} { 8: if { [members -list $pname] contains "[getfield $pmem ":" 1] [getfield $pmem ":" 2]" } { 9: set pmstat1 [LB::status pool $pname member [getfield $pmem ":" 1] [getfield $pmem ":" 2]] 10: log local0. "#prov=$action,$pname,$pmem" 11: after 250 12: set pmstat2 [LB::status pool $pname member [getfield $pmem ":" 1] [getfield $pmem ":" 2]] 13: HTTP::respond 200 content "<html><body>Original Status<br />$pmem-$pmstat1<p>New Status<br />$pmem-$pmstat2</body></html>" 14: } else { HTTP::respond 200 content "$pmem not a valid pool member." } 15: } else { HTTP::respond 200 content "$pname not a valid pool." } 16: } else { HTTP::respond 401 WWW-Authenticate "Basic realm=\"Secured Area\"" } 17: 18: } 19: } I added the “after 250” command to give the pyControl script some time to receive the syslog event and process before checking the status again. This is strictly for display purposes back to the client. The pyControl code I already have the syslog listener in place, and now with the iRule passing real data, this is what arrives via syslog: <134>Oct 13 11:04:55 tmm tmm[4848]: Rule ltm_provisioning <HTTP_REQUEST>: #prov=enable,cacti-pool,10.10.20.200:80 Not really ready for processing, is it? So I need to manipulate the data a little to get it into manageable objects for pyControl. # Receive messages while True: data,addr = syslog_socket.recvfrom(1024) rawdata = data.split(' ')[-1] provdata = rawdata.split('=')[-1] dataset = provdata.split(',') dataset = map(string.strip, dataset) Splitting on whitespace removes all the syslog overhead, the I split again on the “=” sign to get the actual objects I need. Next, I split the data to eliminate the commons and create a list,and the finally, I strip any newline charcters (which happens to exist on the last element, the IP:port object). Now that I have the objects I need, I can go to work setting the enable/disable state on the pool members. Thankfully, the disable pool member pyControl code already exists in the iControl codeshare, I just need to do a little modification. None of the def’s need to change, so I added them as is. Because the arguments are coming from syslog and not from a command line, I have no need for the sys module, so I won’t import that. I do need to import string, though to strip the newline character in the map(string.strip, dataset) command above. The dataset object is a list and the elements are 0:action, 1:pool, 2:pool_member. pyControl expects the members to be a list, even if it’s only one member, which is true of this scenario. ### Process Pool, Pool Members, & Desired State ### POOL = dataset[1] members = [dataset[2]] sstate_seq = b.LocalLB.PoolMember.typefactory.create('LocalLB.PoolMember.MemberSessionStateSequence') sstate_seq.item = session_state_factory(b, members) session_objects = session_state_factory(b, members) if dataset[0] == 'enable': enable_member(b, session_objects) elif dataset[0] == 'disable': disable_member(b, session_objects) else: print "\nNo such action: \"dataset[0]\" " More error checking here, if the action passed was enable/disable, I call the appropriate def, otherwise, I log to console. OK. Time to test it out! Results In this video, I walk through an example of enabling/disabling a pool member Provision Through iRules The Fine Print Obviously, there is risk with such a system. Allowing provisioning of your application delivery through the data plane can be a dangerous thing, so tread carefully. Also, customizing syslog and installing and running pyControl on box will not survive hotfixes and upgrades, so processes would need to be in place to ensure functionality going forward. Finally, I didn’t cover ensuring the pyControl script is running in the background and is started on system boot, but that is a step that would be required as well. For test purposes, I just ran the script on the console. Conclusion There is an abundance of valuable and helpful information on how to utilize the BIG-IP system, iRules, and iControl. Without too much work on my own, I was able to gather snippets of code here and there to deliver a self-contained provisioning system. The system is short on functionality (toggling pool members), but could be extended with a little elbow grease and MUCH error checking. For the full setup, check out the LTM Provisioning via iRules wiki entry in Advanced Design and Configuration wiki. Related Articles HTTP Basic Access Authentication iRule Style > DevCentral > F5 ... F5 DevCentral > Community > Group Details - Python iControl Library Getting Started with pyControl v2: Installing on Windows ... Getting Started with pyControl v2: Installing on Ubuntu Desktop ... Experimenting with pyControl on LTM VE > DevCentral > F5 ... Getting Started with pyControl v2: Understanding the TypeFactory ... Getting Started with pyControl v2: Constructor Changes ... LTM 9.4.2+: Custom Syslog Configuration > DevCentral > F5 ... LTM 9.4.2+ Custom Syslog errors when bpsh used - DevCentral - F5 ... Custom syslog-ng facility - DevCentral - F5 DevCentral > Community ... Customizing syslog-ng f_local0 filter - DevCentral - F5 DevCentral ... How to write iRule log statements to a custom log file, and rotate ... Syslog locally and remote with specific facility level ... DevCentral Wiki: Syslog NG Email Configuration Technorati Tags: F5 DevCentral,pyControl,iControl,iRules,syslog-ng,provisioning308Views0likes0CommentsPython Bigsuds - License BIG-IP
Problem this snippet solves: This bigsuds script will license a BIGIP How to use this snippet: This bigsuds script will license a BIGIP It's a port of some of Erick's: https://devcentral.f5.com/wiki/icontrol.BigIpLicensingCommandLineTool.ashx and update of the pycontrol v2: pyControl v2 - License BIGIP - DevCentral Still missing the proxy functionality in Erick's though. Usage: license-bigip.py --bigip < IP|hostname> --username < username> --server < license_server_hostname> --reg_keys < regkeys> --license < license_file> --eula < eula_file> Ex. From CLI: (virt1)user1@desktop:python $./license-bigip.py --help Usage: license-bigip.py [options] Options: -h, --help show this help message and exit -b BIGIP, --bigip=BIGIP -u UNAME, --username=UNAME -s SERVER_HOSTNAME, --server=SERVER_HOSTNAME -r REG_KEYS_STRING, --reg_keys=REG_KEYS_STRING -l LOCAL_LICENSE_FILE_NAME, --license=LOCAL_LICENSE_FILE_NAME -e LOCAL_EULA_FILE_NAME, --eula=LOCAL_EULA_FILE_NAME Providing New Keys: (virt1)user1@desktop:python $./license-bigip.py --server activate.f5.com --bigip 10.11.50.201 --username admin --reg_keys "B6907-36850-30441-96521-8395199" Enter your password for username: admin Password: Attempting to get license online. License server requires you to submit EULA. reg keys provided ['B6907-36850-30441-96521-8395172'] Getting dossier using keys:['B6907-36850-30441-96521-8395199'] License Found. Attempting to installing License on BIGIP: License status = STATE_ENABLED Use Existing Keys: (virt1)user1@desktop:python $./license-bigip.py --server activate.f5.com --bigip 10.11.50.201 --username admin Enter your password for username: admin Password: Attempting to get license online. License server requires you to submit EULA. Reg Key list is empty, attempting to retrieve existing keys from the unit Getting dossier using keys:[B6907-36850-30441-96521-8395199, P459718-6102942, B273271-7144254, N123537-7304104] License Found. Attempting to installing License on BIGIP: License status = STATE_ENABLED Script license-bigip.py Code : #!/usr/bin/env python ''' ---------------------------------------------------------------------------- The contents of this file are subject to the "END USER LICENSE AGREEMENT FOR F5 Software Development Kit for iControl"; you may not use this file except in compliance with the License. The License is included in the iControl Software Development Kit. Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is iControl Code and related documentation distributed by F5. The Initial Developer of the Original Code is F5 Networks, Inc. Seattle, WA, USA. Portions created by F5 are Copyright (C) 1996-2004 F5 Networks, Inc. All Rights Reserved. iControl (TM) is a registered trademark of F5 Networks, Inc. Alternatively, the contents of this file may be used under the terms of the GNU General Public License (the "GPL"), in which case the provisions of GPL are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the GPL and not to allow others to use your version of this file under the License, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the GPL. If you do not delete the provisions above, a recipient may use your version of this file under either the License or the GPL. ---------------------------------------------------------------------------- ''' def usage (): print "Usage:" print "%s --bigip --username --server \ --reg_keys --license --eula " % sys.argv[0] print "ex. " print " Will attempt to re-license with existing keys on unit using license server activate.f5.com" print " %s --bigip 192.168.1.245 --username admin" % sys.argv[0] print " Will attempt to re-license with provided reg_keys CSV string using license server activate.f5.com" print " %s --bigip 192.168.1.245 --username admin --reg_keys \"XXXX-XXXX-XXXX-XXXX,XXXX-XXXX,XXXX-XXXX\" " % sys.argv[0] def get_license_from_F5_License_Server ( server_hostname, dossier_string, eula_string, email, firstName, lastName, companyName, phone, jobTitle, address, city, stateProvince, postalCode, country ): try: license_string = "" # Unfortunately, F5 wsdl on license server references http but F5 only accepts https so as an ugly workaround need to # download wsdl, save to disk, replace links http with https, and have SUDS client reference local file instead #(eg. url = "file:///home/admin/f5wsdl.xml") download_url = "https://" + server_hostname + "/license/services/urn:com.f5.license.v5b.ActivationService?wsdl" # Check to see if there's a copy of wsdl file on disk first # Careful with this behavior if you switch server hostnames. local_wsdl_file_name = str(server_hostname) + '-f5wsdl-w-https.xml' wsdl_data = [] try: with open(local_wsdl_file_name, 'r') as fh_wsdl: wsdl_data = fh_wsdl.read() except: print "Can't find a locally stored WSDL file." if not wsdl_data: print "Attempting to fetch wsdl online." f5wsdl = urllib2.urlopen(download_url) newlines = [] for line in f5wsdl: # do the replacing here newlines.append(line.replace('http://' + server_hostname , 'https://' + server_hostname)) fh_local = open(local_wsdl_file_name,'w') fh_local.writelines(newlines) fh_local.close() # put url going to pass to client in file format url = "file:" + urllib.pathname2url(os.getcwd()) + "/" + local_wsdl_file_name #Now create client object using wsdl from disk instead of the interwebs. client = Client(url) # NOT using below as will just try actually licensing and fail then if needed # try: # # ping() method should return string containing date # print "Checking License Service Reachability..." # return_ping_date = client.service.ping() # except: # print "License SOAP service unreachable. Check network connectivity." # return transaction = client.factory.create('ns0:LicenseTransaction') # If eula isn't present on first call to getLicense, transaction will fail # but it will return a eula after first attempt transaction = client.service.getLicense( dossier = dossier_string, eula = eula_string, email = email, firstName = firstName , lastName = lastName, companyName = companyName, phone = phone, jobTitle = jobTitle, address = address, city = city, stateProvince = stateProvince, postalCode = postalCode, country = country, ) #Extract the eula offered from first try eula_string = transaction.eula if transaction.state == "EULA_REQUIRED": #Try again, this time with eula populated transaction = client.service.getLicense( dossier = dossier_string, eula = eula_string, email = email, firstName = firstName , lastName = lastName, companyName = companyName, phone = phone, jobTitle = jobTitle, address = address, city = city, stateProvince = stateProvince, postalCode = postalCode, country = country, ) if transaction.state == "LICENSE_RETURNED": license_string = transaction.license else: print "Can't retrieve license from Licensing server" print "License server returned error: Number:" + str(transaction.fault.faultNumber) + " Text: " + str(transaction.fault.faultText) return license_string except: print "Can't retrieve License from Server" traceback.print_exc(file=sys.stdout) def get_reg_keys(obj): try: reg_keys = [] reg_keys = obj.Management.LicenseAdministration.get_registration_keys() return reg_keys except: print "Get Reg Keys error. Check log." traceback.print_exc(file=sys.stdout) def get_dossier (obj, reg_keys ): try: dossier_string = obj.Management.LicenseAdministration.get_system_dossier ( reg_keys ) return dossier_string except: print "Get Dossier error. Check log." traceback.print_exc(file=sys.stdout) def get_eula_file (obj): try: eula_char_array = obj.Management.LicenseAdministration.get_eula_file( ) eula_string = base64.b64decode(eula_char_array) return eula_string except: print "Get eula_file. Check log." traceback.print_exc(file=sys.stdout) def install_license (obj, license_string ): try: license_char_array = base64.b64encode(license_string) obj.Management.LicenseAdministration.install_license ( license_file_data = license_char_array ) except: print "Install License error. Check log." traceback.print_exc(file=sys.stdout) def get_license_status (obj): try: license_status = obj.Management.LicenseAdministration.get_license_activation_status() return license_status except: print "Get License Status error. Check log." traceback.print_exc(file=sys.stdout) ### IMPORT MODULES ### import os import sys import time import traceback import base64 import urllib import urllib2 import getpass from suds.client import Client from optparse import OptionParser import bigsuds # from suds import WebFault # import logging # logging.getLogger('suds.client').setLevel(logging.DEBUG) # logging.getLogger('suds.metrics').setLevel(logging.DEBUG) # logging.getLogger('suds').setLevel(logging.DEBUG) #### SET CONFIG VARIABLES #### #Misc EULA Variables email = "example.icontrol@f5.com" firstName = "example" lastName = "iControl" companyName = "F5" phone = "2062725555" jobTitle = "DEV OPS" address = "111 EXAMPLE ICONTROL RD" city = "Seattle" stateProvince = "WA" postalCode = "98119" country = "United States" parser = OptionParser() parser.add_option("-b", "--bigip", action="store", type="string", dest="bigips", default="192.168.1.245") parser.add_option("-u", "--username", action="store", type="string", dest="uname", default="admin") parser.add_option("-s", "--server", action="store", type="string", dest="server_hostname", default="activate.f5.com" ) parser.add_option("-r", "--reg_keys", action="store", type="string", dest="reg_keys_string" ) parser.add_option("-l", "--license", action="store", type="string", dest="local_license_file_name") parser.add_option("-e", "--eula", action="store", type="string", dest="local_eula_file_name") (options, args) = parser.parse_args() ### INITIALIZE BIGIP OBJECT ### if options.bigip and options.uname: print "Enter your password for username: %s" % options.uname upass = getpass.getpass() else: usage() sys.exit() #hardcoded so don't have to provide any arguments. #print "Enter your password for username: %s" % options.uname #upass = getpass.getpass() # Can re-license a list of BIG-IPs IF you don't provide a list of regkeys on CLI. # As reg key split function does not accommodate list of keys for multiple devices. if options.bigips: bigip_list = options.bigips.split(",") for i in bigip_list: print "\nAttempting to License BIGIP " + i reg_keys = [] license_string = "" eula_string = "" reg_keys_string = "" local_license_file_name = "" local_eula_file_name = "" server_hostname = "authem.f5net.com" uname = "admin" b = bigsuds.BIGIP( hostname = i, username = uname, password = upass, ) ########## START MAIN LOGIC ###### if local_license_file_name: try: print "Attempting to retrive License from local disk ..." with open(local_license_file_name, 'r') as fh_license: license_string = fh_license.read() except: print "Can't Open license file named: \"" + local_license_file_name + "\" on disk." #sys.exit() print "No worries. Will attempt to retrieve one from online." if not license_string: print "Attempting to get license online." print "License server requires you to submit EULA." if options.local_eula_file_name: print "Attempting to retrive EULA from local disk first..." try: with open(options.local_eula_file_name, 'r') as fh_eula: eula_string = fh_eula.read() except: print "Can't find EULA file named : \"" + options.local_eula_file_name + "\" on disk." print "No worries. Will attempt to retrieve one during transaction with License Server." # Could also try seeing if Target BIG-IP has one stored # eula_file = get_eula_file(b) if options.reg_keys_string: reg_keys = options.reg_keys_string.split(",") print "reg keys provided" print reg_keys if len(reg_keys) < 1: print "Reg Key list is empty, attempting to retrieve existing keys from the unit" reg_keys = get_reg_keys(b) print "Getting dossier using keys:" + str(reg_keys) dossier_string = get_dossier( b, reg_keys ) # print "dossier = " + str(dossier_output) license_string = get_license_from_F5_License_Server( options.server_hostname, dossier_string, eula_string, email, firstName, lastName, companyName, phone, jobTitle, address, city, stateProvince, postalCode, country ) if license_string: print "License Found. Attempting to installing License on BIGIP:" install_license ( b, license_string ) else: print "Sorry. Could not retrieve License. Check your connection" license_status = get_license_status ( b ) print "License status = " + str(license_status)343Views0likes0CommentsPerl ARX ManualMigrateRule
Problem this snippet solves: This script is an example of how to use the iControl interfaces provided by an ARX to use manual migration rule to migrate files on an ARX. How to use this snippet: ARXManualMigrateRuleExample.pl --url --user --pass Prerequisites SOAP::Lite perl module An F5 ARX system running release V6.02.000 or later and configured with at least one manual migrate rule. Management access on the ARX must be permitted for HTTPs-API or HTTP-API services. Code : #!/usr/bin/perl #------------------------------------------------------------------------------- # The contents of this file are subject to the "END USER LICENSE AGREEMENT # FOR F5 Software Development Kit for iControl"; you may not use this file # except in compliance with the License. The License is included in the # iControl Software Development Kit. # # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. # # The Original Code is iControl Code and related documentation # distributed by F5. # # The Initial Developer of the Original Code is F5 Networks, # Inc. Seattle, WA, USA. Portions created by F5 are Copyright (C) 1996-2012 # F5 Networks, Inc. All Rights Reserved. iControl (TM) is a registered # trademark of F5 Networks, Inc. # # Alternatively, the contents of this file may be used under the terms # of the GNU General Public License (the "GPL"), in which case the # provisions of GPL are applicable instead of those above. If you wish # to allow use of your version of this file only under the terms of the # GPL and not to allow others to use your version of this file under the # License, indicate your decision by deleting the provisions above and # replace them with the notice and other provisions required by the GPL. # If you do not delete the provisions above, a recipient may use your # version of this file under either the License or the GPL. #------------------------------------------------------------------------------- # # Description # # This script is an example of how to use the iControl interfaces provided by # an ARX to use manual migration rule to migrate files on an ARX. # # Usage: ARXManualMigrateRuleExample.pl --url --user --pass --op # # Prerequisites: # # This script requires the following: # # * SOAP::Lite perl module # * An F5 ARX system configured with at least one configured manual migrate rule. # * Management access on the ARX must be permitted for HTTP-API and HTTPS-API # services. # # For more information on ARX configuration, please consult the # documentation that was provided with your ARX system. #------------------------------------------------------------------------------- # SOAP::Lite lets us send SOAP requests and parse them use SOAP::Lite autotype => 0, default_ns => 'urn:iControl'; # If you need to debug problems with your script, you can use the +trace # option with SOAP::Lite and it will print the XML sent to and received from # the server: # # use SOAP::Lite # autotype => 0, # default_ns => 'urn:iControl' + trace; # Getopt::Long lets us easily parse command line options use Getopt::Long; use POSIX qw(strftime); use Carp; use strict; use warnings; #------------------------------------------------------------------------------- # Main program logic #------------------------------------------------------------------------------- our ($url, $user, $pass, $op); # Load command line options - if the load fails, then we print the usage # instructions and exit. if (!GetOptions("url=s" => \$url, "user=s" => \$user, "pass=s" => \$pass, "op=s" => \$op)) { usage(); exit(1); } # If any arguments were skipped, print the usage instructions and exit. if (!defined $url || !defined $user || !defined $pass || !defined $op) { usage(); exit(1); } # The service path for interface "Interface" is this: # # http:// : /api/services/Interface # my $manualMigrateRuleServiceUrl = $url . "/api/services/ManualMigrateRule"; # In order for SOAP to access a web service, it needs to read the WSDL # for the interface you want to use. The WSDL file for an interface # called "Interface" is available via http/https on the ARX at: # # http:// : /api/services/Interface?wsdl # # If you need a WSDL 2.0 version, that is also available at: # # http:// : /arx-api/wsdl/Interface.wsdl2 # # In this case, we're using the ManualMigrateRule interface and we're # interested in using the WSDL 1.1 version. # my $manualMigrateRuleWsdlUrl = $manualMigrateRuleServiceUrl . "?wsdl"; # Now we build our SOAP::Lite object using the service and WSDL # URLs my $manualMigrateRuleSoap = SOAP::Lite->new(proxy => $manualMigrateRuleServiceUrl, service => $manualMigrateRuleWsdlUrl); print "Please specify a namespace:\n\n"; chomp(my $namespace = <>); print "\n"; print "Please specify a volume:\n\n"; chomp(my $volume = <>); print "\n"; if ($op eq "get_list") { get_list($namespace, $volume); } elsif ($op eq "get_configuration") { get_configuration($namespace, $volume); } elsif ($op eq "get_status") { get_status($namespace, $volume); } elsif ($op eq "create") { create($namespace, $volume); } elsif ($op eq "set_migrate_close_file") { set_migrate_close_file($namespace, $volume); } elsif ($op eq "set_report") { set_report($namespace, $volume); } elsif ($op eq "set_enable") { set_enable($namespace, $volume); } elsif ($op eq "create_and_configure") { create_and_configure($namespace, $volume); } elsif ($op eq "remove") { remove($namespace, $volume); } elsif ($op eq "migrate_files") { migrate_files($namespace, $volume); } #------------------------------------------------------------------------------- # End of main program logic #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- # sub usage #------------------------------------------------------------------------------- sub usage { print "\nUsage: ARXManualMigrateRuleExample.pl --url --user --pass --op \n"; print "\n"; print "Argument Description\n"; print "-------- -----------\n"; print "--url The base URL of the web service on the ARX. Both http and https\n"; print " are supported. The format is:\n"; print "\n"; print " http(s):// : \n"; print "\n"; print " : DNS resolvable hostname or IP address\n"; print " : 83 for http or 843 for https\n"; print "\n"; print "--user The username for authentication.\n"; print "--pass The password for authentication.\n"; print "\n"; print "--op The ARXManualMigrateRule API method that will be called:\n"; print "\n"; print " create\n"; print " create_and_configure\n"; print " set_migrate_close_file\n"; print " set_report\n"; print " set_enable\n"; print " get_list\n"; print " get_configuration\n"; print " get_status\n"; print " migrate_files\n"; print " remove\n"; print "\n"; } #------------------------------------------------------------------------------- # sub getSecurityHeader(user, pass) # # This subroutine builds a security header that will be used for # authentication. This type of security header is required for all calls to # iControl::ARX interfaces, so it makes sense to have this subroutine stored in # a library for common access. #------------------------------------------------------------------------------- sub getSecurityHeader { my $user = shift; my $pass = shift; my $now = time(); my $then = time() + 60; my $created = strftime("%Y-%m-%dT%H:%M:%S", gmtime($now)) . 'Z'; my $expires = strftime("%Y-%m-%dT%H:%M:%S", gmtime($then)) . 'Z'; my $secExt = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; my $secUtil = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'; my $securityHeader = SOAP::Header->name("wsse:Security")->attr( { 'xmlns:wsse'=> $secExt, 'xmlns:wsu'=> $secUtil } ); my $timestamp = SOAP::Data->name("wsu:Timestamp" => \SOAP::Data->value( SOAP::Data->name('wsu:Created')->value($created) ->type(''), SOAP::Data->name('wsu:Expires')->value($expires) ->type(''))); my $usernameTokenType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"; my $usernameToken = SOAP::Data->name("wsse:UsernameToken" => \SOAP::Data->value( SOAP::Data->name('wsse:Username')->value($user) ->type(''), SOAP::Data->name('wsse:Password')->value($pass) ->type('') ->attr({'Type'=>$usernameTokenType}))); $securityHeader->value(\SOAP::Data->value($timestamp, $usernameToken)); return $securityHeader; } sub create { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Calling the \"create\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule: $manualMigrateRule\n\n"; # Build a security header my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->create(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rule')->value($manualMigrateRule), $securityHeader); # Check if there were any faults encountered during the operation. # We find this by checking if the fault member of the result object # is set. If there is a fault, then we can print the detailed # fault text using the faultstring member of the result object. if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub create_and_configure { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Please specify a report prefix:\n\n"; chomp(my $prefix = <>); print "\n"; print "Please specify either 'true' or 'false' for 'verbose':\n\n"; chomp(my $verbose = <>); print "\n"; print "Please specify either 'ARX_POLICY_REPORT_DELETE_UNKNOWN', 'ARX_POLICY_REPORT_DELETE_EMPTY', or 'ARX_POLICY_REPORT_ERROR_ONLY' for 'delete_report':\n\n"; chomp(my $delete_report = <>); print "\n"; print "Please specify either 'ARX_POLICY_REPORT_INTERVAL_UNKNOWN', 'ARX_POLICY_REPORT_INTERVAL_HOURLY', or 'ARX_POLICY_REPORT_INTERVAL_DAILY' for 'interval':\n\n"; chomp(my $interval = <>); print "\n"; print "Please specify either 'true' or 'false' for 'migrate_close_file':\n\n"; chomp(my $migrateCloseFileValue = <>); print "\n"; print "Please specify files to be included in the 'exclude_file_set':\n\n"; chomp(my $excludeFileSetValue = <>); print "\n"; print "Please specify either 'true' or 'false' for 'enable':\n\n"; chomp(my $enable = <>); print "\n"; if ($migrateCloseFileValue ne "true" && $migrateCloseFileValue ne "false") { confess("migrate_close_file value must be either 'true' or 'false'.\n"); } my $baseReport = SOAP::Data->name('report' => \SOAP::Data->value(SOAP::Data->name('prefix' => $prefix), SOAP::Data->name('verbose' => $verbose), SOAP::Data->name('delete_report' => $delete_report))); my $intervalReport = SOAP::Data->name('report' => \SOAP::Data->value(SOAP::Data->name('interval' => $interval), $baseReport)); my $migrateCloseFile = SOAP::Data->name('migrate_close_file' => \SOAP::Data->value(SOAP::Data->name('migrate_close_file' => $migrateCloseFileValue), SOAP::Data->name('exclude_file_set' => $excludeFileSetValue))); my $configuration = SOAP::Data->name('configuration' => \SOAP::Data->value(SOAP::Data->name('rule_name' => $manualMigrateRule), SOAP::Data->name('enable' => $enable), SOAP::Data->name('report' => $intervalReport), $migrateCloseFile)); print "Calling the \"create_and_configure\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule_name: $manualMigrateRule\n"; print "enable: $enable\n"; print "report:\n"; print " interval: $interval\n"; print " report: \n"; print " prefix: $prefix\n"; print " verbose: $verbose\n"; print " delete_report: $delete_report\n"; print "migrate_close_file:\n"; print " migrate_close_file: $migrateCloseFileValue\n"; print " exclude_file_set: $excludeFileSetValue\n\n"; my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->create_and_configure(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), $configuration, $securityHeader); if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub set_migrate_close_file { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Please specify either 'true' or 'false' for 'migrate_close_file':\n\n"; chomp(my $migrateCloseFileValue = <>); print "\n"; print "Please specify files to be included in the 'exclude_file_set':\n\n"; chomp(my $excludeFileSetValue = <>); print "\n"; if ($migrateCloseFileValue ne "true" && $migrateCloseFileValue ne "false") { confess("migrate_close_file value must be either 'true' or 'false'.\n"); } my $migrateCloseFile = SOAP::Data->name('migrate_close_file' => \SOAP::Data->value(SOAP::Data->name('migrate_close_file' => $migrateCloseFileValue), SOAP::Data->name('exclude_file_set' => $excludeFileSetValue))); print "Calling the \"set_migrate_close_file\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule: $manualMigrateRule\n"; print "migrate_close_file:\n"; print " migrate_close_file: $migrateCloseFileValue\n"; print " exclude_file_set: $excludeFileSetValue\n\n"; my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->set_migrate_close_file(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rule')->value($manualMigrateRule), $migrateCloseFile, $securityHeader); if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub set_report { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Please specify a report prefix:\n\n"; chomp(my $prefix = <>); print "\n"; print "Please specify either 'true' or 'false' for 'verbose':\n\n"; chomp(my $verbose = <>); print "\n"; print "Please specify either 'ARX_POLICY_REPORT_DELETE_UNKNOWN', 'ARX_POLICY_REPORT_DELETE_EMPTY', or 'ARX_POLICY_REPORT_ERROR_ONLY' for 'delete_report':\n\n"; chomp(my $delete_report = <>); print "\n"; print "Please specify either 'ARX_POLICY_REPORT_INTERVAL_UNKNOWN', 'ARX_POLICY_REPORT_INTERVAL_HOURLY', or 'ARX_POLICY_REPORT_INTERVAL_DAILY' for 'interval':\n\n"; chomp(my $interval = <>); print "\n"; my $baseReport = SOAP::Data->name('report' => \SOAP::Data->value(SOAP::Data->name('prefix' => $prefix), SOAP::Data->name('verbose' => $verbose), SOAP::Data->name('delete_report' => $delete_report))); my $intervalReport = SOAP::Data->name('report' => \SOAP::Data->value(SOAP::Data->name('interval' => $interval), $baseReport)); print "Calling the \"set_report\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule: $manualMigrateRule\n"; print "report:\n"; print " interval: $interval\n"; print " report: \n"; print " prefix: $prefix\n"; print " verbose: $verbose\n"; print " delete_report: $delete_report\n\n"; my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->set_report(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rule')->value($manualMigrateRule), $intervalReport, $securityHeader); if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub set_enable { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Please specify either 'true' or 'false' for 'enable':\n\n"; chomp(my $enable = <>); print "\n"; print "Calling the \"set_enable\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule: $manualMigrateRule\n"; print "enable: $enable\n\n"; my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->set_enable(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rule')->value($manualMigrateRule), SOAP::Data->name('enable' => $enable), $securityHeader); if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub get_list { my ($namespace, $volume) = @_; # Get a list of manual migrate rules configured on the ARX. print "Calling the \"get_list\" method of the ARX ManualMigrateRule interface.\n\n"; # Build a security header my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->get_list(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), $securityHeader); # Check if there were any faults encountered during the operation. # We find this by checking if the fault member of the result object # is set. If there is a fault, then we can print the detailed # fault text using the faultstring member of the result object. if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } print "Printing the results of the call to the \"get_list\" method of the ARX ManualMigrateRule interface.\n\n"; # The get_list() call did not fail, so we build a list of manual migrate rule # names from the result. Note that the full result is a # concatenation of the result and paramsout members of the SOAP # result object. my @manualMigrateRuleList = ($manualMigrateRuleSoapResult->result, $manualMigrateRuleSoapResult->paramsout); if ($#manualMigrateRuleList < 0) { print("The list of manual migrate rules returned from the call to the \"get_list\" method of the ARX ManualMigrateRule interface was empty.\n"); exit(0); } # We can now print the list of manual migrate rules print "Manual Migrate Rule list:\n"; foreach my $mmr (@manualMigrateRuleList) { print " ", $mmr, "\n"; } } sub get_configuration { my ($namespace, $volume) = @_; # Get a list of manual migrate rules configured on the ARX. # Build a security header my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->get_list(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), $securityHeader); # Check if there were any faults encountered during the operation. # We find this by checking if the fault member of the result object # is set. If there is a fault, then we can print the detailed # fault text using the faultstring member of the result object. if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } # The get_list() call did not fail, so we build a list of manual migrate rule # names from the result. Note that the full result is a # concatenation of the result and paramsout members of the SOAP # result object. my @manualMigrateRuleList = ($manualMigrateRuleSoapResult->result, $manualMigrateRuleSoapResult->paramsout); if ($#manualMigrateRuleList < 0) { print("The list of manual migrate rules returned from the call to the \"get_list\" method of the ARX ManualMigrateRule interface was empty.\n"); exit(0); } # get manual migrate rule configuration from API print "Calling the \"get_configuration\" method of the ARX ManualMigrateRule interface.\n\n"; # Build a security header $securityHeader = getSecurityHeader($user, $pass); # In addition to printing the list of manual migrate rules, we can actually # use that list to retrieve configuration information # for all of the manual migrate rules using the same list by calling # get_configuration(). $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->get_configuration(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rules')->value(@manualMigrateRuleList), $securityHeader); if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } print "Printing the results of the call to the \"get_configuration\" method of the ARX ManualMigrateRule interface.\n\n"; my @manualMigrateRuleConfigs = ($manualMigrateRuleSoapResult->result, $manualMigrateRuleSoapResult->paramsout); foreach my $manualMigrateRuleConfig (@manualMigrateRuleConfigs) { my $rule_name = $manualMigrateRuleConfig->{'rule_name'}; print "----------------------------------------------\n"; print "Manual Migrate Rule: ", $rule_name, "\n"; print "----------------------------------------------\n\n"; print "rule_name: ", $rule_name, "\n"; my $enable = $manualMigrateRuleConfig->{'enable'}; print "enable: ", $enable, "\n"; if (exists $manualMigrateRuleConfig->{'report'}) { my $interval_report_config = $manualMigrateRuleConfig->{'report'}; print "report:\n"; my $interval = $interval_report_config->{'interval'}; print " interval: ", $interval, "\n"; if (exists $interval_report_config->{'report'}) { my $base_report_config = $interval_report_config->{'report'}; print " report:\n"; my $prefix = $base_report_config->{'prefix'}; print " prefix: ", $prefix, "\n"; my $verbose = $base_report_config->{'verbose'}; print " verbose: ", $verbose, "\n"; my $delete_report = $base_report_config->{'delete_report'}; print " delete_report: ", $delete_report, "\n"; } } if (exists $manualMigrateRuleConfig->{'migrate_close_file'}) { my $migrate_close_file_config = $manualMigrateRuleConfig->{'migrate_close_file'}; print "migrate_close_file:\n"; my $migrate_close_file = $migrate_close_file_config->{'migrate_close_file'}; print " migrate_close_file: ", $migrate_close_file, "\n"; my $exclude_file_set = $migrate_close_file_config->{'exclude_file_set'}; print " exclude_file_set: ", $exclude_file_set, "\n"; } print "\n"; } } sub get_status { my ($namespace, $volume) = @_; # Get a list of manual migrate rules configured on the ARX. # Build a security header my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->get_list(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), $securityHeader); # Check if there were any faults encountered during the operation. # We find this by checking if the fault member of the result object # is set. If there is a fault, then we can print the detailed # fault text using the faultstring member of the result object. if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } # The get_list() call did not fail, so we build a list of manual migrate rule # names from the result. Note that the full result is a # concatenation of the result and paramsout members of the SOAP # result object. my @manualMigrateRuleList = ($manualMigrateRuleSoapResult->result, $manualMigrateRuleSoapResult->paramsout); if ($#manualMigrateRuleList < 0) { print("The list of manual migrate rules returned from the call to the \"get_list\" method of the ARX ManualMigrateRule interface was empty.\n"); exit(0); } # get manual migrate rule status from API print "Calling the \"get_status\" method of the ARX ManualMigrateRule interface.\n\n"; # Build a security header $securityHeader = getSecurityHeader($user, $pass); # In addition to printing the list of volumes, we can actually # use that list to retrieve status information for all of the volumes # using the same list by calling get_status(). $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->get_status(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rules')->value(@manualMigrateRuleList), $securityHeader); if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } print "Printing the results of the call to the \"get_status\" method of the ARX ManualMigrateRule interface.\n\n"; my @manualMigrateRuleStatuses = ($manualMigrateRuleSoapResult->result, $manualMigrateRuleSoapResult->paramsout); foreach my $manualMigrateRuleStatus (@manualMigrateRuleStatuses) { my $mmr_status_rule_name = $manualMigrateRuleStatus->{'rule_name'}; print "----------------------------------------------\n"; print "Manual Migrate Rule: ", $mmr_status_rule_name, "\n"; print "----------------------------------------------\n\n"; print "rule_name: ", $mmr_status_rule_name, "\n"; my $available_request_slots = $manualMigrateRuleStatus->{'available_request_slots'}; print "available_request_slots: ", $available_request_slots, "\n"; } } sub migrate_files { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Please specify a file list:\n\n"; chomp(my $fileList = <>); my @fileListArray = ($fileList); print "\n"; print "Please specify a target share (hit to specify a target share farm instead):\n\n"; chomp(my $share = <>); print "\n"; print "Please specify a target share farm (hit if a target share was previously specified):\n\n"; chomp(my $shareFarm = <>); print "\n"; print "Calling the \"migrate_files\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule: $manualMigrateRule\n"; print "files: $fileList\n"; print "share: $share\n"; print "share_farm: $shareFarm\n\n"; # Build a security header my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->migrate_files(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rule')->value($manualMigrateRule), SOAP::Data->name('files')->value(@fileListArray), SOAP::Data->name('share')->value($share), SOAP::Data->name('share_farm')->value($shareFarm), $securityHeader); # Check if there were any faults encountered during the operation. # We find this by checking if the fault member of the result object # is set. If there is a fault, then we can print the detailed # fault text using the faultstring member of the result object. if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub remove { my ($namespace, $volume) = @_; print "Please specify a rule name:\n\n"; chomp(my $manualMigrateRule = <>); print "\n"; print "Calling the \"remove\" method of the ARX ManualMigrateRule interface with the following parameters:\n\n"; print "namespace: $namespace\n"; print "volume: $volume\n"; print "rule: $manualMigrateRule\n\n"; # Build a security header my $securityHeader = getSecurityHeader($user, $pass); my $manualMigrateRuleSoapResult = $manualMigrateRuleSoap->remove(SOAP::Data->name('namespace')->value($namespace), SOAP::Data->name('volume')->value($volume), SOAP::Data->name('rule')->value($manualMigrateRule), $securityHeader); # Check if there were any faults encountered during the operation. # We find this by checking if the fault member of the result object # is set. If there is a fault, then we can print the detailed # fault text using the faultstring member of the result object. if (defined $manualMigrateRuleSoapResult->fault && $manualMigrateRuleSoapResult->fault) { confess("SOAP request failed:\n" . objdump($manualMigrateRuleSoapResult->fault) . "\n"); } } sub objdump { my ($obj, $indent) = @_; my $content = ''; if (!defined $obj) { return $content; } if (!defined $indent) { $indent = ' '; } my $type = ref $obj; if (!defined $type || $type eq '' || $type eq 'SCALAR') { $content = $content . $indent . $obj . "\n"; } elsif ($type eq 'ARRAY') { foreach my $node (@$obj) { $content = $content . objdump($node, $indent); } } else { my $key; my $value; while (($key, $value) = each %$obj) { my $type2 = ref $value; if (!defined $type2 || $type2 eq '' || $type2 eq 'SCALAR') { $content = $content . $indent . "\'$key\' => $value;\n"; } else { $content = $content . $indent . "\'$key\' => {\n"; $content = $content . objdump($value, $indent.' '); $content = $content . $indent . "}\n"; } } } return $content; }305Views0likes0Comments