Planet Collab

🔒
❌ About FreshRSS
There are new available articles, click to refresh the page.
Before yesterdaySam I Am Sam

{a cisco spark auth API “shim”}

By Sam Womack

For those of you who will be creating Spark Bots that need to be able to perform authentication to get an access_token and/or refresh_token without a FrontEnd Authorization flow..currently there is no way to do that..until now.  First however, you have to use a NON-Corporate/SSO Account (@wwt.com accounts do not work as I would imagine other CORP entities): I have a GMAIL account (cscospark@gmail.com) which works perfectly.

How does it work:

curl -H "Content-Type: application/json" -X POST -d '{"user":"spark email","pass": "spark password", "id":"app client_id","secret":"app client_secret","redirectUri":"spark app redirect_uri"}' --insecure https://45.55.244.195/authenticate

If you execute the request with the proper credentials passed in..a JSON object will be returned with an Access_Token and Refresh_Token as referred and commented on here.

If you are interested in perhaps a bit more documentation of the API and/or want to take a look at the code: https://github.com/drkchiloll/cscospark-websvcs.git

{f5 bigIQ 4.5 rest api intro}

By Sam Womack

The jury is out on technical blogs and perhaps how much longer I’m going to do this (at least for me); I’m thinking of moving everything over to Jekyll just to be closer to github because if I continue this..I’m just going to point you to my project on github anyhow..like today.

For this blog post, I suspect you already know what BigIQ is..However, don’t feel bad, I’ve never programmed an F5 Device (and still haven’t gotten my hands on any part of it yet besides getting controlled devices (BigIP) and Backing them up (which is probably best done using the BigIQ interface to begin with).

Today I wanted to share my Introduction to F5’s BigIQ REST API Authentication scheme..which is somewhat of a Kludge. BigIQ using BASIC Authentication like a lot of the Networking companies (many shops are moving to OAUTH api key + tokens); Basic takes the username:password and hashes the user and pass together as shown (with the : in between them)..However, with BigIQ they take the Authentication process 1 Step Further in the same REQUEST; In the Body of the request you must pass a JSON object like below.

{
  "username" : user,
  "password" : pass
}
//If using LDAP there is one other property which I document in the README file in GITHUB

Normally, when utilizing BASIC Auth, you can GET information from the Very Beginning (/bigiq/bigipdevices). However, before you can start asking for devices, you have to Authenticate; when you Authenticate, BigIQ passes back a Token; this token is then used so that you can interact with the devices BigIQ manages. How are you Following Along? Let’s look at the Request Options Below for Authentication

{
  uri     : HTTPS + /mgmt/shared/authn/login,
  method  : POST,
  //BASIC AUTH
  auth    : { user : user, pass : pass },
  headers : {
    'Content-Type' : 'application/json',
    'Accept'       : 'application/json'
  },
  //Didn't I already Pass that in?
  //This is the Message Body Though..
  json    : { username : user, password : pass }
}

If the request above is successful you are return an Object..and one of the Properties of this Object contains your Token (actually 2 one of the properties is name and the other is token..they both have the same value..hmm). One you have the Token..so something like this below

headers['X-F5-Auth-Token'] = token;

Let me know if you find this post useful in any way..and before I forget..here’s the project on github..

https://github.com/drkchiloll/f5bigiq.git

return sam;

{uccx api: dynamically changing an agents skill}

By Sam Womack

Product: UCCX 9.x and above (the configuration API wasn’t available until 9.x)
Scenario: An Agents answers calls for CSQ A from 12PM – 6PM and then needs to answer calls for CSQ B from 6:30PM – 8PM.
Problem: In UCCX, the only way for an Agent to be a member of more than one CSQ is to use Skills. In the Post, I will use a very simple demonstration of a Use of the API to Automate the Changing of an Agents Skill in order for that Agent to answer calls from different CSQs (and not overlap).
Disclaimer: This Post assumes you have Node.js installed and the Request Library loaded as a dependency in your project. I’m not going to show you how to install Node and or Request. The code I will leave you is classified as a Minimum Viable Product; for example, it seems as though the NodeJS rendition of the setTime function could cause memory leaks. Use this code at your own risk..Ultimately I wanted to demonstrate the use of the API in a real-world scenario as mentioned here.

I could use many tools to perform this little magic trick..Python, Java, Ruby, etc; today I wanted to break out Node.JS mainly to show you CRUD operations in a different language..and Node is just cool too!

Let’s break down this problem into it’s simplest form:

/*
First this script needs to be executed before 6:30pm.

The Script starts by getting the difference btw script
execution and the first Agent Skill Change 6:30pm (1830). This
intervalTime is used in a JS setTimeout function that fires
a function when the Interval is reached (@1830). 

At that point, the Agent Skill gets changed and
another Interval Time is set for 8pm (2000).

When the 8pm interval is triggered, the Agent skill changes
to the initial skill again and the Datetime is reset for the
following day at 6:30pm.
*/

The first problem I had to tackle (using javascript) was Time. As I mentioned this script needs to execute before 6:30pm when the Agent’s Skill needs to change.

module.exports.getInterval = function(iter) {
 var date = new Date();
 switch (iter) {
  case 1:
   var changeSkill =
    new Date(date.getFullYear(), date.getMonth(), date.getDate(), 18, 30, 00);
   break;
  case 2:
   var changeSkill =
    new Date(date.getFullYear(), date.getMonth(), date.getDate(), 20, 00, 00);
   break;
  case 3:
   var tomDate = new Date(date);
   //Add One Day to Today's Date to get Tomorrow
   tomDate.setDate(date.getDate() + 1);
   var changeSkill =
    new Date(tomDate.getFullYear(), tomDate.getMonth(), tomDate.getDate(), 18, 30, 00);
  }
  return changeSkill.getTime() - date.getTime();
}

Suffice it to say, the first iteration (case 1) will only ever run once (during the initial execution of the script). In the 3rd interval, the Skill is changed and the clock is reset to change the Skill Again..for the following day @ 1830 (6:30pm). And the return is in milliseconds: setTimeout(callback, millisec)

This script is set to run indefinitely (on whatever computer/server you deem viable: Node runs on Windows, OS X, and many flavors of Linux distros); it is able to run indefinitely by using setTimeout which calls a function that executes our script logic..resets our interval and then calls the setTimeout again in a recursive manner..this is actually a pretty common scenario.

function callback() {
  if (skill is this) {
    //do stuff including setting new interval
    setTimeout(callback, interval);
  } else {
    //do other stuff including setting new interval
    setTimeout(callback, interval);
  }
}
setTimeout(callback, interval);

Next up, is the API Call using CRUD: In this post Retrieve and Update (GET, PUT). First we have to Create an Object (JSON) which are the Options we will use to make our Request..below is what is looks like..it should actually be pretty self-explanatory unless you are new to this..

var options = {
  uri : uri,
  method : 'GET/POST/PUT/DELETE',
  auth : {
    user : username,
    pass : password
  },
  header : {
    'Content-Type' : 'application/json',
    'Accept' : 'application/json'
  },
  strictSSL : false,
  json : jsonObj
};

Next up is using Request which, once the Options Object has been put together properly is pretty “easy”.

var request = require('request');
request(options, function(error, response, body) {
  // Body will be our JSON data we want to PARSE
  var json = JSON.parse(body);
  //If there was an Error we will get a JSON object returned
  // We can collect our response (codes) in the case of
  // Updating data where JSON is not returned (201, 204, etc etc)
});

Ok things you will need to do/change in my script to get this off the ground:

// Change the IP Address in the Options
// Change User/Password
// Change The Skill Name to Match Yours (skill_a, skill_b)
// Change The Agent Name to Match Yours (resource/agent1)
// I use 2 Files for this..Keep them together in the same Directory...
// If you know NODE you'll know how to use this..
// To Execute the Example from the Command Line
$node timeEvent_example.js

And the CODE:

return sam;

💾

{unity connection api: schedules}

By Sam Womack

Recently, I’ve had the notion to create some automation tasks for Unity Connection (9.x) using the provided API (apparently it (the api) now goes back to 7.x) that uses REST..I’ve blogged about this before even..in Resetting a User’s VM PIN. Why am I spending time creating automation you might ask? The tasks that must be performed on a week by week basis (for the next 49 weeks) includes: Create a Class of Service, Directory Handler, Schedule (and Schedule Detail of course), GDMs, 2 Call Handlers, a User Template and a partridge in a pair tree..MULTIPLIED by 4..I’ve been doing all this for the past 6 weeks..all by hand..Today I’m going to briefly discuss automating the task of creating Schedules..(the actual Holiday Schedules are already configured..but and this doesn’t show you how to create a Holiday Schedule..but if you follow..then you might get the notion and/or no-how to do this 🙂

Before moving on with this, a few disclaimers must be added..this post is really about getting you started with creating Unity Schedules in a bulk fashion..I’m not sure there is any “built-in” methods to do this within Unity itself..unless you use COBRAS (and modify the CSV file?). My other intent is to share some code that might spawn ideas on how to do other interesting things using the API..

Diving right in, the first thing you have to do to create a schedule is get the Unity Location ObjectId; if you are “writing to” the Connect APIs you will need to get used to ObjectIds which is a Globally Unique ID (guid).. used for every object on the system..and you will have to parse it out of “documents” or returns from API calls routinely..To create a schedule here is a list of the items you need to do:

  1. Get the Unity Location Object
  2. Create the ScheduleSet
  3. Create the Schedule
  4. Apply the Schedule/Holiday as a ScheduleSet Members (one at a time)
  5. Create the Schedule Detail for the Schedule

If you are trying to learn Python, most of the blogs I’ve written (besides the contact center express ones) have demonstrated Python code..and I can’t express this enough..you really should get used to the Requests library as this is the one Python library that is most popular amoungst those perform Web Service calls..Below is the first step in the process of creating a schedule.

# Get the LocationObject
# GET /vmrest/locations/connectionlocations
objid = json.loads(req.content)['ConnectionLocation']['ObjectId']
#We Want JSON
headers = {'Accept' : 'application/json' }
#The Request Code:
req = requests.get(url,verify=False,headers=headers,auth=(user,pwd))

Create the ScheduleSet and the Schedule (these are essentially performed the same..the url is different..)..if there is a drawback of the API..this might be listed as one..unless I sat down with the Web Service development team and discussed the design decisions that were made..Why do I say this?..I will show you..and what I will show you..are Artifacts in the DB that are essentially abandoned..

Whenever a Schedule is created from the GUI, 2 Objects are actually created. The Schedule itself and a ScheduleSet let me demonstrate (however that works). So I created: GUI_Created_Schedule and you can check that Object out in the Database (OS Administration) twice..in the Schedule Table and the ScheduleSet Table.

admin:run cuc dbquery unitydirdb select objectid from tbl_Schedule WHERE DisplayName='GUI_Created_Schedule'

The return:

objectid
------------------------------------
abbd0501-6121-4cf2-ace7-1c44a84db169

If you want to get the ObjectId of ScheduleSet Object then just change the tbl to ScheduleSet(the ID is unique of course). Now..to the issue..if your code blows up..Let’s say I delete the ScheduleSet Object from above:

admin:run cuc dbquery unitydirdb delete from tbl_ScheduleSet WHERE DisplayName='GUI_Created_Schedule'
Rows: 1
#Indicates that it deleted 1 Row from the appropriate Table..

So as I mentioned..When a Schedule is created from the GUI, two objects are created..but I just removed one of them using a Query DB statement (I could have shown you an api way to do it too: request.delete). So the schedule is gone right?

AbandonedSchedule

As you can see above, the Schedule above has been officially abandoned and cannot be deleted from the GUI..Try it yourself 🙂 . Obviously you cannot delete the ScheduleSet from the GUI..it’s not really there. There are a few ways to fix the issue from above..you could create another ScheduleSet and then add the Schedule as a Member..For the rest of this post I’m going to piece together the code needed to “Complete The Schedule.”

Let’s get back to it..as I squirreled myself a few paragraph ago..when the abandonment of objects in the Unity Connection DB. First..we need to add to the Headers..which is a Python Dictionary Type (a “JSON” object). We also have to address the result of the Headers..when we get response data back from the server (cuc) we need to parse the JSON (which is converted to DICT type). And whenever data is “posted” (or “put”) on the server the DICT type has to be converted to JSON. Other points of interest when working with this API is how the ObjectId’s are passed for creating/updating objects and how they are returned. For the sake of Schedules I decided to Use Regular Expressions to parse the “content” of some of the data. (All these examples are below).

headers = {
           'Content-type' : 'application/json',
           'Accept'       : 'application/json'
          }
cucloc_json = json.loads(cuc_req.content)
sset_obj = {'ScheduleSetObjectId' : 'objid'}
data = json.dumps(sset_obj)
import re
parsestr = '/vmrest/users/f82b301d-1ec8-44c6-a3ee-0012269439bf'
regx = '/\w+/\w+/(\S+)'
objid = re.search(r''+regx+'',parsestr).group(1)
objid
'f82b301d-1ec8-44c6-a3ee-0012269439bf'

For the code above utilizing reguarlar expressions..you can input those lines into the python interpreter (>>>) to see how it works..I’m going to terminate this post..by leaving you with the code 🙂

cuc_schedule.py

return sam;

{uccx scripting: db get data}

By Sam Womack

In this follow up post to DB integration in UCCX, we’re finally going to attempt to get our hands dirty..First we’ll start easy and pull out a simple set of data from a single table with an easy sql statement. I also want to utilize the Java Array type in order to hopefully dispel any intimidation you might have toward this object (variable). And lastly, I’m going to write some equivalent Java code around JDBC that does away with the built-in Cisco steps to “open” your mind to the fact that although Cisco has a specific step for something doesn’t necessary require you to use it..(this will obviously be geared to those of you out there with some programming experience..whether it is C, python, perl, php, etc).

Here is a list we can use for this post. We probably won’t demonstrate getting all of this data..but I will demonstrate everything necessary to start from a single table..to joining multiple tables together to get at the data in the DB..

  1. Get the Authors First and Last Name
  2. How many titles are in circulation for the Author?
  3. What are the title names
  4. What is the Title’s Publisher
  5. What is the Price per Title
  6. What stores are selling the title
  7. How many sells per store per title

Let’s examine Question #1: In an IVR, the user interface would ask the caller for specific information in order to get the data..let’s look at the table where this information is at to determine what type of data a telephony user interface (tui) information would most accompany:

authors_fnln

Ah! field au_id can be used in a TUI..And the magic step to get this data? Get Digit String of course..however the dashes won’t exist and for the purposes of this post I won’t be leaving you any type of script that uses Triggering Contacts (because I doing this from the airport and I’m not going to use a phone..) but I will demonstrate putting in the dashes for this because it just sounds fun..Below, using a Set Step using the String (we want to assign value to) we will use to get the Data we want from the Database I will demonstrate how to format the String properly..

String s = "213468915";
//s represents the value we got from the GET DIGIT STRING STEP
if (s.length() == 9) {
     s = s.substring(0,3) + "-" + s.substring(3,5) +
         s.substring(5);
} else {
     //BAD INPUT..RE-ENTER
}
return s;

If we want to DB Get data from the Database, we have to use the DB Read Step; I showed you one of these in a previous post. And not only do you have to Read (connect) the database, the Select statement in the Read step has to be able get data from the Tables you are trying to reference.

dbread_select_auth_pub

However, the DB Get Step can only get data from one table at a time (as one would expect); here we are using authors as you can tell from the select  statement.

dbget_fn_ln

As you can see, the table, authors and the field names I am interested in are shown above. I’m not going to get too much into the rules of using these steps because I really don’t know all of the rules..I just adjust as I encounter “limitations”; and oh, btw, Cisco doesn’t really list the rules of the road either..only a particular SELECT statement doesn’t work:

SELECT count(*) from table

In the image below you will find a routine SELECT statement: * is a wildcard that returns all fields in a particular table if the where clause is matched; however, if you can help it..you should refine the statement down to just the information you need.

basicselectstmt1

All we want is the First Name and Last Name:

specficselectstmt2

Below is a very simple reactive debug for item number one.

ex_get_fn_ln

I realize you probably have to experience this for yourself in order to really get this..What you should take away from this is this: when you connect to the DB and use the DB Get Step..if you didn’t ask for the Field you are looking for in the SELECT statement..although you can see it above..doesn’t mean you will get it.

Now I’m going to switch gears. With the callers au_id I’m now going to check to see how many books this Author has in circulation; below is the table we’ll be referencing to get that data.

titles_table1

The Data we’ve collected is au_id. In the above Table (called titles) there is no reference to that data: au_id.  So how do we get the title(s) the authors (represented by au_id); we need to join a new table in our query from the table below. We are going to take the au_id and grab all the title_id(s) from the titleauthor table.

titleauthor1

Let’s get down to business..with the SELECT Statement we’re going to need in order to get the Data we want..

SELECT title from titles
join titleauthor on titles.title_id = titleauthor.title_id
join authors on titleauthor.au_id = authors.au_id
where authors.au_id='213-46-8915'

The UCCX Editor Screenshot:

dbread_titles

And as you can see above with (Number of rows returned) this particular author has 2 Titles in Circulation. And I probably should break down this statement just a bit:

SELECT title from titles
/*titles = table from db, title = field in titles */
join titleauthor on titles.title_id = titleauthor.title_id
/* titleauthor = table from db, title_id in that table
is equal to the same title_id field from titles */
join authors on titleauthor.au_id = authors.au_id
/* authors = table from db, au_id field from titleauthor
is equal to the same au_id from authors */

I don’t know if that will make any sense..but that’s about as good as I can explain the statement that ultimately retrieves title. Next I’m going to discuss the UCCX Scripting portion of this particular get from the DB.

Before we get the titles (for this example we are going to get more than 1 title) we need to “talk” about what variables we’ll need to “move the cursor” in the returned data from the DB and then the data structure we’ll use to store the returned data. From an IVR viewpoint, returning a Book Title to a telephony interface may not be ideal unless you have a Text to Speech Server that speaks that title name to the caller (just a disclaimer as I am using my example as a demonstration of getting data from a db..not necessarily the practical usage case for it). Below is the big picture of my application logic when I’m getting Titles using DB Read/Get:

readgetTitleBigPic

In explaining the graphic above, first (as we already know) we use the DB Read in order to get to the appropriate Table/Field(s) we want to get data from. Then we use a DB Get step to set the appropriate Application Values from the Values “extracted” from the DB (in this case a String called sTitle); if there is Data at the Cursor (you can read about how DB cursors thing the cursor in a document that moves line by line as you hit carriage-return) in the DB the Successful branch of the DB Get step is chosen.  Below is a graphic of the expression that is used in this case:

getdataSuccessBranch

In the following expression I actually implemented it (initially) at least 2 different ways. In the manner above, I have 2 int(s) that keep a count of 1.) number of book titles 2.) number of times we’ve transitioned from the Successful branch and the No Data branch of the Step (we only want to do it once).  So when we are getting the number of Titles we aren’t actually setting the value of our container (sTitleArr). After this expression is run, there is a GoTo Step that returns execution of the Application back to the DB Get Step to get the next set of Data (move the cursor to the next row). Really fast, I want to talk about that Array and how to populate value into it.  The first time the No Data branch is taken the Array is initialized with a Size (length) which is set to the size of the TitleCounter integer:

sTitleArr = new String[ 2 ];
//In my code it is new String[ iTitleCounter ]
//The Value becomes
sTitleArr = new String[] {"",""};

In this case, (I know) there are 2 Titles returned with the dataset that I’m using a staticly entered value..but that is not the programmatic way of doing things unless we know the constant (final) value (size) of the container. Then, back to the Successful output branch each index of the container (Array) is given a value of each successive Title that is returned from the Database:

//Loop 1
sTitleArr[0] = sTitle;
//Loop 2
sTitleArr[1] = sTitle;

In all of the languages that I know a Container (e.g. List, Array) starts at position 0..and just as a quick tip..if you tried to set a value for [ 3 ] you would get an ArrayIndexOutOfBounds Exception.

So allow me to post a closeup of the next expression I need to explain: the No Data Output Branch.

nodataoutput_1

From the above, if this is the 2nd time we’ve hit this step then we know ‘we’re finished’ however if not (False) then we Initialize our Array (container) with the Number of Titles that were counted from the Successful branch; we then return our TitileCount back to zero and Increment our DataCounter by 1 (so that next time we hit this branch..we will return True from this IF statement) then we go back to the DB Read Step (this is critical..you can’t go to the Get step).

At this point, I’m going to stop with a video of the script during a reactive debug and you can see the titles that are populated in the container (Array). Let me know what you think..

return sam;
DBPt3

💾

💾

💾

💾

{uccx scripting: db subsystem setup}

By Sam Womack

When I last left you in regards to ‘UCCX DB Integration’ I was talking about the tools that help aid you when you have a project that calls for you to interact with an external DataBase within a UCCX Script. Ok, it has been over 2 months and I still haven’t posted a part 2 in this “sql” series. Last week a friend asked me to help get him started with integrating with an MS SQL Server because as he said it..’it has been a while since I last did this.’

Product: UCCX 8.x + with Premium License, MS SQL Server 2000
Scenario: You need to interact with a MS SQL Server.

Most of this post is going to be in screenshots..at this point there will not be any code (that I leave behind)..as I’m just going to focus on setting up the DB Subsystem and testing the DB Connection from there.. and from the tool I use, which I previously introduced, called RazorSQL. Let’s get started:

You need the Java Driver used for the JDBC connection: (Subsystems>Database>Driver)
UploadMSSQLDriver
AfterJDBCDriverUpload

Add the Data Source: (Subsystems>Database>Data Source) and Testing the Connection…
TestDataSourceGUI

From the UCCX Appadmin perspective we are done now. We could go to UCCX Serviceability to check the status of our DB Subsystem..it should show that it is In Service from the Engine’s perspective:
DB_SubSystemInService

My next step in this process is to access ‘the’ Table in the DB that I am interested in from Razor..just to ensure I “know” what I’m doing…below is a screenshot of that along with the SELECT statement:
RazorSql_ReadDBSelect

Now that I know that works the first place to start in the UCCX Script in order for you to Get Data is you have to “connect” to the DB; you connect to the DB using the DB Read Step in the UCCX Editor; when you first connect to the DB using the step below, you need to Refresh the Database Schema (this is in the General tab below).

DBReadScr_RefreshDB

Once refreshed you will see the Data Source Name: (don’t worry about the Timeout value at this point)
DBReadScr_General

Next we are going to visit the Field Selection tab: Here, I am using the same SELECT statement I used when performing this in Razor; if you look close enough you can see that I returned 2 Rows of Data in this Test Connection..the same amount of Data returned within the RazorSQL Tool (at this point we know we are on the right track)

DBRead_TestFieldSelect

Going back to the Field Selection: In the previous Test Statement we used ‘Hard Coded’ data where variables will be used: the zipcode and the doctor category. So my next step is to replace these hardcoded (Strings) with variables from within my script:

DBReadTestFieldSel_Variables

This should get you off the ground when connecting to an MS SQL Server 2000-2008. Let me know what you think. Let me know if I can help you with SELECT statements..I am going to leave you with the currently supported driver for connecting with MS SQL Servers if you happen to need it HERE.

return sam;

{uccx scripting: write xml doc w/o keyword transform step}

By Sam Womack

This post was inspired by a conversation on the Cisco Technical Support Forums that was originally pinned in 2007 (fwiw I was still in the US Army then).

Can I write an xml document other than using the template file and keyword tranform step?
I have too many values (1000+) to transform, it would take too long to enter them manually in the keyword transform step…

In my previous post, “automating after hours on-call “engineer” rotation schedules (who has been assigned this role before?), I created an XML Document from the document respository using the Create XML Document Step, Parsed that document for specific values using XPath Expressions in the Get XML Doc Data Step, and then “Wrote to the XML Document”(we’re really “modifying” our XML Document here) using UCCX’s KeyWord Transform Step. In this post we are going to pretend that we cannot process this document the easy way; we are going to “manually” create the XML Document using the JDOM Java Library (UCCX Custom File), I’m going to demonstrate several JDOM Methods to get XML Document Data (some additional stuff for our learning purposes), then we are going to modify some of the XML Document’s Data, so we can then Upload the changes back to the UCCX repository..and thus helping answer the above question..that really was never answered until recently (using String..don’t do it..and the built-in Java DOM Library..hence I’m going to demonstrate a more elaborate and efficient means of doing it..of course this is definitely debatable 🙂

Product: UCCX 8.x + with Enhanced License or better
Scenario: Read Above..
Problem: The Keyword Transform Step GENERATE Entirely New XML files, and in the case of having over 1000 Elements in your XML File as the statement above suggests, if you want to perserve the values of your XML File..you have to create a Keyword for All 1000+ TextNodes for the Elements in the Doc since the Keyword Transform Step CANNOT accept an existing XML Document.
Disclaimer: Consume at your own risk.

If you cannot use the Cisco “built-in” method (Keyword Transform Document) for modifying XML (because of limitations etc), then this post is for you as long as you can connect the dots with your own data..Java offers up many ways to work with XML, and Java developers are a dime a dozen and if there isn’t 1 Post on working with XML in Java there are 100. As a UCCX engineer who might encounter a scenario such as this, the best thing to do is find a resource to help you; however, this is reality and most won’t do that. If you take one thing away from the posts that I concoct in regards to UCCX scripting, the language used is not proprietary and if you perform a simple search on parsing/modifying xml documents with Java you could find the “documentation” you need to solve the problem..outside of the UCCX documentation that I and everyone else for that matter says is lacking; Here is just one example of many you can find.

Since we have to go to Java to get work done, I am going to show you a really handy Java Library I like to use called JDOM. But first we’ll talk a little about the built-in Java Document Object Model Library. I’ve covered this before in previous posts, but the typical builtin library for XML across languages is very similar; in that previous post I covered Python and Java. If you look at JavaScript (where the DOM is alway present because HTML is built on the DOM) accessing and modifying the DOM is pretty much the same syntactically as the other two languages I mentioned. And for the record, I don’t memorize the code needed in order to get a job done. As far as XML is concerned, I know you have the Root Element of a Document: OnCallAgent and then all the Children of the Root are exactly 1 Branch Down from the Root (which makes this document that much easier to parse). So we’ll change the Document we used previously as noted below:

<?xml version="1.0" encoding="ISO-8859-1"?>
<OnCallAgent>
    <Number>6262</Number>
    <WeekOfYear>28</WeekOfYear>
</OnCallAgent>

<!--2 Different Docs-->

<OnCallAgent>
    <AgentDetails>
        <Number>6262</Number>
        <Name>John Doe</Name>
    </AgentDetails>
    <WeekOfYear>28</WeekOfYear>
</OnCallAgent>

In order to use the code I’m going to provide, you need to download jdom2 (I’m using jdom-2.0.5.jar)..I’ll leave a copy of the jar file in the files here. When using external JARs the page where you “activate” them tells you what you need to do after you Upload a file (when you click on Upload Custom Jar Files, the page redirects you to the Document Repository, upload the jar there, then return to Custom Classs Config to make the available class path entry –> selected classpath, then proceed to restart appropriate services):

CustomFileUploadThe documentation for using JDOM to perform XML file work is HERE (parsing, creating, modifying, extracting, et al). The layout is in about the same manner of the typical Java Langugage documentation. To get the process kicked off, first we have create a JDOM document for parsing. For the sake of the script we are going to build, there is one major method we’ll use, but it’s also important to understand at least 2 other methods (in my testcase code I’m leaving you with 3 methods):

//Create Our Parser
org.jdom2.input.SAXBuilder builder =
    new org.jdom2.input.SAXBuilder();
try {
    //Create JDOM DOC by Building it from the Parser
    org.jdom2.Document d =
        builder.build( DOC[onCallAgent.xml].getInputStream() );
catch(org.jdom2.JDOMException jex) {
    //Something Happened
}
//It is good practice to Protype these in a customary
//Java IDE, so you know what Exceptions the Methods
//That are used "Throws"
/*
Other Methods You Might Use
builder.build("http://ccxip:9080/documents/onCallAgent.xml");
builder.build("/opt/cisco/uccx/Documents/User/en_US/d.xml");
I'm leaving others behind in the Code I'll leave you with
*/

Once we have the Document we have to get the Root Element; this allows us to then navigate the “tree-like” structure of the document (DOM).

org.jdom2.Element root = d.getRootElement();
//Interested in the Root Name?
//Gives you an Idea of where you are in the DOC
//Not really needed for what we are doing
String rootName = root.getName();

Let’s take a peak at what the document tree looks like..the image is pretty crude but hopefully you get the point:XMLTree

The root element of this document is OnCallAgent. The children of the root are AgentDetails and WeekOfYear. At this point I should “print” out the Children of the Root for demonstration and to show you an oddity with the “UCCX Expression Language” (it actually hurts me to call it that). For those of you who can program in Java you will understand what I’m referring to with this oddity..just bare with me.

java.util.List elemList = root.getChildren();
String child1 = (org.jdom2.Element) elemList.get( 0 ).
    getName(); //Name
//Cast Element Type on the List.get(int) method
//To then Call the Element.getName() method
String child2 = (org.jdom2.Element)elemList.get(1).getName();
//Number

This may not make any sense to anyone..but I’m going to explain it regardless…In Java 5 and later Java introduced Generics/Parameterized Type for Container Classes such as Lists/ArrayLists et al so the normal method for instantiation is like such: List<Element> eList = root.getChildren(); this syntax allows us to “type” what is added to the List (container) otherwise the default type is java.lang.Object and when that is the case as is above..you have to Cast the Type when extracting the data from within the contents of the List Object..whereas when I call the List.get(int) method I have to Cast it to a JDOM Element because that what is actually returned..and then I can call the Element’s getName() method in order to see what the Value of the First Element in the XML Tree is. Why did I tell you all of that? UCCX doesn’t support Generics, so when you extract the data from the List into the “Type” you know it to be..you have to Cast (how many times did I say that now?).

Next we need to Get XML Document Data (Step) using the appropriate org.jdom2.Element methods. Then we will proceed to modify them. To modify them we have to get the appropriate ElementsName/ and Number/. Check it out below.

//Walk the TREE Branch by Branch
String sName = root.getChild("AgentDetails").
    getChild("Name").getText();
String sNum = root.getChild("AgentDetails").
    getChild("Number").getText();
String sWoy = root.getChild("WeekOfYear").getText();
//Modify Appropriate Elements
nameElem = root.getChild("AgentDetails").getChild("Name").
    setText("Jane Smith");
numElem = root.getChild("AgentDetails").getChild("Number").
    setText("9999");
//"Reassign" Value of our String(s)
sName = nameElem.getText();
sNum = numElem.getText();
//It might be easier to get the "agentDet" Element

We’re still not finished..Now we need to take the Document we have been working on and convert it into a com.cisco.doc.Document so it can be uploaded to the Document repository just like in the previous post..

org.jdom2.output.XMLOutputter out =
    new org.jdom2.output.XMLOutputter();
dOnCallAgent = TEXT[ out.outputString( d ) ];

I’m leaving you with all the code (plus the jdom jar file and the relevant XML file needed as before: OnCallAgent.xml)…First I have a “scratch” pad for demonstration (yes this is a uccx script)..the code in this scratch pad is runnable as long as you have a uccx system with the proper license, the JAR File, and time of course..The other item I have is the previous posts’ script but using JDOM instead of the build-in uccx xml steps; I’m not going to demonstrate how’s it has been modified..if you would like to play with it and/or use it..please play and use at your own risk..Our Next Post concerning this (unless I get distracted) is to take all the “exposed raw” code that I have written and JAR it:  AF_JDOM_Files

return sam;

{uccx scripting: after hours transfer automation}

By Sam Womack

Product: UCCX 8.x + with Enhanced License or better
Scenario: During after hours, you want to have the ability to automate the process of assigning the Representative who will answer the calls. The rotation occurs every week; the on call assignment/duration is MON-SUN.
Problem: I’m quoting someone who commented on a potential method of setting up an automated way of doing this: “You are right but it doesn’t work the way I’m thinking. Such as an on-call rotation. Still need to Manually hit a button.” What I’m demonstrating here solves that very statement…but with UCCX…
Disclaimer: There are a few other ways to do what I’m about to demonstrate, which have in fact been demonstrated on the Cisco technical support forums..If you use this script you use with limited support, but I do love to answer questions especially when it involves solving a puzzle..Obviously if this script isn’t abstracted enough for your use and you don’t understand that IF Step(you will come to see shortly 🙂 then you may find yourself in trouble if you use this.

This is not a new idea: the assignment of an OnCall Representative when you are outside of normal business hours. But over the past 2 weeks or so, I saw this question posed twice; the first question was actually around changing ScriptParams via the API…can you guess where I’m going to go with that one? The premise of the script that I’ll be writing about today revolves around A List of Numbers to Be Dial from a text file and 2 XML Documents (my approach is half and half; stick with the normal traditions of uccx scripting but break away with a very busy IF Step (you will see what I’m talking about momentarily).

Knowing what we know about how the rotation occurs (every Monday) we can then using Java Dates (actually a Calendar Object) to control that aspect of the Schedule:

java.util.Calendar c = java.util.Calendar.getInstance();
//By Default the First Day of the Week is SUNDAY
//This would Screw Up Our Rotation Plan as we want to
//Rotate OnCall RSRCS after the FULL WEEKEND.
c.setFirstDayOfWeek( java.util.Calendar.MONDAY );
//This var will be used for Comparison
//To the Value in the XML Document
iWeekOfYear = c.get( java.util.Calendar.WEEK_OF_YEAR );

Below is a representation of the XML document I’m using for the script; I will be providing this for you download pleasure if you want to use the script. How about making it better? If you’ve updated Documents before then you’ll notice the Keyword Transform Document Step; you can check it out as I provide you the script.

<?xml version="1.0" encoding="ISO-8859-1"?>
<OnCallAgent>
    <Number>2010</Number>
    <WeekOfYear>28</WeekOfYear>
</OnCallAgent>
//The "KeyWords"
%number%
%week%

I didn’t show you the simple text file..but it is just that: a simple text file with each “line” of the document containing a “phone number” (use the format that is associated with calling your phone numbers..cell numbers are normally 10 digits..followed by a newline). At this point, I’m going to step through the process of the IF Step in a sort of pseudocode style.

/*
 * Open OnCallAgent XML Document for Parsing
 * Extract the Number (//Number) and Week (//WeekOfYear)
 * IF Current WOY is the same as the DOC WOY
 * END IF
 * RETURN false
 * Continue Sending AfterHours Calls to the Num in the Doc
 * ELSE
 * Open the TEXT Document convert to "Container"
 * GET the Index of the NumInDoc var in the "Container"
 * IF the INDEX+1 is equal to the SIZE of the "Container"
 * SET the Num to Call to the First ITEM in the "Container"
 * END IF
 * ELSE
 * SET the NUMtoCALL to the next ITEM in the "Container"
 * END ELSE
 * SET the WeekOfYear to the Current WOY (int)
 * RETURN true
 */

For those of you who will not download the script for whatever reason:

AfterHours_1

There are a few way you could use this script depending on if it’s for your CallCenter after hours rep or from a transfer from a Hunt Group or whatever. If this is used to select the oncall rep from your Call Center then you may want to use this script as SubFlow Script that is called with No Arguments where the Number to Call is Returned by the Script: the sNumInDoc variable in the OutPut Mapping Tab of the SubFlow Step. You could also take the variables that are needed for this, create them in your current script and copy all the Steps into your existing Script: sNumInDoc is the “Destination” of your Call Redirect Step. Hope this has been useful..please let me know..I at least had fun..The Script that I am leaving you with is very Runnable with Few Adjustments: Put the DOCs (.txt, .xml) in their proper home (Documents>en_US>) and configure you uAdmin and password appropriately for your User..if you are interested in the ******** masking the password in that step..click on the Expression Editor for the “password” field, right click in that area, Protect (it will tell you to enter a password..that you would need to reenter if you UnProtect (same method) the field..I unprotected it in the files I’m providing). Enjoy and Consume at will here.

return sam;

{uccx tips & tricks: more dates part 2}

By Sam Womack

Product: UCCX 8.x + with Enhanced License or better
Scenario: For whatever reason, you want to have the ability as a script writer to get Today’s Date (that’s easy enough), Yesterday’s Date, and Tomorrow’s Date then we will Create Generated Date Prompts out of them that only play Month Day and not the Year.
Tip/Trick: Demonstrating the power of the Expression Editor when using a bit of Java code to get stuff done. (This could be said of a lot of the tip/tricks I like to write).
Disclaimer: There are a few other ways to do what I’m about to demonstrate, which have in fact been demonstrated on the Cisco technical support forums..

I think the majority of this post will be in a Text Editor. From a Java programming standpoint I’m not adding anything that hasn’t ever been answered on StackExchange..however, UCCX specific folks might not gravitate to StackExchange naturally, so you have what I’m producing..but if you are reading this then I suppose you might now.  The one difference from a UCCX standpoint you will see in this post is I will create a Java Object that is typically done with the Create Generated Prompt Step.

//First we need a Calendar Object
java.util.Calendar cal = java.util.Calendar.getInstance();
//Calendar Objs. don't get Instantiated with new
//Create Today's Date
Date today = new Date(); //Easy Enough
//Use CAL's add method (for tomorrow's Date)
cal.add(cal.DATE, 1);
Date tomorrow = cal.getTime();
//Put CAL Back to the Present Date
cal.setTime(today);
//Use CAL's add method (for yesterday's Date)
cal.add(cal.DATE, -1);
Date yesterday = cal.getTime();

I thought I would break from the TextEditor to talk briefly about the GeneratedDate Prompt. First Off, I’ve showed you once before how to Generate a Prompt that “speaks” the Date without you having to use that specific Step: 

Prompt p = P[] + new Date()
//This Plays out the Entire Date: Month Day Year

What if you didn’t want to use the Step but only wanted to speak the Month and Day? Or some other supported way? For you Java folks out there let me show you the innards of the Created Generated Prompt Step and then I will show you the “code” that it represents (in Java):

GenDate_1

Do you see the “Constructor Type” there? From a Java Object standpoint this is typically written like below:

new String("Hello World");
//So Generated Dates Look Like This
Prompt p = new com.cisco.prompt.GeneratedDate(today, true);
//As you can see from Above, this doesn't Play out the YEAR
//Another Constructors (easy)
//GeneratedDate(int year, int month)

I’m going to stop there and state the following: I’m not asking for debate on whether or not you should use this style when creating CCX Scripts. I wouldn’t necessarily steer you away from potential creativity because these types of “styles” sometimes lead to “breakthroughs” in ideas..So if you want to slam me for showing/demonstrating this “hidden” trick go ahead..you can debate with as many people as will give you audience..I won’t..but I will approve your comments as long as they are in good taste. Below is a look at the Expression Editor for the Code that has been Demonstrated (the GenDatePrompt has a different constructor that doesn’t play out Year Month(as ordered), but Month Year: November 2014).

ToYesterDayTomorrowExprEditor_1

What I’m going to leave you with is a runnable script that will convert Dates, convert to Prompt and then if you tweak the script a bit..upload the “wav” file to the Prompt repository. You can get the files here.

For the guy on the SupportForum who couldn’t “set a generated date prompt from a string variable”, none of the parameters take that type, but given the format you provided MMddyyyy there are a few ways to get to the result you need. By default the Constructor needed to Generate a Date Prompt is a Date. So you need to convert your String to Date; we could use the Set Step to do this, but you have to change the String Format to MM/dd/yyyy to have the String Auto Converted; I don’t like doing it that way. The majority of CCX Developers would use 3 Steps to eventually Play the Prompt that is Generated: Set, Create Generated Prompt, Play Prompt. However, everything could be created from the Play Prompt Step directly with a little programming..check out the screen shot:

TradNonTrad_1

//BTW, the any day part is really in the CAL.ADD Method
cal.add(Calendar.DATE, 180); //3 Months?
return sam;

{cisco api series: updating phones with owner user id}

By Sam Womack

*Update: The version of the requests library I used in this post was 2.2.1. Recently 2.5.0 was released (http://docs.python-requests.org/en/latest/) which seems to have broken SSL Self-signed certificates (blindly trusting the verification of a lab box). You can “downgrade” requests using Easy_Install:

#From OS X Open Your Terminal and perform the following
sudo easy_install uninstall requests
sudo easy_install https://pypi.python.org/packages/source/r/requests/requests-2.3.0.tar.gz
#On Windows from a CMD Line
easy_install -m requests
easy_install 'URL from ABOVE'

Product: CUCM 9.x – 10.x (this script was tested on CUCM 10.5 using the 9.1.x AXL ToolKit)
Scenario: You are on the other side of a Communications Manager from 6.x – 8.x upgrade to 9.x-10.x. Users in CUCM have appropriate Devices Associated with his/her UserId, but the OwnerUserId is not populated on the Phone itself.
Problem: Reconciling User Licenses in 9.x/10.x (ELM/PLM) in order to not be penalized with “less” licenses if/when we deploy Jabber and/or other services for our Users. By default, if you do not “associate” an OwnerUserName with a Device, you only consume a UCL Enhanced License which only allows an “associated user” to have 1 Device, meaning, no Jabber, etc etc devices can be added later for the “user” without purchasing appropriate licenses (again potentially).
Disclaimer: I take no responsibility and/or necessarily provide support for your consumption of this script, so Use at your own peril..and if you do use it, test run it in a lab environment first. This script does 2 things and does them well: It loops through (all) end users (not application users), populates all their controlled device associations and then updates all those devices with an appropriate ownerUserName; it also has the ability for Exclusions of end users (possibly admins who control devices that really aren’t theirs..such as the phone view user if not an application user).
Requirements: Python 2.7, Requests and LXML Libraries, a little programming knowledge is good as well. Watch how to video?

Below is a representation of what we want to fix (we do not want to have unassigned devices or we at least want to keep them at a minimum):

DevicesNoOwner

The script that was created in order to perform said task is significantly innocuous. The first thing that is always needed is where we need to connect and our credentials for login. Then we need to start accessing the User Directory via the User Data Service API (UDS API). UDS is a fairly new interface which silently crept into CUCM 8.6.2 but the details on how to “access” the interface and what was there to begin with weren’t “talked” about; it wasn’t until around the time of Cisco Live this year that the API was “officially announced” and the ability to perform certain configuration tasks for end users became supported such as setting Remote Destinations, Speed Dials, etc for device. Essentially, it is an extension of the CCMUser page..but customizable to the point where you could create standalone applications for the management of devices..in fact I have a Phone Management on Steroids application under development right now that takes UDS severals steps further..But I digress.  The beauty and the hope I have for UDS is will it become a replacement for AXL and if so..when is the party; UDS is a REST based WS and if you don’t understand the implications of that statement..then you have some history to attend to..the AXL/SOAP interface appeared in CUCM 4.x days..Below is how to request for the User Directory using the UDS API and a returned user:

GET https://ip:8443/cucm-uds/users?last=&max=64&start=0

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<users uri='https://ip:8443/cucm-uds/users' version='10.5.1'start='0' requestedCount='64' returnedCount='64' totalCount='5022'>
    <user uri='url'>
        <id>UUID</id>
        <userName>agent1</userName>
        ...
    </user>
</users>

Start above is an actual variable that we will increment as we move through each page of results (users that we are parsing out of the XML document) and as long as there are pages we will continue to loop and call the GET users service: by default the user search list in Enterprise Parameters is 64 objects per page..Once all the users and their accompanying devices are stored into our dictionary (‘json’) object we then use the ‘keys’ (userIds) to loop through the [list] of values within while using the updatePhone AXL (SOAP request) method to populate the ownerUserName field for the devices configuration..unless there are exclusions populated. I thought it would be handy if you could populate a text file with UserNames that you know you can’t use (because they have more devices associated that they own..). This file will be attached but the basic format is the following:

user1
user2
user3

There are ways to make this script much more flexible, and I already have but cannot release that version..it’s due elsewhere. However,  I’ll list off some of the things I’ve added:

Enhancement 1

Use a CSV file with the DeviceName, UserName prepopulated; you could get fancy as well by automating the creation of this CSV file by iterating through the users and writing/appending them to said CSV file..then parsing that file for accuracy.

Enhancement 2

We could enhance our Exclusions file by allowing more or adding inclusions and information that is left off is excluded, for example listing Device Pools that are included and excluding the rest, so if a Device is in an Included Device Pool updatePhone..but don’t update if the DP isn’t included.

Enhancement 3

An idea I like is creating a log file that captures users with no devices associated and/or errors that might occur within the script. Additionally, adding more checks by performing getPhone method to check to see if a device already has an owner assigned..and if so sending an alert to the console during the operation of script. There are always ways in which we can make the process “smarter”. Do you have any suggestions? Will this script be helpful for you?

Link to the files for your use: BlogOwnerUserStuff

//Happy Hacking!
return sam;

 

❌