Massive Firmware Upgrade

We are testing firmware upgrade using Genie GUI and also via NBI through tool Postman, and it is working very well.

However, there is a question about massive FW update , which I would like to have your help answering the questions below:

  1. Is there a way to make a massive FW upgrade campaing for an specific CPE device?
  2. Is there a way to upgrade the FW for a CPE located in an specific region, for instance?

The activities listed above are very important for small ISPs.

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.

image

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});
7 Likes

Man, I’d love to dig in to your install of Genie! These script-nuggets you “leak” from time to time are just awesome!
Cheers! :slight_smile:

1 Like

Thank you! I spend a lot of time thinking about how to create the most robust, simplest, easiest to manage solution; while still dealing with the areas where each vendor feels the need to express their unique creativity :smiley:.

I gained a lot of insight many years ago reading Martin Fowlers book Refactoring: Improving the Design of Existing Code. My personal opinion is the first 80 pages or so of that book could stand on their own, without the Java shudder refactoring recipes in the rest of the book.

3 Likes

I am not sure how I understand schedule time looking at your example, maybe v1.2 different format? As wiki says:

Cron expression is in the format of: seconds minute hour day date.

Shouldn’t your settings “7200 0 11 * * *” work like: activate preset for 2hours when time is 0 second, 11 minute, any hour, day, month?

More like number of seconds the expression is good for. So 7200 seconds (2 hours). Start at 11:00 GMT.

So this wiki sample is right or wrong?

For example, a schedule expression of 3600 * * 3 * 1-5 would execute every morning from 3 AM to 4 AM for the months of Jan to May. Time specified is in UTC.

It seems to be logical, but for v1.2 it seems not working like that. So it means first number is seconds for active expression, second is minutes, third is hours? Then days, months and year?

Hi,

For example, a schedule expression of 3600 * * 3 * 1-5 would execute every morning from 3 AM to 4 AM for the months of Jan to May. Time specified is in UTC.

I couldn’t find this statement in the docs, but I think it’s wrong and 3600 * * 3 * 1-5 actually means: execute this preset on the 3’rd day of every month if it’s a workday (monday - friday) so for this example, the next execution window will be: 2020-07-03 00:00 → 2020-07-04 00:59.

For it to express “execute every morning from 3 AM to 4 AM for the months of Jan to May” I think it should be one of these:
3600 0 3 * 1-5 *
60 * 3 * 1-5 *

awsome, I would like to test your code. Is it possible to get a copy of the whole provision script?
I´m not that familiar with javascript but would love to use this to automate updating devices on my instance of genieacs.

I’m sorry, but this is not possible. This code is owned by my employer.