I do exactly this. There is nothing in the box of GenieACS to do this. You can use presets and provision scripts to accomplish this goal.
So first thing is some how tag your CPEs with their location. All of our CPEs have two interfaces, a private management interface that runs in the 172.x ip space, and the public PPPoE interface. Each of the cities we serve has a different /20 assigned to it. I created a VirtualParameter called Location. In this script, I match the first two octets of the IP to a location.
Next I created two presets. One called Firmware_Upgrade_Scheduled and the other Firmware_Upgrade_Boot. The scheduled provision script has its schedule set to run for two hours (7200 seconds) starting at 2 am, and only if the CPE doesn’t send a BOOTSTRAP event during this time window (-0 BOOTSTRAP). This preset calls the FirmwareUpgrade provision script passing the argument of “Scheduled”.
The boot preset is the same, except the events is set to “1 BOOT” and it passes the argument of “Boot” to the provision script.
Next, I created a provision script called FirmwareUpgrade. In this script, I create a mapping of CPE models to firmware files and also allow a custom override to determine if an upgrade is required. This can be useful so you don’t have to write a ton of if/else. Each entry can have its own function to determine if the file needs to be sent to the CPE. Example:
let firmware = [
{ model: 'SR360n', version: '2.6.2.4', filename: 'CA_PBCA_2.6.2.4_d553117_SR360n_cfe_fs_kernel', type: '1 Firmware Upgrade Image' },
{ model: 'SR510N', version: '2.6.1.9', filename: 'CA_PBCA_2.6.1.9_093a42c_SR510n_cfe_fs_kernel', type: '1 Firmware Upgrade Image' },
{ model: 'SR510N', version: '2.6.2.0', filename: 'CompanyName_SR510n_vdsl.conf', type: '3 Vendor Configuration File',
comparator: function () {
let configOrigin = getParameterValue('InternetGatewayDevice.DeviceInfo.X_CLEARACCESS_COM_Origin');
let username = getParameterValue('InternetGatewayDevice.ManagementServer.Username');
let result = configOrigin !== 'Our Company Name v2' && username !== 'SmartRG';
info('510 Comparator', {configOrigin: configOrigin, acsUsername: username, upgrading: result});
return result;
}
},
};
The first entry for SR360n has no special provisions, it will only be applied if the firmware version is < 2.6.2.4, the same with the first entry SR510N.
The second entry for SR510N is where the fun is. In my code, I check if there is a comparator function, and if use the results from that function to determine if the firmware/file should be sent to the CPE.
So the first thing I do is gather all the files that apply to the CPE model in question, then I filter through those to find only the applicable ones for this CPE firmware version. The reason for doing this in two steps is to keep the code simpler. Not all CPE vendors use semantic versioning, some, like Zyxel use a firmware version string that looks like someone barfed on their keyboard, and I needed a way compare those too, so in my function that gets the firmware for the model it has all the knowledge of how to understand the various vendors versioning strategy
The function that further filters the files is where the comparator function is executed.
The last bit of code checks if there are any results, and applies the first entry. Reason being, you could have a situation where a CPE needs to be upgraded from v1.0.0.99 to v2.6.2.1 before going to v2.6.2.4 (had this happen).
let applicableFiles = filesForModel(firmware, model);
if (applicableFiles.length === 0) {
log('No firmware mapping for ' + model);
return;
}
let upgradeFiles = filesForModelVersion(applicableFiles, currentVersion);
if (upgradeFiles.length === 0) {
log('No newer firmware/config for ' + model + '. CPE is at version: ' + currentVersion);
return;
}
//Only do the first entry since for some CPEs we want to upgrade firmware/config in a specific order
let file = upgradeFiles[0];
log('Upgrading ' + file.type, {version: file.version, filename: file.filename, type: file.type});
let basePath = "Downloads.[FileType:" + file.type + "]";
declare(basePath, {path: 1}, {path: 1});
declare(basePath + ".FileName", {value: 1}, {value: file.filename});
declare(basePath + ".Download", {value: 1}, {value: now});