03-12-2016 03:14 AM - edited 03-21-2019 08:53 AM
Hi - I spent an entire day trying to get automate the zero touch provisioning of SPA504G phones from the lowest possible F/W 7.3.7 up to the current 7.6.1.
So I am a bit stuck, I need to ensure I can get phones from the lowest possible F/W revision up to 7.6.1.
I know I have to get them to at least 7.5.2b before 7.6.1.
I want the spa504G.cfg file to come via a http server, not TFTP, and I then want to replace the profile_rule with a https:// URL to the proper config file.
The initial spa504G.cfg file needs to get the phone up to F/W 7.5.2b and then I can do the 7.6.1 F/W upgrade via the final xml profile_rule URL.
I found that in the spa504G.cfg file I could put the downgrade revision limit to 7.6.1 ad then the would prevent a 7.6.1 phone being downgraded to 7.5.2b after a factory reset.
I also thought that if the DHCP 66 option could not set the path as well as the IP to the initial .cfg file I may need to set up a virtual host for the URL name and just put it in the root of the virtual server.
Any help would be appreciated but the requirement is simple to get an SPA504G witg F/W 7.3.7 up to 7.6.1 with my current XML config on it.
On a side note, the 7.6.1 SPC tool would not run under Linux - kept giving a 'segmentation fault'.
Solved! Go to Solution.
03-13-2016 07:53 AM
Well. I'm using User-Agent header to identify current version. It have one of two form:
There's model, firmware version, MAC address and serial number. Lets fetch the version number into $_GET["SW"] variable:
$hdr=apache_request_headers(); if (preg_match('/^Linksys\/SPA50.G-/',$hdr["User-Agent"])>0) {
$_GET["SW"]=strstr(substr($hdr["User-Agent"],16), " ", true);
} else if (preg_match('/^Cisco\/SPA50.G-/',$hdr["User-Agent"])>0) {
$_GET["SW"]=strstr(substr($hdr["User-Agent"],14), " ", true);
}
Now we know the current version. Now the upgrade logics. Note we are on 7.5.5 now. We are testing 7.6.1. The following fragment *does not* upgrade if phone's current firmware is 7.5.5 or 7.6.1 - otherwise it upgrade to 7.5.5. Remember the current version is in $_GET["SW"] variable.
if ( $_GET["SW"] != "" && $_GET["SW"] != "7.5.5" && $_GET["SW"] != "7.6.1" ) {
$upgrade = true;
// upgrade from FW < 7.5.2b to FW > 7.5.2b require upgrade to 7.5.2b !!
if (cmp_ver($_GET["SW"], ">=", "7.5.2b")) {
$upgrade_rule = 'https://$A/Cisco/spa50x-30x-7-5-5.bin?SW='.$_GET["SW"];
} else {
$upgrade_rule = 'https://$(A)/Cisco/spa50x-30x-7-5-2b.bin?SW='.$_GET["SW"];
}
};
Most of work is done. Now you need just to emit appropriate Upgrade_Rule content according the analysis results:
if ($upgrade) {
echo "<Upgrade_Enable>Yes</Upgrade_Enable>"; } else { echo "<Upgrade_Enable>No</Upgrade_Enable>"; } echo "<Upgrade_Rule>$upgrade_rule</Upgrade_Rule>";
Note the cmp_ver() function used above is my implementation of version comparsion:
function parse_sw_version($v) {
$str = preg_replace("/^([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)((\([0-9]*\)(_[0-9]*){0,1}){0,1})$/i", "\$1:\$2:\$3:\$4:\$5:\$6:\$7", $v);
$ar = explode(':', $str);
return($ar);
}
function cmp_ver($v1,$op,$v2) {
$d = "";
if ($v1 == "*" && $v2 == "*") {
$d = 0;
} else if ($v1 == "*" && $v2 != "*") {
$d = -1;
} else if ($v1 != "*" && $v2 == "*") {
$d = -1;
} else if ($v1 != "*" && $v2 != "*") {
$ar1 = parse_sw_version($v1);
$ar2 = parse_sw_version($v2);
while(true) {
if ($ar1[0]!=$ar2[0]) { $d=($ar1[0]-$ar2[0]) ; break; }
if ($ar1[1]!=$ar2[1]) { $d=strcmp($ar1[1],$ar2[2]) ; break; }
if ($ar1[2]!=$ar2[2]) { $d=($ar1[2]-$ar2[2]) ; break; }
if ($ar1[3]!=$ar2[3]) { $d=strcmp($ar1[3],$ar2[3]) ; break; }
if ($ar1[4]!=$ar2[4]) { $d=($ar1[4]-$ar2[4]) ; break; }
if ($ar1[5]!=$ar2[5]) { $d=strcmp($ar1[5],$ar2[5]) ; break; }
if ($ar1[6]!=$ar2[6]) { $d=($ar1[6]-$ar2[6]) ; break; }
$d=0 ; break;
}
}
if ($d < 0 ) {
$d=-1;
} else if ($d > 0) {
$d=1;
}
switch($op) {
case '==': return($d == 0);
case '!=': return($d != 0);
case '>=': return($d >= 0);
case '<=': return($d <= 0);
case '<': return($d < 0);
case '>': return($d > 0);
case '': return($d);
default: return(false);
}
}
Hope it help. Ask in doubts. Rate if it help you.
03-12-2016 05:24 AM
I have again watched Patrick's youtube video (https://www.youtube.com/watch?v=76IMzvfpsx8) and looked at my process again:
SPA504G phone on, say, 7.3.7 (lowest possible F/W).
So my question is - if the 7.3.7 F/W cannot evaluate the upgrade syntax, how can it ever be zero touch provisioned ?
Thanks guys.
03-13-2016 04:40 AM
Well, we use zero touch deploying for more than three years (not only for SPA50x but for SPA30x, SPA51x, SPA1xx, SPA2xx, SPA9xx and even PAP2T as well). We have networks deployed in seven countries around. So you may consider simplified description of our configuration valuable. The goal is - devices unknown to us MUST NOT be provisioned (e.g. no name/password leak to unauthorized device). If someone fired reset to factory default, the device should recover to operational state with no mans intervention required. Deployment of new phone must not require personal presence of a IT specialist. So we:
Hope it help (rate to lets me know ;-) ).
03-13-2016 04:40 AM
So firmware 7.5.2b is an actual firmware revision, and yes, it is alphanumeric so it will not ever be able to be evaluated? This firmware happens to be the firmware I have to upgrade to first in order to get up to 7.6.1. So without the PHP method you use there is simply no way to zero touch provision a phone (remember my goal is to be able to provision any SPA phone regardless of how old the F/W is) using Cisco tools ? Are you able to share any more information as to how you are using PHP to determine logic for provisioning and how, from server side, you are influencing firmware revision etc ?
03-13-2016 06:51 AM
7.52b is non numeric, e.g. only operators suitable for string can ben used - so you can evaluate them to be either equal or non equal to other string, no less/greater than possible
So without the PHP method you use there is simply no way
You may use $SWVER as part of Upgrade_rule string. So the phone will download firmware file according current firmware version. So you may need no scripting at all.
Also, PHP may be so complex tool for simple task like this one. Even shell script will be enough. We use PHP because "it's here" - we use it for more complex tasks related to phone configuration as well as remote phone dictionary.
Are you able to share any more information as to how you are using PHP to determine logic for provisioning and how, from server side, you are influencing firmware revision etc ?
Yes. but be patient, I will paste relevant fragments of code later (but today)..
03-13-2016 07:53 AM
Well. I'm using User-Agent header to identify current version. It have one of two form:
There's model, firmware version, MAC address and serial number. Lets fetch the version number into $_GET["SW"] variable:
$hdr=apache_request_headers(); if (preg_match('/^Linksys\/SPA50.G-/',$hdr["User-Agent"])>0) {
$_GET["SW"]=strstr(substr($hdr["User-Agent"],16), " ", true);
} else if (preg_match('/^Cisco\/SPA50.G-/',$hdr["User-Agent"])>0) {
$_GET["SW"]=strstr(substr($hdr["User-Agent"],14), " ", true);
}
Now we know the current version. Now the upgrade logics. Note we are on 7.5.5 now. We are testing 7.6.1. The following fragment *does not* upgrade if phone's current firmware is 7.5.5 or 7.6.1 - otherwise it upgrade to 7.5.5. Remember the current version is in $_GET["SW"] variable.
if ( $_GET["SW"] != "" && $_GET["SW"] != "7.5.5" && $_GET["SW"] != "7.6.1" ) {
$upgrade = true;
// upgrade from FW < 7.5.2b to FW > 7.5.2b require upgrade to 7.5.2b !!
if (cmp_ver($_GET["SW"], ">=", "7.5.2b")) {
$upgrade_rule = 'https://$A/Cisco/spa50x-30x-7-5-5.bin?SW='.$_GET["SW"];
} else {
$upgrade_rule = 'https://$(A)/Cisco/spa50x-30x-7-5-2b.bin?SW='.$_GET["SW"];
}
};
Most of work is done. Now you need just to emit appropriate Upgrade_Rule content according the analysis results:
if ($upgrade) {
echo "<Upgrade_Enable>Yes</Upgrade_Enable>"; } else { echo "<Upgrade_Enable>No</Upgrade_Enable>"; } echo "<Upgrade_Rule>$upgrade_rule</Upgrade_Rule>";
Note the cmp_ver() function used above is my implementation of version comparsion:
function parse_sw_version($v) {
$str = preg_replace("/^([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)\.([0-9]+)([a-z]*)((\([0-9]*\)(_[0-9]*){0,1}){0,1})$/i", "\$1:\$2:\$3:\$4:\$5:\$6:\$7", $v);
$ar = explode(':', $str);
return($ar);
}
function cmp_ver($v1,$op,$v2) {
$d = "";
if ($v1 == "*" && $v2 == "*") {
$d = 0;
} else if ($v1 == "*" && $v2 != "*") {
$d = -1;
} else if ($v1 != "*" && $v2 == "*") {
$d = -1;
} else if ($v1 != "*" && $v2 != "*") {
$ar1 = parse_sw_version($v1);
$ar2 = parse_sw_version($v2);
while(true) {
if ($ar1[0]!=$ar2[0]) { $d=($ar1[0]-$ar2[0]) ; break; }
if ($ar1[1]!=$ar2[1]) { $d=strcmp($ar1[1],$ar2[2]) ; break; }
if ($ar1[2]!=$ar2[2]) { $d=($ar1[2]-$ar2[2]) ; break; }
if ($ar1[3]!=$ar2[3]) { $d=strcmp($ar1[3],$ar2[3]) ; break; }
if ($ar1[4]!=$ar2[4]) { $d=($ar1[4]-$ar2[4]) ; break; }
if ($ar1[5]!=$ar2[5]) { $d=strcmp($ar1[5],$ar2[5]) ; break; }
if ($ar1[6]!=$ar2[6]) { $d=($ar1[6]-$ar2[6]) ; break; }
$d=0 ; break;
}
}
if ($d < 0 ) {
$d=-1;
} else if ($d > 0) {
$d=1;
}
switch($op) {
case '==': return($d == 0);
case '!=': return($d != 0);
case '>=': return($d >= 0);
case '<=': return($d <= 0);
case '<': return($d < 0);
case '>': return($d > 0);
case '': return($d);
default: return(false);
}
}
Hope it help. Ask in doubts. Rate if it help you.
03-14-2016 12:13 AM
Thanks a lot for your help.
Your PHP code, when in the boot process are you looking at the HTTP headers ? And once your code has evaluated, based on the header values, is it writing the following (upgrade disabled in this case):
"<Upgrade_Enable>No</Upgrade_Enable>
to an spa504G.cfg file or is it writing individual spa$MA.xml files ?
I will digest this and see if I can come up with a solution.
03-14-2016 12:54 AM
For the purpose of firmware upgrade I don't distinguish 'initial provisioning' from 'routine provisioning'.
Our DHCP daemon delivers https://..../Cisco/Provisioning.php via option 160 to the virgin phone. Provisioned phone have the same URL configured in <Profile_Rule...>
Thus phones, either virgin or provisioned already, regardless the firmware version, use this URL only to call for configuration. Yes, HTTP headers are used to identify current firmware version in all cases.
Note the default spa508G.cfg file is never asked by phone connected to our network. Because of DHCP, even virgin phone asks https://..../Cisco/Provisioning.php
is it writing the following ... to an spa504G.cfg file or is it writing individual spa$MA.xml files
No, you missed the idea. I generate no files. The phone asks configuration from .../Cisco/Provisioning.php and such script is sending configuration to them on the fly.
We use no separate site-wide and individual configuration. The Provisioning.php create complete configuration at once. We consider it's faster, so phone can recover quickly.
But of you wish, you can create mixed environment. DHCP and Profile_Rule may call PHP script. Such script will handle firmware upgrade logic and will set Profile_Rule_B and Profile_Rule_C. The Profile_Rule_B will be URL to static file with site-wide configuration while Profile_Rule_C will be static fiel with individual configuration.
But I consider it suboptimal. Even if you have configuration in static file for a reason,. it's faster to order PHP to include it's content to the datas sent - thus phone become provisioned in one step.
Just note - someone may ask why we emit <Upgrade_Enable>No</Upgrade_Enable> if the firmware is the same. It is bandwidth-saving decision. With upgrade disabled the phone will not try to load firmware file to identify the firmware version. It's not necessary - we wish to have full sovereignty to drives upgrade, so we will order phone to upgrade once right time comes.
Just another note - if you wish to distinguish initial provisioning from routine provisioning, set GPP_P variable to something and use it in Profile_Rule (e.g. https://..../Cisco/Provisioning.php?GPP_P=$P). You can test the variable content in the code. If empty - it's initial provisioning. If set to string known to you, you has configured such phone previously.
03-14-2016 12:55 AM
Great, thanks. That makes it all clear now ! I will get started on a PHP script to generate some test config values.
03-14-2016 06:02 AM
@danlukes - With your help I have got provisioning via PHP working. I assume you are pulling in phone config data via SQL query to a DB holding all your phone/client info.
This has been a great start - thank you very much for your time and input here.
03-14-2016 06:25 AM
Glad to hear.
Yes, we have own small simple information system that allow us to configure our phones and client. The database is used in Provisioning.php and it's used to generate text configuration files for our Asterisk as well.
Note that our Provisioning.php supports not only SPA50xG but also other SPA* IP Phones and gateways. There are some trick necessary to support PAP2T (as size of resulting configuration must not exceed 64kB, moreover the embedded client certificate has expired already), there are some firmware bugs related to use of some parameters (for example in Report_Rule), but generally - you can extend the code you have to flexible tool.
Don't bother to ask even in the future.
03-15-2016 04:43 PM
Hi Dan - I wrote a python based provisioning script (python is easier for string manipulations) based on your code. I can now get a 7.3.7 SPA504G phone up to 7.6.1 and all config applied that is stored in an SQLite DB all with SSL and client auth to the webserver.
This is exactly what I needed ! Thanks again.
See http access log below (it starts iff with me doing a manual downgrade to 7.3.7 to test the process)....
192.168.10.97 - - [16/Mar/2016:01:14:45 +1000] "GET /test/spa5x5-7-3-7.bin HTTP/1.0" 200 3471464 "-" "Cisco/SPA504G-7.6.1 (58BFEA1104E6)(CCQ16380FYH)"
192.168.10.97 - - [16/Mar/2016:01:21:02 +1000] "GET /cisco1.py HTTP/1.0" 200 3600 "-" "Linksys/SPA504G-7.3.7 (58BFEA1104E6)(CCQ16380FYH)"
192.168.10.97 - - [16/Mar/2016:01:22:38 +1000] "GET /spa50x-30x-7-5-2b.bin HTTP/1.0" 200 4160129 "-" "Linksys/SPA504G-7.3.7 (58BFEA1104E6)(CCQ16380FYH)"
192.168.10.97 - - [16/Mar/2016:01:26:13 +1000] "GET /cisco1.py HTTP/1.1" 200 3722 "-" "Cisco/SPA504G-7.5.2b (58BFEA1104E6)(CCQ16380FYH)"
192.168.10.97 - - [16/Mar/2016:01:26:32 +1000] "GET /spa50x-30x-7-6-1.bin HTTP/1.0" 200 4456891 "-" "Cisco/SPA504G-7.5.2b (58BFEA1104E6)(CCQ16380FYH)"
192.168.10.97 - - [16/Mar/2016:01:30:04 +1000] "GET /cisco1.py HTTP/1.1" 200 6628 "-" "Cisco/SPA504G-7.6.1 (58BFEA1104E6)(CCQ16380FYH)"
03-15-2016 05:15 PM
Glad to hear you got it.
Python is as good as PHP for the purpose of this task. As I mentioned previously -even just plain shell script can handle the provisioning.
Beware of *-XU flavor of firmware. Once installed, they can't be distinguished from non-XU kind of firmwares.
03-15-2016 05:21 PM
I did see the -XU firmware in the list. How are you handling them ? Do you have an example host header ? I might flash one of these firmwares to an SPA504G and take a look.
03-15-2016 05:46 PM
-XU and non-XU headers are same, unfortunately.
I wish the brand new unit have no newest firmware we are using, so it will be upgraded.
I never upgrade to -XU firmware thus I wish all -XU units are reverted to be non-XU
May be it is possible to recognize them in spacfg.xml saved, but I didn't tried yet.
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide