#1 – request

Request is the easiest way to make REST calls. It runs server-side to hide your token and if you use Postman to test the HTTP request, it can even generate the code for you!
https://www.npmjs.com/package/request

Example Request Using Spark Messages API:

var request = require(‘request’);
var req = {
  auth: { bearer: ‘sparkTokenHere’ },
  url: ‘https://api.ciscospark.com/v1/messages’,
  json: true,
  body: {
    ‘roomId’: roomId,
    ‘text’: message
    }
};//end setup

request.post(req, function(err, res) {
if(err) {
console.log(err);
} else {
  }
});//end rest call


#2 – picker

Picker is a server side router for Meteor that works alongside middleware to easily provide an API or webhook into your application.
https://atmospherejs.com/meteorhacks/picker

Example Webhook with JSON parsing using body-parser:

Picker.middleware(bodyParser.json());
Picker.middleware(bodyParser.urlencoded({extended: false}));
Picker.route(‘/myWebhook’, function(params, request, response) {
  personEmail = (request.body.data.personEmail);
  msgid = (request.body.data.id);


#3 – Q

Before getting too far ahead with node.js, you’ll need to grasp the underlying language, javascript. Javascript is a synchronous language, meaning it runs functions all at the same time, which can make a mess of things if you don’t use callbacks properly. Q uses something called promises to run things asynchronously in the order you tell it. To learn more about callbacks and why this package is so necessary, check out callbackhell.com
http://documentup.com/kriskowal/q/

Example of promises with Q:

Q.fcall(promisedStep1)
.then(promisedStep2)
.then(promisedStep3)
.then(promisedStep4)
.then(function (value4) {
// Do something with value4
})
.catch(function (error) {
// Handle any error from all above steps
})
.done();


#4 – bert

Bert makes notifications simple
https://github.com/themeteorchef/bert

Bert.alert({
  title: ‘Conference Now’,
  message: ‘Connecting to Tropo…’,
  type: ‘info’,
  style: ‘growl-bottom-right’,
  icon: ‘fa-phone’
});


#5 – fontawesome

fontawesome is a collection of 675 icons in unicode that can be embedded in your site with a single tag. Most people will use their built in CDN (content delivery network) to make deployment even easier.
http://fontawesome.io/icons/

<i class=fa fa-camera-retro”></i>


#6 – nodemailer

nodemailer is the best email package hands down. It supports an html body and allows you to specify options within the app.
https://www.npmjs.com/package/nodemailer

var nodemailer = require(‘nodemailer’);

// create reusable transporter object using the default SMTP transport
var transporter = nodemailer.createTransport(‘smtps://user%40gmail.com:pass@smtp.gmail.com:587’);

// setup e-mail data with unicode symbols
var mailOptions = {
  from: ‘”Fred Foo 👥” <foo@blurdybloop.com>’, // sender address
  to: ‘bar@blurdybloop.com, baz@blurdybloop.com’, // list of receivers
  subject: ‘Hello ✔’, // Subject line
  text: ‘Hello world 🐴’, // plaintext body
  html: ‘Hello world 🐴‘ // html body
};

// send mail with defined transport object
transporter.sendMail(mailOptions, function(error, info){
  if(error){
  return console.log(error);
  }
console.log(‘Message sent: ‘ + info.response);
});


#7 – validator

Need to check if an email is actually an email? A dollar amount is actually a dollar amount? Validator has a ton of built-in methods to check for you.
https://www.npmjs.com/package/validator

var validator = require(‘validator’);
validator.isEmail(‘foo@bar.com’); //=> true


#8 – csv

While ECMA 5 has native XML and JSON support, a lot of Cisco applications still rely on CSV (comma seperated values). This package aims to bridge the gap.
https://www.npmjs.com/package/csv

var csv = require(‘csv’);
csv.parse(data, function(err, data){
  csv.stringify(data, function(err, data){
  process.stdout.write(data);
  });
});


#9 – bootstrap

Boostrap is so useful, I wish it was just included in HTML period. Any buttons, menus, fonts, tables, grids, layouts, etc. are super easy with Bootstrap.
https://www.npmjs.com/package/bootstrap

Just include the bootstrap code below to utilize their CDN:

<!– Latest compiled and minified CSS –>
<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css” integrity=”sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u” crossorigin=”anonymous”>

<!– Latest compiled and minified JavaScript –>
<script src=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js” integrity=”sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa” crossorigin=”anonymous”></script>


#10 – jsPDF

PDFs are still the industry standard for “digital hard copies” of documents. jsPDF makes generating PDFs easy.
https://parall.ax/products/jspdf

<script src=”https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.2/jspdf.debug.js”></script>
var pdf = new jsPDF();
pdf.text(30, 30, ‘Hello world!’);
pdf.save(‘hello_world.pdf’);

I did a post not too long ago on the vlookup function and how it can be used to extract data by matching on a row and returning data x number of columns away. The same exists using the hlookup command to match on a column and return data x number of rows away.

HLOOKUP(lookup_value, table_array, row_index_num, [range_lookup])

The use case for this is simple: you’re doing a parallel upgrade of Call Manager using the import/export method and you want to copy over all the tables that exist in the old database, but if they don’t exist, you still need the table headers. To do this you’ll need the old phone.csv and a blank export of the new phone.csv side by side.

You’ll paste this on the new phone.csv in A2 and drag to the right and down to populate the database.

=hlookup(a$1,(old-phone!$a$1:$dyt$5000),(row(a2)),false)

When you’re done you should copy all values to a new sheet to clean up any #N/A values it may create and thats your new phone.csv

A lot of apartment buildings have callboxes that will dial a tenant and allow the tenant to open the door by pressing a key sequence. I have had to use this to call myself before when I left my keys upstairs, or that time I lost my keys for a week (but thats another story). So what happens if you don’t have your phone or your keys? Or what about when you have guests that arrive early and you’re still in the shower, getting ready or can’t get to your phone in time?

In this example, the callbox dials Tropo instead of your phone, uses the built-in IVR to ask for a password. If the password is correct, it dials the dtmf digits to open the door and sends a text message to your phone alerting you of company.

var
result = ask(“The password is?”, {
choices: “sausages”
});
if(result.value == “sausages”){
say(“that is correct”);
say(“https://evolution.voxeo.com/library/audio/prompts/dtmf/Dtmf-9.wav”);
message(“A Guest has Arrived”, {
to:”+19192223323″,
network:”SMS”
});
}
else{
say(“there is a nice bench outside for you to sleep on”);
}

I recently posted about the Jabber Problem Report Tool and the csf-unified.log that turned out to be one of my most popular posts. I had some feedback asking for more info on specific troubleshooting after home cluster is found, so here it is.

Service Discovery Failure Codes (csf-unified.log)

[table id=2 /]

Service Discovery Cache File (service-location.xml)

Located in %appdata%\Cisco\Unified Communications\Jabber\CSF\Config

<?xml version=”1.0″?>
<UCServices>
<DomainName>pnslabs.com</DomainName>
<UCService>
<type>CUCM</type>
<connectionInformation>
<name>_cisco-uds</name>
<scope>UNKNOWN</scope>
<address>cucm.pnslabs.com</address>
<protocol>_tcp</protocol>
<port>8443</port>
</connectionInformation>
</UCService>
</UCServices>

IMS Result Codes

Client Profile Agent Logs pulled from IM&P Server EPASSoapXXXXX.log
[table id=3 /]

Anyone who has deployed Jabber knows of the cisco-uds SRV record that Jabber uses (or Expressway-C in the case of MRA) to discover it’s services. It’s also used for directory searches, and home cluster lookup with ILS.

UDS or User Data Services is a simple REST based API for CUCM. While the UDS API is not as extensive as what you can do with AXL through SOAP and WSDL, it makes a great use case for a front end to allow end users to manage their own devices: Change their single number reach destination, password, speed dials, and conference pins.

For security reasons, most browsers will block an XMLHttpRequest served on one page and originating on another (in the case of a front end making UDS calls to CUCM). To get around this, you would use CORS or Cross Origin Resource Sharing, defining the front end URL inside CUCM.

UDS supports REST queries POST/PUT/GET/DELETE in XML format. Some calls do not require authentication and some do. Ones that do will use Basic authentication built into the browser which encodes the username:password in Base64 format.

UDS resources that do not require authentication
clusterUser, installedLocales, models, options, phoneService(s), servers, timezones, users, version

GET https://{host}:8443/cucm-uds/users?last=Smit
GET https://{host}:8443/cucm-uds/clusterUser?username={userId}

UDS resources that require authentication
credentials, device(s), extension(s), remoteDestination(s), speedDial(s), subscribedService(s), user, userPolicy

POST https:­//{host}:8443/cucm-uds/user/{userId}/speedDials
<!–add speedDials example request body–>
<speedDials>
<speedDial>
<index>1</index>
<number>1234567890</number>
<label>Manager</label>
</speedDial>
<speedDial>
<index>2</index>
<number>1234567899</number>
<label>Assistant</label>
</speedDial>
</speedDials>

CUCM UDS API Reference
CUCM UDS Developer Guide
CUCM UDS Authentication Guide
Base64 Encoder/Decoder

You’ve probably heard a lot of buzzwords lately: API, JSON, and REST among them, however truth be told, REST or Representational State Transfer has always been around as the “language of the internet” (a REST HTTP GET brought you this page!), so it goes without saying that in the age of Internet of Things, REST would become infinitely more important.

REST is made up of 4 things

URI: https://api.ciscospark.com/v1/rooms
Request Type: POST, GET, PUT, etc
Header: Authorization (API key), Content-Type (html, application/xml, application/json)
Body: { “function” : “sendMessage”, “message” : “this is a message”}

JSON is the preferred format over XML because its more efficient. With PHP methods json_encode() and json_decode() for instance, it’s very easy to parse arrays into JSON format and vis a visa.

One of the great things about REST is that when a call is made, it sends a response, which can then in turn be a variable in a second response and so on. It’s not client-server, it’s a conversation.

rest-api

Check out my demo’s for Spark API and Tropo API for a full writeup of how to write some simple REST calls using an html form and some PHP.

Cisco has a good writeup on CUC SQL queries, which are very useful to determine which voicemail accounts are active and which are just taking up a license, and perhaps could be skipped over during migrations. To get everything you need, use the query below:

lists all message counts, durations, size, and dates per alias:

run cuc dbquery unitymbxdb1 select alias as UserID, count (*) as TotalMessages, sum(case when deleted=’0′ then 1 else 0 end) as Inbox, sum(case when deleted=’1′ then 1 else 0 end) as Deleted,  min (arrivaltime) as OldestMessageTime, vw_mailbox.bytesize, sum(duration/1000) as TotalDuration_In_sec from vw_message,vw_mailbox, unitydirdb:vw_mailbox, unitydirdb:vw_user where vw_message.mailboxobjectid=vw_mailbox.mailboxobjectid and vw_mailbox.mailboxobjectid in (select mailboxid from vw_mailbox where unitydirdb:vw_user.objectid = unitydirdb:vw_mailbox.userobjectid) group by alias, vw_mailbox.bytesize order by TotalMessages desc

MRA (Mobile and Remote Access) is a technology that allows phone registrations without raw sip signaling sent over the internet. I compare it to RPC over HTTPS proxy, aka “Outlook Anywhere.” Email came first, now I’m not sure if anyone remembers having to VPN in for Outlook to work.

After having several MRA projects back to back, I’ve found that most of the lessons I’ve learned were dealing with project management than the technical pieces. No other piece of hardware or software requires so many different groups of people: the datacenter guy, the firewall guy, the certificate guy, the dns guy, the voice guy. Throw in a business analyst and a project manager and it gets messy. I literally had projects ongoing for months and months when I was able to get MRA working in my lab in just a couple hours. Anytime I finish a project I look at how it could’ve been done better, so here it is:

First: It’s better to think of MRA as 5 separate, independent tasks because almost nothing depends on anything else.

  1. VM Deployment
  2. Networking
  3. Certificates
  4. DNS
  5. Firewall

Second: Clearly define roles and responsibilities per task at the onset of the project. The certificate guy doesn’t care about ip addresses or storage so just gather contact information and give role takers hyper-specific information to keep their attention. Limit emails.

Third: Make the configuration as cookie-cutter as possible. I wouldn’t even mention single NIC with some nat reflexion workaround, you use the advanced networking key with an outside and an inside interface. Public Certs on everything. It’s always a cluster, even if its a cluster of one; you never know when they will scale up. I assume inside out, everything is allowed through the firewall; this is usually the case and customers get confused with the lengthy cisco documentation. I usually give something like below:

Purpose

Protocol

Internet Endpoint (source)

Expressway-E (listening)

XMPP

TCP

>=1024

5222

XMPP federation

TCP

>=1024

5269

HTTP proxy (UDS)

TCP

>=1024

8443

SIP media

UDP

>=1024

36002 to 59999

SIP signaling

TLS

>=1024

5061

HTTPS (Unity)

TCP

>=1024

443

Also, I posted before about Flexible Jabber ID, which allows for multiple presence domains. That can cause holdups if you aren’t allowed to sign for these domains for your cup-xmpp certificate. It becomes an all-or-nothing service model. The best thing to do is get ahead of this and remove services from domains that you cannot sign for! You can run a whois lookup to ensure the technical contact has some relationship with the customer.

For a list of all jabber users and their last login time, you can run the following query:


run sql
select e.userid, cd.timelastaccessed from enduser as e, credentialdynamic as cd, credential as cr where e.pkid=cr.fkenduser and e.tkuserprofile=1 and e.primarynodeid is not null and cr.tkcredential=3 and cr.pkid=cd.fkcredential order by cd.timelastaccessed

This date is in something called “epoch” format. To convert to something that makes sense, you’ll need to use the following formula:

=(((B2/60)/60)/24)+DATE(1970;1;1)+(-5/24)

This is for Eastern Standard Time where -5 = UTC -5, you can change this accordingly.

expresswayc-e

There are a number of deployment options for Expressway depending on your customers environment, which could lead you to having to come up with creative solutions. This is called engineering.

We know external services need to be discovered using “outside” dns, however what happens when a customer uses a single dns server for outside and inside networks? Well, The traversal zone will need to be a FQDN for TLS validation, so it needs to use dns to resolve the inside addresses, but clients on the public internet need to use dns to resolve the outside address of the Expressway E.

expc-mra_-_Edit_zone

Tandberg VCS aka Cisco Expressway runs on linux and uses a lightweight network services package called dnsmasq

Being a linux guy, the first thing I tried was editing the /etc/hosts file, but to prevent hacking, everything is on a read-only filesystem except for the /tandberg mount, which also had an etc but changes were not persistent. After poking around I found the dnsmasq.conf man pages here and an option to specify a “conf-dir” to include configuration files that are loaded on start.

For static A records, create a file called hosts.conf

/tandberg/etc/dnsmasq.conf.d/hosts.conf

address=/expe-mra.car.pnslabs.com/10.20.30.40

For static SRV records, create a file called srv.conf

/tandberg/etc/dnsmasq.conf.d/srv.conf

srv-host=_cisco-uds._tcp.car.pnslabs.com.,cucm.car.pnslabs.com.,8443,100,0

After adding the file, you’ll need to restart dnsmasq:

/etc/init.d/dnsmasq restart