1.2+ Config export, backup and rollback

Is there a way to rollback the config that is entered via the Admin | Config page to a known good config, or at least the previous config?

Surely there should be a way to checkpoint the config and perform a rollback ?

At the very least how about providing a way to export the config to a text file as a form of backup that can then be imported to restore the config to its previous state?

There isnā€™t a built-in export/import function but the API exposes everything you need to do this. This is what I wrote to manage my configs from dev to production with git being used for version control. Just be careful if youā€™re using this to sync systems. If youā€™ve deleted a parameter it will still exist on the destination server and needs to be manually removed.

#!/bin/python
# export/import genieACS 1.2 configuration files using the UI API
# PGunter 20200122

import requests, json, os, getopt, sys, re

# Globals
apiURL = "http://localhost:3000/"
baseDIR = "/opt/genieacs/local/configs"

# List of configuration sections to operate on
configs = ["config","permissions","presets","provisions","users","variables","virtualParameters"]


def exportConfigs(webSession):
   for config in configs:
      # Create directories to save configuration files if they don't exist
      path = baseDIR + "/" + config
      if not os.path.exists(path):
         os.makedirs(path)

      URL = apiURL + "api/" + config + "/"
      # Download complete config
      request = webSession.get(URL)
      data = json.loads(request.content)
      for item in data:
         # Save each config item into a seperate file
         name = item["_id"]
         file = path + "/" + name + ".json"
         # JSON dump to disk
         with open(file, 'w') as json_file:
            json.dump(item, json_file)


def importConfigs(webSession):
   for config in configs:
      path = baseDIR + "/" + config
      for filename in os.listdir(path):
         # Walk the configuration directories looking for JSON files
         if filename.endswith(".json"):
            file = path + "/" + filename
            with open(file, "r") as json_file:
               conf = json.load(json_file)
            # Extract _id from the JSON file
            id = conf['_id']
            # Remove _id from the JSON object. Required by GenieACS API
            del conf['_id']
            # Build URL e.g. http://localhost:3000/api/presets/inform
            URL = apiURL + "api/" + config + "/" + id
            # Upload JSON file
            request = webSession.put(URL, json = conf)
            if str(request.status_code) != "200":
               # Display error if we don't receive a 200 OK response
               print str(request.status_code) + ": Failed to upload " + file

def usage():
   print "usage: configMgr [--password <password>] --import | --export"
   print "Options:"
   print "-e, --export      Save configruation files to /opt/genieacs/local/configs"
   print "-i, --import      Load configruation files from /opt/genieacs/local/configs"
   print "-p, --password    admin password for the GenieACS UI"
   print   

def main():
   # variables
   if len(sys.argv) <= 1:
      usage()
      sys.exit(1)
   try:
      opts, args = getopt.getopt(sys.argv[1:], "heip:", ["help", "export", "import", "password="])
   except getopt.GetoptError as err:
      # print help information and exit:
      print str(err)
      usage()
      sys.exit(2)
   apiPassword = "admin"
   apiAction = ""
   for o, a in opts:
      if o in ("-h", "--help"):
         usage()
         sys.exit()
      if o in ("-p", "--password"):
         apiPassword = a
      if o in ("-e", "--export"):
         apiAction = "export"
      if o in ("-i", "--import"):
         apiAction = "import"

   # Check which action to run
   if apiAction in ["export", "import"]:
      # Login to GenieACS UI and save session
      apiSession = requests.Session()
      URL = apiURL + "login"
      request = apiSession.post(URL, json={"username": "admin", "password": apiPassword})
      # Check request successful
      if str(request.status_code) != "200":
         # Display error if we don't receive a 200 OK response
         print ("Unable to login to GenieACS. Error %d: %s"% (request.status_code, request.text))
         sys.exit(2)

      # Which action
      if apiAction == "export":
         print "Exporting GenieACS configuration"
         exportConfigs(apiSession)
      elif apiAction == "import":
         print "Importing GenieACS configuration"
         importConfigs(apiSession)

   else:
        print "Unknown action"
        usage()

if __name__ == "__main__":
   main()
1 Like

Thank you, that is very helpful, I will give it a try.

Is this portable across python 2.7 and 3.x ?

Iā€™m using RHEL7 boxes for GenieACS so Iā€™ve only been using this with python 2.7.
That said Iā€™ve just tested against python 3.6 and the only issue that I found was the print statements needed to be put in brackets
eg
< print(str(request.status_code) + ": Failed to upload " + file)
ā€”
> print str(request.status_code) + ": Failed to upload " + file

Thank you.

My current test platform has python 2.7.

I get the following error.

Are you sure the GenieACS UI is running? Are you running the script on the GenieACS server? Look at the last line on your screen grab. Connection refusedā€¦

Thank you.

Yes, the UI is running. The problem seems to be related to the container plumbing.

I made this change to the script:

##apiURL = ā€œhttp://localhost:3000/ā€
apiURL = ā€œhttp://192.16.90.160:3000/ā€

Now I donā€™t get the connection error, but it fails with ā€œ[Errno 110] Connection timeoutā€ instead of ā€œ[Errno 111] Connection refusedā€.

I am seeking help from the docker experts in my team.

OK, several problems sorted out to get the script running (see below) but now the usage is not quite as expected.

The ā€˜importā€™ action effectively merges the config with whatever is already present instead of replacing it.
Is this your intention?

In my case I am aiming for the ā€˜importā€™ action to completely replace the config on the target system.

Fixes for my context:

  1. Helps to use the correct IP address, I had a typo in the hard-coded address

  2. The list of Mongo tables included ā€˜variablesā€™ and this would crash with a permissions problem, removing it solved the problem.

  3. Hard-coded directory path required sudo permissions to create. Created and modified the permissions from comamnd-line with sudo.

With these changes the script runs. I am uncertain of the implications of not including the ā€˜variablesā€™ table. Any thoughts?

I think the best way to backup configurations and scripts is with Mongodump utility. For example:
mongodump --db=genieacs --collection=config --gzip
backups all your config. It can be easily restored with:
mongorestore --drop --gzip /root/dump/genieacs/config.bson.gz

This backup can be done also for presets, provisions, users, virtualParameters

mongodump restore passwords too?

Which passwords? The passwords created for users of GenieACS? Yes, those are included in the backup. Plaintext passwords for users are not stored in GenieACS, they are hashed.

1 Like

I mean passwords that were setted in any parameter, like SSID, PPPoE, etcā€¦

If I did a mongodump and after restore, the passwords will be there? It works?

Yes