superEngine – FreePBX/NetSapiens Migration Utility

superEngine is an extensible command line utility that performs actions against the NetSapiens API and migrations from FreePBX to NetSapiens. Among its various uses are the ability to perform CRUD operations on any currently implemented NetSapiens object.

Installation & Configuration

To install superEngine, run the following commands on a Linux or Mac operating system with Git, PHP 7.3+, and Composer installed. Installation for Windows machines varies according to the deployment specifics of your PHP and Composer installations and is not recommended.

git clone https://github.com/Ringfree/super-engine.git
cd super-engine
composer install

Upon cloning the repository you will be prompted for your Github credentials as the repository is private. Upon running composer install you’ll be prompted to create/copy an access token as one of the dependencies, the NetSapiens PHP SDK, is also in a private repository.

At this point you’ll need to add configuration parameters so that superEngine knows where you’d prefer to store files and how to communicate with the various services. A default (and almost empty) configuration file called config.ini is available within the super-engine directory. It’s recommended to copy the file to /etc/superEngine.ini as superEngine looks for the file there by default.

Within the configuration file there are four sections. Here’s a brief rundown of everything you need to know:

storage

  • dir – Where superEngine should write/download files used during migration actions.

netsapiens

  • apiUrl – The domain of the NetSapiens instance you’ll be communicating with. This should be limited to the subdomain, second-level domain, and top-level domain.
  • clientId – The OAuth client ID you’ll be using to access the NetSapiens API. OAuth clients are configured within the SiPbx interface.
  • clientSecret – The OAuth client secret/password. This is also configurable within the SiPbx interface.
  • username – The username of the user you’ll use to access the NetSapiens API. For superEngine it is recommended that you create a dedicated API user in the “Super User” scope.
  • password – The password of the user you’ll use to access the NetSapiens API.

portal

  • apiUrl – The domain of the Ringfree Portal instance you’ll be communicating with. This should be limited to the subdomain, second-level domain, and top-level domain.
  • username – The username of the user you’ll use to access the Ringfree Portal. Any user with the api-basic-auth:read permission will do.
  • password – The password of the user you’ll use to access the Ringfree Portal.

freepbx

  • username – The username of the user you’ll use to access FreePBX instances. Note that Ringfree has a standard common username for this that should allow access to all instances.
  • password – The password of the user you’ll use to access FreePBX instances. Note that Ringfree has a standard common password for this that should allow access to all instances.

Basic Application Structure

superEngine is a simply structured application with the only hard dependencies being the NetSapiens PHP SDK, prim/httpfactory, and league/container. When run, the application first initializes the service container, reads the action from the command line arguments, and attempts to perform that action using any other command line arguments specified.

In superEngine terminology, an action is a class located within the src/Action directory. Any action located within this directory can be called via the command line so please don’t put any internal classes here. An action can be something like CreateDomain or ImportSubscribers and naming is generally consistent. You can add a Test action here that will be ignored by Git.

Services provided by the service container implement interfaces found within the src/Policy directory. If you wish to swap a service implementation with a new one, you just need to modify what’s being added to the container in the appropriate class within the src/Service directory. If you’re writing a new implementation, please put your business logic either within the src/Internal directory or within a new component. With the exception of the two aforementioned hard dependencies, it’s expected that appropriate interfaces will be added and the service container will be used for all dependencies.

Using superEngine

To use superEngine, run the application from within the super-engine directory as follows:

php superEngine --action="Test"

You may add additional arguments in any order following php superEngine command but an action argument is always required. Various actions require various different arguments. The specific arguments necessary for any action are available in the file level docblock for that action and are referenced with the @uses tag.

As an example, the CreateReseller action contains the following file level docblock:

/**
 * Creates a new Reseller in NetSapiens.
 * 
 * @uses territory      The name of the reseller.
 * @uses description    A short description of the reseller.
 */

Therefore in order to create a new Reseller within NetSapiens, superEngine will need to be run as follows:

php superEngine --action="CreateReseller" --territory="Demo" --description="Demo"

Any time you run superEngine you have the opportunity to specify a configuration file by passing the config argument with the path of the config file you wish to use. If you do so, the specified file will completely replace the default configuration file for that execution of the application. Basically you can’t override just a couple of parameters, rather you need to specify everything in your file. For example:

php superEngine --action="Test" --config="/path/to/config.ini"

Words of Caution

Because of the nature of superEngine, it’s incredibly easy to damage or delete things critical to the operation of a NetSapiens phone system. Please exercise caution when running any commands and double check your inputs.

Additionally, the NetSapiens PHP SDK follows the API specification very closely. Because the API is poorly and often incorrectly documented, sometimes things don’t always work as expected. This typically presents as Bad Request errors, however sometimes API requests will “succeed” in unexpected ways. Fixes and workarounds should be implemented within the NetSapiens PHP SDK, not within superEngine.

Extending the Application

There are three ways in which superEngine may normally need to be extended:

  1. Adding new action classes.
  2. Supporting new command line arguments.
  3. Putting new dependencies in the service container.

Also of note is that superEngine is written against PHP 7.3 so you should be using either 7.3.x or 7.4.x when running it. It’s possible that it’ll work with 7.2 but hasn’t been tested. superEngine also uses strict types everywhere and this convention should be followed moving forward.

Adding a New Action

Simply create a new class within the src/Action directory and adhere to the following:

  • Include a file level docblock containing @uses tags for all command line arguments that will be used by the action.
  • Declare a public constructor that takes a League\Container\Container object as the only argument.
  • Keep all business logic exclusive to the action within the constructor or private methods. Reusable business logic should be factored out into classes within the src/Internal directory.

Adding a New Command Line Argument

Open the src/Internal/Args.php file and do the following:

  • Add a private class property for the new argument. Also be sure to add that property to the docblock.
  • Within the constructor, add the argument suffixed by two colons to the $longOpts array.
  • Within the constructor before $this->config is set, set the property from the $args array and provide a default value. null is an acceptable default value.

Service Container Dependencies

superEngine uses league/container, which is a PSR-11 compliant service container implementation. Service definitions are located within the src/Service directory. To add a new service, please do the following:

  • Create a new class within the src/Service directory that extends the League\Container\ServiceProvider\AbstractServiceProvider class.
  • Add a protected property called $provides that contains an array of the interfaces the new service will provide.
  • Add a public method called register that accepts no arguments and returns void. Within this method, register the classes implementing the aforementioned interfaces to the container. Consult the official documentation for specifics on how to do this.
  • Open the src/ServiceProviders.php file and, within the init function, add the service to the container using the service’s fully qualified namespace.