Monthly Archives: April 2014

{uccx scripting: holiday check via XML}

There are numerous Holiday check scripts out there for CCX; I saw one yesterday that comes with some scripts Cisco provides for free (or used to anyhow) and thought I’d write this post with a script that is quite the dichotomy from that particular script..Of course the objective is the same: Is Today a Holiday or Not?

Below is the Document we are going to parse..

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Holidays>
  <Holiday>1/1/14</Holiday>
  <Holiday>1/20/14</Holiday>
  <Holiday>2/17/14</Holiday>
  <Holiday>7/4/14</Holiday>
  <Holiday>9/1/14</Holiday>
  <Holiday>10/17/14</Holiday>
  <Holiday>11/27/14</Holiday>
  <Holiday>12/25/14</Holiday>
  <Holiday>1/1/15</Holiday>
</Holidays>

Let’s briefly go over some terminology when it comes to XML. Holidays is considered the Root of this Document; other ways in which to refer to an item in an XML file are: Element, Tag, Node. In fact every Element within an XML Document is considered a Node; you have the Elements

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Holidays><Holiday></Holiday></Holidays>

Holidays is the Root Node/Element of the Document..another way to say this is the Root Tag is Holidays and the child element (Tag) is Holiday each of these Elements can be referred to as XML Nodes as well..another XML Node is the Text Node values of the Holiday Elements: the Dates in “M/dd/yy” Format (Short). Ok..let’s break this down in the most simple term I possibly could; once I’m done I feel that you will get it!

//First We Have a String that will be converted to a
//(java.text.DateFormat.SHORT) style
//Cisco Set Step Follows:
Set todaysDate = new Date()
//Properties of this shows Short Date
//Download the Document, Setup for PARSING
//DOC is Store in DOC Repository
dHolidays = Create XML Document(DOC[Holidays.xml])
//The Parser is SAX (from the STD Java Library)
//Next we use the Get XML DOC Data
//To do this you have to use XPath
//holiday is a String type
holiday = Get XML Documnet Data(dHolidays,
     "//Holiday[.='" + todaysDate + "']")
//The Xpression above is 3 Strings
//Concat'd together
If(todaysDate == holiday) Then
    True
        Set isHoliday = true
    False
        //Today is Not a Holiday

Because there are multiple Holiday Nodes, in our XPath Expression we need to pass in a “String” Value that matches the Style in the Document (“M/dd/yy”) which essential returns the same text if that Date exists in the document; hopefully that makes sense..it is a weird looking “syntax” (better than descendant/child IMO) but if you read what XPath is, Wikipedia says it best:

XPath, the XML Path Language, is a query language
for selecting nodes from an XML document. In addition,
XPath may be used to compute values (e.g. strings, numbers,
or Boolean values) from the content of an XML document.
XPath was defined by the World Wide Web Consortium (W3C).

We are done..it can’t get much easier than that to check for a Holiday. I’m going to leave you with the Script and the DOC that I used in this Post. Most use HolidayChecks with SubFlow Steps in the Script..but why? In fact, why do we store the Holidays in an XML file to begin with; as a guy said it best to me the other day in the SupportForums, “this is how I was taught” and that voodoo is the hardest break…Open the script in the version of CCX you are running (it should work on 7.x and above)

HolidayCheckScript

Lastly I decided I would leave you with a video..this video depicts 2 Debug Sessions..1 where the Date Doesn’t Match Today (Not A Holiday) and 1 where I Manually Set Today’s Date to Holiday in the Document..so (isHoliday will equal TRUE)..If you want to watch the video in HD..then you’ll have to go over to the vimeo site and watch it there..And before I forget, here are the files I promised: HolidayCheck

return sam;

{cisco uc devops: remotely deleting the ITL cert from a 7900 series phone}

Del78series

 

 

 

 

Can you guess what type of phone is depicted in the Image above? First one to guess correctly..wins…

In todays installment we are going to dive into Cisco IP Phone Services Development; we aren’t going to develop any Services, but we will do something that might be somewhat useful for everyone interested in this blog..I am going to demonstrate the remote deletion of an ITL file “just like they do” with the paid applications and just as an FYI, PhoneView’s secret sauce to sending commands like Backgrounds et al is XSI Object Passthrough..Ok now that that is out of the way..today we are just going to POST XML objects straight to a Cisco IP Phone..So, there are a few things that have to be in order before we can get this to work: Web Access has to enabled on the Phone, the CUCM end user account you will use will need to be associated to the phones you are going to “touch”, and the Authentication URL needs to be pointing to a CUCM Node.
Continue reading

{uccx problem solving series: XML and the outdated SAX parser}

A lot of you reading the title of this post may be asking yourself..what does a “SAX” have to do with UCCX…I’m sure there are many bugs to pick with how Cisco’s products operate (if that was a pun then it was definitely intended) especially when things go sideways..such as parsing XML documents. When I use the Create XML Document normally followed up with the Get XML Document Data what is occuring under the hood..besides supplying the Java Beans with Document, XPath Expression (what the H is that), and Output(String)..e.g. the “return” or the Data we really want from that document. That my dear readers is a mystery that you will “never” find out(never say never)..until an exception is thrown, you look deep into the bowls of the log files, and/or you “hack” the box and download the JARs Cisco uses for the product. But back to SAX: simply put *I believe*(based on debug outputs I will show) it is the parser Cisco uses behind the scenes to help Aid us (or hinder us in the case today) in the Parsing of XML Documents..and the version of it would be SAX1 (and I surmise this because v1 doesn’t handle XML Namespaces well and SAX2 addressed those issues).

This post is/was inspired by a Cisco supportforum thread in which an XML Document was being downloaded, “parsed”(Create XML Document actually sets up the Parser), and an attempt to extract data “failed” on the DOC (null String; the doc in that post wasn’t a well-formed doc btw)..Lets take a look at what the well-formed XML is supposed to be:

<?xml version="1.0" encoding="UTF-8"?>
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">
     <storeInfo>
          <storeID>654</storeID>
          <storeType>Main</storeType>
     </storeInfo>
</string>

There nestled in the root tag/element (<string>) is the surreptitious NameSpace. Again as I stated earlier using UCCX’s standard procedure for parsing XML *DOES NOT WORK:

xml_1
Continue reading

{cisco unity connection rest: reseting a users vm pin}

Starting with Cisco Unity Connection 7.x?? Cisco allowed third-party applications to access a REST interface in order to perform configuration tasks, et al on the system without having to navigate the Administrative Pages of cuadmin. Today, I will leave you with the most “basic” script that makes use of this API: we will Unlock a users account and reset the users PIN. This script should be extremely useful for everyone; I don’t know how many times I’ve been asked to provide something similiar. I don’t want to completely own (support) this script once it gets into your hands and I take no responsibility for any damage this might cause in your environment..but I could help streamline the “UX” if you were to reach out to me with very specific requests. The code I am writing for you in this post is FREE and will be available (eventually) on github with a bit of documentation..and I encourage your own forks since I didn’t mind doing my own..

This script is written in Python; it would probably look equally good written in Perl, Ruby, et al (perhaps not a bulky C#/Java)..so I encourage porting the code to the Language of your choice..Also, alot of you all reading this blog may be using a Windows PC; if you haven’t already done so, you will have to install python (and you also should update the Environment Variable on the system so you can type python at a command prompt without being in the directory where python.exe is located); you will also need the requests and lxml module in order to run this code; the easiest way to get those modules is to install easy_install which makes it that much more easier to install them (as its name implies of course); the link to get easy install: (http://simpledeveloper.com/how-to-install-easy_install/). Once you have easy_install (you can add this to that Environment Variable as well) you can type easy_install lxml | easy_install requests at the command line to install the appropriate modules needed for the code I will leave you with. For mac users..you’ll already have Python and easy_install installed..type the appropriate commands above to install the necessary packages to run the code…Good luck getting everything up and running (if you decide to actually use this script).

Lets look at the main() function of our program:

def main():
    global ip,user,pwd,userid
    #ip = raw_input('Enter the IP of your CUC Server. $')
    ip = '10.10.1.50'
    user = raw_input('Please enter your Admin ID. $')
    pwd = getpass('Please enter your Password. $')
    userid = raw_input('Enter the Users ID. $')
    pin = raw_input('Please enter PIN to Use. $')
    status_codes = resetpin(pin)
    if status_codes == 401:
        print 'Bad Username or Password. Please Reenter.'
        user = raw_input('Please enter your Admin ID. $')
        pwd = getpass('Please enter your Password. $')
        status_codes = resetpin(pin)
    if status_codes == 500:
        userid = raw_input('Enter the Users ID. $')
        status_codes = resetpin(pin)
    while True:
        userid = raw_input('Enter the Users ID. To quit enter q. >')
        if userid is 'q':
            break
        status_codes = resetpin(pin)
    print 'Thank you for using me! I hope it saved you precious time.'

There’s nothing great or too terribly unique here..you could hardcode the IP Address (I am here), UserName, Password for your Unity Connection Credentials..you may want to be careful exposing a password in a “text” file; UserId and Pin represent the user you are working on. After collecting information about what user account we will be reseting..the “main” function that performs the real work is called: int = resetpin(pin). Let’s look at the first couple of lines now.

def resetpin(pin):
    headers = {'Content-type':'application/xml'}
    url = 'https://%s/vmrest/users?query=(alias is %s)' % (ip, userid)
    r1 = requests.get(url,verify=False,auth=(user,pwd))
    if r1.status_code == 401:
        return r1.status_code
    userdoc = etree.XML(r1.content)
    find = etree.XPath("//User[Alias='%s']/URI" % userid)
    try:
        objid_uri = find(userdoc)[0].text
    except IndexError:
        print 'That User Doesn\'t Exist. Please Try Again.'
        return 500

I’m thinking about breaking the resetpin(pin) function into into 3 separate functions because there are 3 big sections; above is the first section. Let’s look at the URL. When dealing with the User API within the overall API the normal call to the method is: /vmrest/<function>; one cool feature of these REST calls is the fact that you can perform a query using passed in values in order to get back the information on the users account needed in order to use the methods that unlock the users account and reset the users PIN: objectid. If the GET requests returns 401 then the userid/password we entered was incorrect (if you look back at main() you might see me checking for a 401 and if one is found the script reprompts you to enter in new information). If everything goes as planned then an XML/JSON Object is returned; an excerpt of this return is captured below:

<Users total="1">
 <User>
     <URI>/vmrest/users/2401c165-f2d1-4376-bdf1-d87ebadaa1ad</URI>
     <ObjectId>2401c165-f2d1-4376-bdf1-d87ebadaa1ad</ObjectId>
     <FirstName>Sam</FirstName>
     <LastName>Womack</LastName>
     <Alias>samwomack</Alias>
     <DisplayName>Sam Womack</DisplayName>
     <!--The rest has been removed for brevity-->
 </User>
</Users>

In order for us to get to Credential information for the users account we need to extract the ObjectId from the returned XML above..in particular from the code excerpt we are actually extracting the Text Node from the URI Tag(Element):

"//User[Alias='samwomack']/URI"

If the ObjectId doesn’t exist in the return data, then we know that the user we entered doesn’t exist; when this occurs in the TRY/EXCEPT “block” we return to the main() function to reprompt for userid. If everything goes well and we’ve captured the users ObjectId URI we can perform the next section of code which retrieves the Credential data for the particular users which indicates the Account Status for the user; below is that code and then the XML Data that is returned.

url = 'https://%s%s/credential/pin' % (ip, objid_uri)
#Check to See if Account is Locked
r2 = requests.get(url,verify=False,auth=(user,pwd))
usercred_doc = etree.XML(r2.content)
ishacked = etree.XPath("//Hacked/text()")
ishacked = ishacked(usercred_doc)[0]
if 'true' in ishacked:
    hackcount = etree.XPath("//HackCount/text()")
    hackcount = hackcount(usercred_doc)[0]
    print 'The user has %s failed login attempts.' % hackcount
    hack_doc = etree.Element('Credential')
    hack_e = etree.SubElement(hack_doc,'HackCount')
    hack_e.text = '0'
    time_e = etree.SubElement(hack_doc,'TimeHacked')
    time_e.text = ''
    hackstr = etree.tostring(hack_doc, pretty_print=True)
    print 'Unlocking the Account. Please Wait.'
    r3 = requests.put(url,headers=headers,data=hackstr,verify=False,auth=(user,pwd))
    if r3.status_code == 204:
        print 'The Account has been Unlocked Successfully.'
        print 'Now we need to Change the PIN.'
    else:
        print 'Something went wrong with your request.'
        return 1

And now what the return XML looks like from r2:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Credential>
    <URI>/vmrest/users/2401c165-f2d1-4376-bdf1-d87ebadaa1ad/credential/pin</URI>
    <UserObjectId>2401c165-f2d1-4376-bdf1-d87ebadaa1ad</UserObjectId>
    <CredentialType>4</CredentialType>
    <Credentials/>
    <IsPrimary>false</IsPrimary>
    <CantChange>false</CantChange>
    <DoesntExpire>true</DoesntExpire>
    <TimeChanged>2014-04-01 19:56:35.107</TimeChanged>
    <HackCount>7</HackCount>
    <Locked>false</Locked>
    <TimeLastHack>2014-04-01 22:00:41.279</TimeLastHack>
    <Alias>samwomack</Alias>
    <CredMustChange>false</CredMustChange>
    <CredentialPolicyObjectId>bd9808d7-71ea-4721-bcf2-7adc44d3058e</CredentialPolicyObjectId>
    <TimeHacked>2014-04-01 22:00:41.277</TimeHacked>
    <Hacked>true</Hacked>
    <ObjectId>bc461736-71c2-4708-8c03-ba18553ed17a</ObjectId>
    <EncryptionType>3</EncryptionType>
</Credential>

Before we reset the users PIN, we first need to verify the Account (which is typically <Hacked>true</Hacked>). When we find that the account is “Hacked” (the if statement), we then print how many failed login attempts..this is ‘window dressing’..so isn’t essential. If the Users account isn’t locked, then we can proceed on to changing the users PIN; we could disregard request2 and “unlock” the account regardless of checks..try it at your own risk. So, if the account is hacked we will construct our first REST PUT request (liken to a SOAP REQUEST as an XML document is constructed in this process). In lines 11-15 a new XML document is created. In previous posts, I’ve used the DOM Standard for building XML Documents; today I introduce you to a more streamlined approach to creating the very same document (saving anywhere between 30-50% lines of code that don’t have to written). Below is what the document looks like:

<Credential>
  <HackCount>0</HackCount>
  <TimeHacked></TimeHacked>
</Credential>

Request 3 is sent with the document in tow..as it was in my previous post (written precisely the same)..and don’t forget the headers which is alway in dictionary (data-type) form. Really fast, I’m going to show a nice screencapture of what you can use to see the “code” working from a graphical standpoint:

UnlockAcct

As you can see with the PUT call, the returned status_code is 204 so the if statement in line 19 result is True and you will get back a printed message stating that the ‘Account was Unlocked.’ Let’s move on and finish this thing already and as it stands there are only 7 or so lines of code left..where we Send the PIN in a Prepared XML Document (in my next REST demo..I may use a JSON object..but I’m so accustomed to using XML..it just feels more natural to me).

cred_doc = etree.Element('Credential')
cred_e = etree.SubElement(cred_doc,'Credentials')
cred_e.text = pin
credstr = etree.tostring(cred_doc, pretty_print=True)
r4 = requests.put(url,headers=headers,data=credstr,verify=False,auth=(user,pwd))
if r4.status_code == 204:
    print 'You have successfully changed the PIN.'
return r4.status_code

And the XML that is sent:

<Credential>
    <Credentials>PIN</Credentials>
</Credential>

After the PIN is reset, you are Returned to the “infinite” while loop where you are prompted with a statement to enter the next userid or enter q to quit and if the user’s id is q then we have problems :). And as the code is written, if you do reset another User, you will reset that user with the same original PIN you entered. If you want to Force the caller to Change their PIN once going back in, we could do that with another REST call for Update PIN Settings, but for the sake of brevity, I will leave that off this post. If you want me to Add it as an Addendum be the First to Leave a Comment..As I haven’t received nary a one since the inception of this..As promised I leave you with the script files: ucxn_scriptfiles. If you download the files, get python running on your machine with the modules installed mentioned eariler in this post, below is an output of how to run the code: the *.py file is the only file you need..put it in a Directory of your choosing..open a Terminal/CMD Prompt, navigate to that directory, enter the command as the caption below suggests (FYI, I did make an adjustment to the code, to force the user to change their PIN even after the admin gives them a new PIN..I would imagine this is customary)

python ucxn_accountreset_v01.py
#The Rest will take care of itself...

AppInAction

FYI, there is an even easier approach to this problem in the API..especially if you use LDAP accounts and passwords for mailbox users and clean up the PIN change “url”:

PUT /vmrest/user/credential/pin?newpin=<PIN>
{
    ...
    return sam;
}