Netsplice

Release v 0.26.0.

Netsplice is a multiplatform desktop client for VPN services. It is written in python using Logbook, OpenSSL, OpenVPN, PySide (Qt) and Tornadoweb licensed under the GPL3.

User Guide

User Guide

Introduction

Netsplice

Netsplice is a multiplatform desktop client for VPN services. It is written in python using Logbook, OpenSSL, OpenVPN, PySide (Qt) and Tornadoweb licensed under the GPL3.

Use Cases

There are several scenarios where it is essential to use a VPN connection. Most often the goal is to increase the security when using the Internet in general.

  • Securing remote maintenance operations
  • Securing data transmissions between a network of globally distributed parties
  • Connecting Teleworkers to the corporate Network
  • Make sure that the geographic detection systems used by certain tracking services can’t see your real location.
  • Appear in multiple locations at the same time
  • Hide the origin of requests
Users

Netsplice aims to satisfy users with multiple levels of experience. As VPN is a technical concept, not all users will understand what its implications are.

Level 1

No experience in VPN usage at all. The installation and configuration was done by a friend (Level 2) and only one of the VPN use-cases is expected. The software is navigated using the mouse. When the user starts the application the desired effect is expected and its progress and needs ‘translated’ progress information instead of technical messages. The major events (connecting, up, down) of the VPN need to be communicated as toast/popup messages over any other software (eg games or movies). The user will not be aware of the possible negative effects when the VPN is not up and cannot see the difference to clear. When something goes wrong and the user needs support, the software needs to compile a high-level summary and a detailed attachment and preferably send it without additional tools to the VPN provider.

Level 2

Experience using VPN as business / university. The installation is done by the user and one of the VPN use-cases is expected. To navigate the software mouse and basic keyboard shortcuts (copy&paste) are used. The user will start the software for the first time to configure one account. All future starts of the software are expected to execute that configuration. The major events (connecting, up, down) of the VPN need to be communicated as toast/popup messages. The user is aware of the possible negative effects when the VPN is not up but cannot reliably find out when that happens. When something goes wrong and the user needs support, the software needs to compile a high-level summary and a detailed attachment and preferably send it without additional tools to the vpn provider.

Level 3

Experience using VPN for various purposes. The installation is done by the user and one or more VPN use-cases are expected. To navigate the sofware mouse and keyboard shortcuts are used. The user will start the software for the first time to configure one or more accounts. All future starts of the software allow the user to decide which account to connect. The major events (connecting, up, down) of the VPN need to be communicated as toast/popup messages. The user is aware of the possible negative effects when the VPN is not up and can configure methods to handle this events. When something goes wrong the user will investigate log messages and try to fix the configuration. when the user still needs support, the software needs to compile a high-level summary and a detailed attachment with the relevant information that can be edited and send with third party software.

Level 4

Experience using VPN for various purposes running them from various devices like the router. The installation is done by the user and one or more VPN use-cases are expected. During the installation the software components and certificates are distributed to different machines. The software is used to monitor the network state for more or less complicated setups. The setups are configured and validated by the user using third party tools. When something goes wrong the user will investigate log messages and fix the configuration or file a bug-report for the faulty component.

Features

Netsplice allows to easily secure network connections. Configure multiple Accounts. Display Logs and Support information. Platform independent code base.

  • Accounts
  • Connection Status
  • Log Viewer
  • Preferences
Accounts

Netsplice supports managing one or multiple accounts and allows to launch them in a predefined order. Accounts are displayed in the Connection Status View in the main window and in the systemtray popup menu.

_images/accounts-view.png

Read More

Connection Status

When a Account is used to setup and connect, it creates a internal Connection that is used to monitor the progress and allows Netsplice to handle events. The major progress is highlighted using colors along with descriptive messages.

_images/connection-status.png

Read More

Systray

The System Tray is used to inform the user that the application is running and which state the application currently has. It provides a Context-menu that allows to Exit the application and access configured Accounts. A balloon message can be displayed to notify the user about relevant events. The System Tray is displayed next to the clock in all systems that provide a notification area.

_images/systray.png

Read More

Log Viewer

Connecting to VPN providers causes certain log messages from OpenVPN and from the application that are needed to be viewed in order to ensure correct function. Those logs need to be colored and filtered for debug, info, warning and error messages. The log messages are collected during the runtime of Netsplice and give the user and the provider’s support essential information about what is going on.

_images/logviewer.png

Read More

Preferences

Netsplice can be configured to startup when the user logs in, where and how to store account credentials, what the available providers are and how OpenVPN is invoked.

_images/preferences.png

Read More

Plugins

Netsplice allows installing various plugins. Those extend the core functionality.

Read More

Installation

This part of the documentation covers the installation of Netsplice.

Download

Get the latest installers, and their matching signatures at the downloads page.

Standalone bundle

The quickest way of running Netsplice is using a installer for the platform.

Signature verification

To verify the signature of the downloaded file both, the download and sinature in a directory and use

$ gpg --verify netsplice-DOWNLOADED-FILE.asc

Compare sign-key fingerprint with the fingerprint of the public key of the website.

Checksum verification

To verify the checksum of the downloaded files create the checksum with the tool at hand (md5sum, shasum et al) and compare the result with the contents of the checksum file.

Debian Installation
  • Download the debian installer debian/netsplice-[VERSION]_amd64.deb
  • Double-click the debian installer.
  • Enter Administrative Password.
  • Click Install.

To alternatively install using the terminal type:

.. code-block: console
sudo apt-get update sudo dpkg -i netsplice_${VERSION}_amd64.deb sudo apt-get install -f

The Application is now installed and added to the Applications Menu in the Subsection ‘System’.

In Gnome 3 the Topbar does not provide a notification or systray area by default. Instead all notification icons are located in a bar in the bottom left corner.

To have the Netsplice icon in the topbar, install the shell extension `TopIcons Plus`.

OSX Installation
  • Download Installer for OSX.
  • Double-click the Installer
  • Accept the License Agreement
  • Drag Netsplice Icon to Applications

The installation is finished. Netsplice can now be launched from the Applications directory.

Ubuntu Installation
  • Download the ubuntu installer netsplice_[VERSION].deb
  • Enable community maintained free and opensource package source in software-properties-gtk.
  • Double-click the deb-file
  • Click Install
  • or dpkg -i netsplice_[VERSION].deb; apt-get -f install in a root-shell

The Application is now installed and added to the Applications Menu in the Subsection ‘System’

Windows Installation
  • Download the Windows Installer
  • Double-click the installer.exe
  • Accept the License Agreement
  • Choose a Install Location
  • Click Install
  • Confirm TAP Driver installation

Netsplice is now installed and accessible from the Start Menu

Sourcecode

For the users that can find their way through python packages, the code is in the source packages:

  • Download the source tar package and verify the signature.

  • Unpack

  • Install python-dev (Python.h), pyside and pyside-tools using your os package manager. There are more packages depending on your platform that need to be installed with the include directories (libffi, openssl)

  • run:

    virtualenv --system-site-packages --python=python2 /prefix/netsplice
    source /prefix/netsplice/bin/activate
    python setup.py install --prefix=/prefix/netsplice
    pip install -r /downloaded_source/netsplice/pkg/requirements.pip
    
  • run `NetspliceGuiApp.py`

  • to have independent configurations per installation,

    `export HOME=/prefix/netsplice` and modify the `venv/lib64/python2.7/site-packages/Netsplice-VERSION-py2.7.egg/netsplice/config/constants.py` for multiple parallel instances.

  • to have independent executables put them in /../site-packages/Netsplice-VERSION-py2.7.egg/netsplice/var/build

  • to run the code without the setup.py rundtrip, use the Makefile (run) or execute `python2 src/netsplice/NetspliceGuiApp.py`. This will requires `PYTHONPATH` and `NAMESPACE_SOURCE` environment variables.

Running

This document covers how to launch Netsplice.

Launching Netsplice

After a successful installation, there should be a launcher called netsplice somewhere in the PATH:

% netsplice

The first time the application is launched, it will show the first run wizard that will guide through the essential settings.

Commandline Options

To see all the available command line options:

% netsplice --help
% man netsplice

See Also the Netsplice Manual Page

General Snippets

Netsplice is a multiplatform desktop client for VPN services. It is written in python using Logbook, OpenSSL, OpenVPN, PySide (Qt) and Tornadoweb licensed under the GPL3.

Feature Description

Account configuration

To connect to a VPN provider a relatively complex configuration is required. The Account Configuration tries to hide the complexity to avoid overwhelming the user. Advanced users can see the configured values in a table view and edit the plain config files. The configuration dialog can be canceled, the current configuration may be checked and the changes in the dialog can be saved.

_images/account-config.png

The first pane in the configuration is used to query the user for a name that is displayed in the account list and a type of the account. Each type loads a different set of forms to the dialog that should consist of multiple levels.

OpenVPN configuration

The OpenVPN configuration allows the user to enable and disable the account and exposes the autostart option, that tells Netsplice to connect the account when the application starts. The Import Button lets the user choose a previously downloaded config that has to contain all certificates.

Advanced OpenVPN configuration

The Advanced Configuration allows to remove, modify or introduce lines properties of the Account. A Key-Value table and a syntax highlighted plaintext edit allow to change any aspect of the account. The Key-Value configuration view is similar to what the user knows from firefox’s about:config.

Unmodified lines are displayed as normal lines. Lines that have been inserted show italic. Lines that have been modified by the user are displayed in bold. The dialog provides two actions Reset to defaults - that resets all user modifications (no more bold entries). and reset to a line to its imported value.

_images/account-config-advanced.png

For advanced users the configuration options are presented in a table view with documentation hints and indicators changed, inserted values. The rows in the table can be reset to the imported defaults and the complete configuration may be reset to the imported defaults.

_images/account-config-advanced-plaintext.png

A advanced user can also edit the configuration in plaintext in a syntax highlighted editor with tooltips on the OpenVPN keywords. To influence the highlights in the table view, the imported values may also be edited in the advanced mode.

More details can be found in the OpenVPN Plugin Description.

Accounts

Netsplice supports managing one or multiple accounts and allows to launch them in a predefined order. Accounts are displayed in the Connection Status View in the main window and in the systemtray popup menu.

_images/accounts-view.png

Each account has a name that is shown in the Connection Status View. When an account is connected, Netsplice creates an internal connection object that references tha account by id, so its type and name can be received. This allows the GUI to handle displaying useful information without loading sensitive information from the backend. Accounts are preference objects that are stored in the user configuration directory along with the user’s grouping and order. The type of the account determines which dialog will be used to edit the account, which icon is displayed on the account-list, and what method is used to setup a connection from the account. The configuration itself is stored as a simple text. All accounts have a enabled, autostart, default_route and persist flag.

Context actions for the account in the Connection Status View are:

  • Connect
  • Connect All
  • Disconnect
  • Reconnect
  • Reset
  • Add/Create
  • Remove
  • Show Log
  • Settings
  • Group
  • Ungroup (only if a parent group exists)
  • Move Up
  • Move Down
  • Move Left
  • Move Right

A double click on the account will trigger the connect action.

Below the Connection Status View is a button to connect groups or create a new account.

Actions for the account in the systemtray are connect and disconnect. Those are dependent of the state of the account.

Accounts have small icons that hint their configuration in the account list.

_images/chain_info.png

The account with this icon is chained to a parent.

_images/chain_info_parallel.png

The account has chained accounts and they are connected in parallel.

_images/chain_info_sequence.png

The account has chained accounts and they are connected in sequence.

_images/chain_info_default_route.png

The account provides a default route when connected.

Read more about the Connection Statemachine that is used to setup, connect and shutdown configured accounts.

Account Order

The user may order the accounts so they are started in a predefined order. Depending on an accounts routing configuration the order can have a significant impact on performance. Improper use of account ordering can make communication impossible or prevent connecting a VPN.

A good rule of thumb is to start the account that sets a new default route first. Then start accounts which only get host routes pushed.

Account Group

The user may order the accounts in a hierarchy so they are started in a predefined order. The hierarchies can also make the accounts dependent but do not allow the accounts to interact. A group may define a connect mode sequence or parallel how all accounts in the group are connected. When an account in the list of to-connect accounts needs credentials, they are requested before the list- connect starts. Groups are always aggregated in leaf to root direction.

Actions for the group in the Account-List-Treeview are:

  • Connect All Accounts
  • Disconnect All Accounts
  • Reconnect All Accounts
  • Create Group
  • Rename Group
  • Delete Group
  • Collapse

When hierarchies of groups are defined, the groups are aggregated to a list before they connect:

Global
    SubgroupA
        SubsubgroupAA
            AccountAA1
            AccountAA2
        SubsubgroupAB
            AccountAB1
            AccountAB2
        AccountA1
        AccountA2
    SubgroupB
        SubsubgroupBA
            AccountBA1
            AccountBA2
        SubsubgroupBB
            AccountBB1
            AccountBB2
        AccountB1
        AccountB2

Connect All on Global will connect the accounts in the following order:

AccountAA1
AccountAA2
AccountAB1
AccountAB2
AccountA1
AccountA2
AccountBA1
AccountBA2
AccountBB1
AccountBB2
AccountB1
AccountB2
Account Chain

The user may define chains of accounts to control how they are related and how they interact. In contrast to the Account Order this allows better control for complex routing setups.

When chains of accounts are defined, the accounts are aggregated to connect in a parent-child order:

Global (Group)
    AccountA
        AccountA1
            AccountA2
            AccountA3
    AccountB
        AccountB1
            AccountB2

Connect All on Global will connect the accounts in the following order:

AccountA
AccountA1
AccountA2
AccountA3
AccountB
AccountB1
AccountB2

Connect on A1 will connect the accounts in the following order:

AccountA
AccountA1

Connect all on A1 will connect the accounts in the following order:

AccountA
AccountA1
AccountA2
AccountA3

The modes that are affecting in which order the accounts of a chain connect are Connect Modes and Failure Modes.

Read more about the Connection Statemachine that controls the transitions.

Connect Modes

Account Groups and Account Chains can select from various modes to configure how they connect all accounts in that group or chain.

None

When None is selected, no account in the group or chain will be connected. The accounts can only be connected manually.

None is activated when multiple accounts in aggregated accounts have the default route flag enabled.

Parallel

When the mode Parallel is selected, all accounts on the same level of a group or chain are connected at the same time.

This mode requires that the accounts in the list do not define conflicting routes. When they do, the results are undefined as the order cannot be predicted.

Beware that depending on your system connecting lots of accounts at the same time can cause hard to debug issues.

Use this mode to manage multiple different independent networks.

digraph G {
    Host -> backend_vpn[label="10.1.1.0/24)"]
    Host -> frontend_vpn[label="(10.1.2.0/24)"]
    Host -> database_vpn[label="(10.1.3.0/24)"]
    Host -> payment_vpn[label="(10.1.4.0/24)"]
}
Sequence

When sequence is selected, all accounts are connected from top to bottom. Netsplice waits until each connection is up, this mode takes longer than the parallel connect.

The wait time between each connection can be defined with the global preference sequence_wait_after_connect. The default value is 1 second. Waiting a bit gives the system time to process the network changes without causing funny side effects.

This mode is useful if you connect to a many different VPN endpoints that set a bunch of routes.

digraph G {
    Host -> firewall_internet[label="(10.1.0.0/24)"]
    firewall_internet -> firewall_backend[label="(10.1.1.0/24)"]
    firewall_backend -> vpn_database[label="(10.1.2.1/24)"]
}
Failure Mode

Connect Failure Mode configures how Netsplice should act when an account fails to connect or when a account of a chain disconnects.

Ignore

The Ignore failure mode is the default setting for chains and will cause all other accounts that are in the chain to continue connecting.

Disconnect

The Disconnect failure mode will cause all other accounts in the chain to disconnect when the account fails to connect or reconnect.

Reconnect

The Reconnect failure mode will cause the owning account to reconnect. The account that causes the failure will also reconnect. Other accounts in the parent chain are not reconnected, but all dependents will.

Examples

Create a VPN connection that tunnels over TOR:

Account "Tor 5960"
    Chain [Reconnect]
    Advanced: SocksPort 5960

    Account "IPredator v4 pool"
        Chain [Reconnect]
        Advanced: TCP only, socks-proxy=localhost 5960
        ProcessLauncher: create/privileged host route to guard node

Ensure no Tor traffic is leaked to the attached network:

Account "IPredator v4 pool"
    Chain [Reconnect]
    Advanced: UDP/443
    Optional ProcessLauncher: create/privileged iptables rules

    Account "Tor 5960"
        Chain [Reconnect]
        Advanced: SocksPort 5960
Manual Network Settings: SOCKS Host localhost Port 5960 and RTFM!

Create SSH socks proxy to a node behind a hidden service:

Account "Tor 5960"
    Chain [Reconnect]
    Advanced: SocksPort 5960

    Account "SSH myh1dd3n5ervice1.onion"
        Chain [Reconnect, Parallel]
        Advanced: -D 5961
                  -o ProxyCommand ... socksport=5960

        Account "SSH Firewall Navigation"
            Chain [Reconnect]
            Advanced: -D 5962
                      -o ProxyCommand ... socksport=5961

        Account "SSH Firewall Students"
            Chain [Reconnect]
            Advanced: -D 5963
                      -o ProxyCommand ... socksport=5962
Configure ~/.ssh/config Host/ProxyCommand.

# ProxyCommand /usr/bin/socat STDIO SOCKS4A:localhost:%h:%p,socksport=11000
# ProxyCommand /usr/bin/nc -x localhost:11001

# socat: 1.7.3.2
# nc: openbsd-netcat 1.105-r1
# Other parameters are needed for different versions of nc.

Credentials

When a account requires credentials to connect, a credential dialog is shown. This dialog allows to enter a username and a password and allows the user to define how this values are handled.

_images/credential.png
Storage

The default is that the application does not store the credentials and forgets them as soon as they are used. Remember for the session keeps the values in memory, so they can be reused when the account needs to reconnect but dont write them to the disk. When the credentials should be stored the OS keystore is used. The keystore is a operatingsystem provided store that should be used when the host system is trusted. The preferences option config.backend.STORE_PASSWORD_DEFAULT configures the preselection of the dialog.

When the log contains messages about ‘OS Keystore not available’, then it is possible that the crypto components of the virtualenv are not built, recreate the virtualenv and review the output during wheel build. This happens for users of the source package.

Connection Status

When a Account is used to setup and connect, it creates a internal Connection that is used to monitor the progress and allows Netsplice to handle events. The major progress is highlighted using colors along with descriptive messages.

_images/connection-status.png
Status details

The details are consistently used in various locations in the GUI. The colors may differ based on the theme set in the preferences.

Colors

The colors are controlled by the selected theme.

  • None: Disconnected
  • Red: Failed connection
  • Orange: Connecting
  • Green: Connected
Details
  • The current interface velocity
  • The assigned IP-address

The details depend on the connection type.

Messages
  • Connected
  • Connecting
  • Disconnected
Account Hierarchy

The accounts configured will be configured to a hierarchy. The hierarchy may be a Group or a Chain.

Groups are displayed in a collapsable box, chains as indented boxes.

_images/connection-status-groups.png _images/connection-status-chains.png
Read more about Groups
Chains.

Connection State Machine

Connections are implemented as state machines. This ensures that each event can be processed by a finite number handlers and all transitions are defined.

Connection

The connection state machine interfaces between the plugins that implement asynchronous methods for the transition of the states.

The states of a connection are:

  • Aborted - The processing was disconnected with an error
  • Created - - The object has been created
  • Setting_Up - - All information to connect is made available to the process.
  • Initialized - The object has been created and all information to connect is available in the process. This state is required from all accounts in a chain before the first connect is executed.
  • Connecting - The connection has been queued for connection.
  • Connecting Process - The plugin has initialized the (external) process that is responsible to handle the protocol. The process (OpenVPN/Tor) starts and loads the provided configuration.
  • Connected - The plugin has completed the handshakes and a interface or socket is setup and ready to use.
  • Disconnecting - The connection has been queued for disconnection
  • Disconnecting Failed - The failed connection has been queued for disconnection because a failure occurred during connect or operation that may be recovered by reconnecting the parent.
  • Disconnected - The connection has been terminated by the plugin, the interface or socket has been removed, the allocated process resources are free.
  • Disconnected Failed - The connection has been terminated because of a error.
  • Reconnecting - The connection has been queued for reconnection.
  • Reconnecting Process - The plugin has triggered the reconnect sequence in the external process.
  • Reconnected - The plugin has completed the sequence to restore the connection, the interface or socket is setup and ready to use.
digraph G {
  Created -> Setting_Up
  Setting_Up -> Initialized
  Setting_Up -> Disconnected_Failure
  Initialized -> Connecting
  Connecting -> Connecting_Process
  Connecting_Process -> Connected
  Connecting_Process -> Disconnecting_Failed

  Connected -> Reconnecting
  Connected -> Disconnecting
  Connected -> Disconnecting_Failed

  Reconnecting -> Reconnecting_Process
  Reconnecting -> Disconnecting_Failed
  Reconnecting_Process -> Reconnected
  Reconnecting_Process -> Disconnecting_Failed

  Reconnected -> Disconnecting
  Reconnected -> Reconnecting
  Reconnected -> Disconnecting_Failed

  Disconnecting -> Disconnecting_Process-> Disconnected
  Disconnecting_Failed -> Disconnecting_Failure_Process -> Disconnected_Failure
}
Connection Broker

The connection broker interfaces between Connection and Account. The broker ensures that connection chains are established and translates the connection events into account events.

The connection broker holds multiple lists. A connection list that tracks all connection objects. A connection queue that holds connections that need to be notified and a connection chain that holds the relation between the connection objects.

A user usually requests an account object to connect. This request is send to the connection broker that queues the account for connection (setup the connection object).

The broker acts on the events connected, disconnected, disconnected_failure and reconnected from the connection.

The states of the broker are aborted, active and inactive.

When the broker arrives (with the RESUME trigger) at a queued account entry it evaluates the account chain preference and creates additional connection objects depending on the defined chain. Those objects are then send into different state transition path’s that are requested (connect/disconnect/reconnect api), unconditional (disconnect all children of a disconnected) or preference controlled (reconnect parent).

digraph G {
    subgraph cluster_connection_broker {
        style=filled;
        color=lightgrey;
        label="Connection Broker";
        CMTRESUME[shape=box];
        CMTCANCEL[shape=box];
        CMTDONE[shape=box];

        CMTRESUME -> CMNextQueue
        CMNextQueue -> CTConnect[label="next"];
        CMNextQueue -> CTReconnect[label="next"];
        CMNextQueue -> CTDisconnect[label="next"];
        CMNextQueue -> CTSetup[label="next"];
        CMNextQueue -> CMTDONE[label="none in queue"];
        CMTCANCEL -> CMNextQueue[label="modify queue"]
    }
    subgraph cluster_connection {
        style=filled;
        color=lightgrey;
        label="Connection";

        CTConnect[shape=box];
        CConnecting;
        CConnected;
        CTConnect -> CConnecting -> CConnected;

        CTSetup[shape=box];
        CSetting_Up;
        CInitialized;
        CTSetup -> CSetting_Up -> CInitialized;

        CTDisconnect[shape=box];
        CTDisconnect -> CDisconnecting -> CDisconnected;

        CTReconnect[shape=box];
        CTReconnect -> CReconnecting -> CReconnected;
    }
    CInitialized ->CMTRESUME;
    CConnected -> CMTRESUME;
    CDisconnected -> CMTRESUME;
    CReconnected -> CMTRESUME;
}
Connection Plugins

Every connection plugin will implement its process as asynchronous code. State changes are not async, this means the connection plugin methods are called in the next ioloop. Something triggered in the state machine they cause all state machine events to be processed in the current loop/thread.

Log viewer

Connecting to VPN providers causes certain log messages from OpenVPN and from the application that are needed to be viewed in order to ensure correct function. Those logs need to be colored and filtered for debug, info, warning and error messages. The log messages are collected during the runtime of Netsplice and give the user and the provider’s support essential information about what is going on.

_images/logviewer.png

The log viewer can be accessed from the main menu and displays a window with actions to filter and sort the output. The default is not to filter any messages and display all messages in reverse order. The messages are pre-processed to demote or promote certain messages as some messages come in as ‘info’ while they should be highlighted as error while others state ‘error’ but should in fact only be information. The log viewer will show when the backend requests the GUI to display for example when a account failed to connect or when the backend crashed. New log messages are appended to the list and when the user has scrolled to the end, the list continues to keep the end visible.

Log entry colors

The lines of each log entry are highlighted by color:

  • Bold red: Critical
  • Red: Error
  • Orange: Warning
  • White: Info, Debug
Filter of messages

On the top it is possible to filter the messages by their message level. Multiple selections are possible.

Available filters are:

  • Debug
  • Info
  • Warning
  • Error
  • Critical

When a text is entered to the filter, only messages containing the string are displayed. The text filter can be toggled case-sensitive. The clear filter action can be used to reset all filters and display all available log messages.

Log actions

The log viewer provides two actions: copy to clipboard and send to support. The lines in the log viewer may be selected and copied to the clipboard using the context menu.

Plugins

Netsplice allows installing various plugins. Those extend the core functionality.

Unlike the name suggests, plugins are not user-configurable plugins but require the config/plugins.py to be modified.

The following plugins are part of the default installation:

Other Plugins must implement the Plugin API and need to be installed with the Preferences. Be careful trusting the developer of the plugin. Plugins can potentially expose the users identity even if Netsplice displays everything is alright.

Preferences

Netsplice can be configured to startup when the user logs in, where and how to store account credentials, what the available providers are and how OpenVPN is invoked.

_images/preferences.png

The preference dialog will be modeled after the implementation of the features has completed. Therefore a generic dialog is displayed for now. The following description applies to the planned preference dialog.

From the Main Menu a Preferences View can be launched that allows to set the possible options. The options are split into the following sections:

  • General
  • Display
  • Security
  • Plugins
  • Advanced

The preferences are stored as JSON in ~/.config/Netsplice/preferences/*.conf (linux/OSX) or in /Users/[your_name]/AppData/Local/Netsplice/preferences/*.conf.

The Preferences can be reset to the installation defaults with a button action.

General

The general preferences control how the application integrates into the Operating System.

  • Status Display How/If notifications are displayed (connect/disconnect)
  • Notification Sounds How/If notification sounds are played (connect/disconnect)
  • Autostart behavior Control if the Application is launched when the user is logged in. Should it automatically connect?
Display

The display preferences the look of the application. This is intended for better accessibility.

  • Font and Font Sizes
  • Icon Theme (eg tray icon)
  • Colors and Theme
Security

The security preferences control how sensitive information is stored and what should be done when the application quits / gets no connection.

  • Master Password to encrypt all remembered provider passwords (not stored)
  • Review/Modify provider passwords
  • Shutdown behavior (route to lo)
  • When to re-check Provider capabilities (before connection, based on age, never)
Plugins

The plugins preferences control which plugins are active and allows the installation and uninstallation of plugins.

  • List Plugins
  • Install Plugin
  • Uninstall Plugin
  • Enable Plugin
  • Disable Plugin
Advanced

The advanced preferences allow configuration of not-so-common features and options that are not exposed to the user interface. Those options should be handled with care.

  • Alternate backends
  • Internal configuration variables
  • Updates
  • Overrides
  • SSL-Certificates for network communication
  • Crash Reporter

System Tray

The System Tray is used to inform the user that the application is running and which state the application currently has. It provides a Context-menu that allows to Exit the application and access configured Accounts. A balloon message can be displayed to notify the user about relevant events. The System Tray is displayed next to the clock in all systems that provide a notification area.

_images/systray.png
Icons

Various icons signal the state of the application.

  • Starting
    The application has been started but needs time to setup its backend
  • Started
    The application has been started and is ready to process user input
  • Connecting
    The application currently connects a Account VPN connection. The animation that displays during the connecting state can be disabled with the config.gui.SYSTRAY_ANIMATE_ICON option.
  • Connected
    The application is currently connected to a Account, the VPN connection is active
Click Action

When the user clicks on the tray icon, the main window is toggled between visible and invisible. This feature is not available in OSX

Messages

Some messages are displayed using a system tray balloon. Those messages can be disabled using the config.gui.SYSTRAY_SHOW_NOTIFICATIONS option.

  • Connected

    • Displays when a Account has been successfully connected.
    • -Displays the IP assigned from the VPN server-
    • -Displays the IP visible by other services-
  • Connecting

    • Displays when a Account has started to connect.
  • Disconnected

    • Displays when the Connection to a VPN server is lost.
    • Displays when the User has chosen to disconnect a Connection.
  • Failed

    • Displays when a Connection failed to connect.
Context Menu

Various Actions can be triggered using the context menu

  • Quit

    • Quit the Application, shutdown all connections
    • Displays a Confirm message when there are active connections
  • Accounts

    • One Entry per configured Account
    • Message
      • Connect (when disconnected)
      • Disconnect (when connected)
      • Connecting (when connecting)
  • Show / Hide

    • Show or Hide the main window.

Developer Guide

Contributing to Netsplice

The following is a set of guidelines for contributing to Netsplice. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request.

This project adheres to the Contributor Covenant 1.2. By participating, you are expected to uphold this code. Please report unacceptable behavior to Support.

Reporting bugs

Report all the bugs you can find to us! If something is not quite working yet, we really want to know. Reporting a bug to us is the best way to get it fixed quickly, and get our unconditional gratitude.

It is quick, easy, and probably the best way to contribute to Netsplice development, other than submitting patches.

Reporting better bugs: New to bug reporting? Here you have a great document about this noble art.

Where to report bugs

Use Support.

What to include in your bug report
  • The symptoms of the bug itself: what went wrong? What items appear broken, or do not work as expected? Maybe an UI element that appears to freeze?
  • The Netsplice version you are running. You can get it by doing netsplice --version, or you can go to Help -> About Netsplice menu.
  • The installation method you used: bundle? from source code? debian package?
  • Your platform version and other details: Ubuntu 14.04? Debian unstable? Windows 8? OSX 10.8.4? If relevant, your desktop system also (gnome, kde…)
  • When does the bug appear? What actions trigger it? Does it always happen, or is it sporadic?
  • Maybe a screenshot of the problem you’re seeing.
  • The exact error message, if any.
  • Attachments of the log files, if possible (see section below).

Also, try not to mix several issues in your bug report. If you are finding several problems, it’s better to issue a separate bug report for each one of them.

Attaching log files

If you can spend a little time getting them, please add some logs to the bug report. They are really useful when it comes to debug a problem. To do it:

Launch Netsplice in debug mode. Logs are way more verbose that way:

netsplice --debug

Get your hand on the logs. You can achieve that either by clicking on the “Help -> Show log” menu, and saving to file, or getting the log files from the config folder.

Need human interaction?

You can also find us in the #support channel on the our IRC server. If you do not have a IRC client at hand, you can enter the channel via web.

Pull Requests

  • Fork our repo
  • Work your code in a separate branch (feature/ticket), create it from master
  • Create a pull request against develop
  • All tests should pass
  • The code needs to be pep8 compliant (run pep8 . from the top folder)

Example workflow::

$ git clone https://??
$ cd netsplice
$ git checkout master
$ git checkout -b '#12345'
$ git commit -m '[bug] title of bug

summary.

  - what has changed'
$ git push origin '#12345'
Git Commit Messages
  • Use the present tense (“add feature” not “added feature”)
  • Use the imperative mood (“move cursor to…” not “moves cursor to…”)
  • Short (50 chars or less) summary on the first line
  • Separate subject from body with a blank line
  • Wrap the body at 72 characters or less
  • Do not end the subject line with a period
  • Use the body to explain what and why vs. how

For a good reference on commit messages and why does it matter see: http://chris.beams.io/posts/git-commit/

Template for commits

You can activate a standard template for your commits on this repo with:

git config commit.template docs/netsplice-commit-template

The template looks like this:

[type] <subject>

<body>
<footer>

Type should be one of the following:

  • bug (bug fix)
  • feat (new feature)
  • docs (changes to documentation)
  • style (formatting, pep8 violations, etc; no code change)
  • refactor (refactoring production code)
  • test (adding missing tests, refactoring tests; no production code change)
  • pkg (packaging related changes; no production code change)
  • i18n translation related changes

Subject should use imperative tone and say what you did. For example, use ‘change’, NOT ‘changed’ or ‘changes’.

The body should go into detail about changes made.

The footer should contain any issue references or actions. You can use one or several of the following:

  • Fixes: #12345
  • Refs: #12345

The Documentation field should be included in every new feature commit, and it should link to an issue in the bug tracker where the new feature is analyzed and documented.

Example
[feat] add vpn status to ui

Display the status of the current vpn.

- Resolves: #12345

Terminology

This document describes terms that are used by users and developers related to the software. This document uses nouns for naming and orders them alphabetically. It defines link-labels for other documents and references itself.

Account

User configurable entry in the GUI that contains all relevant information about how to connect to a Provider.

Account Group

User configurable hierarchical group of Accounts that defines an order how accounts are displayed and connected.

Action

UI Button Element that is usualy triggered with a mouse-click or from a Menu Entry. A Action can have a enabled and disabled state and needs at least two text elements: the Action Label - usualy the Text on the Button and the Action Tooltip - usualy the Text that is displayed when the mouse is hovering the Button

Backend

Application component that implements the business functionality and brokers between the other components.

Client

Application that connects to a Provider.

Config

Configs are Provider presented configuration files. They vary in the selection of encryption algorithms to the IP-protocol used. OpenVPN needs a Config to connect to a service of the Provider.

Connection

Connections are created from Accounts by the backend to handle state-changes and collect Log information.

Credential

Credentials are the secrets that are required to authenticate to a Provider or to unlock a certificate.

GUI

Application component that is visible to the user. It displays status and dialogs and is controlled from the Backend.

ISP

Service that provides the Client with open internet access and allows it to connect to Provider.

Log

The application stores technical information in a log that can be reviewed by the User to evaluate reasons for failures. The log is a list of log items that relate to Connections or the application in general.

OpenVPN

Opensource technology that uses virtual private network techniques for creating secure point-to-point or site-to-site connections.

Install Package

Installer for a specific platform that allows normal user to install the application with the native means of the operatingsystem.

Model

A model is a technical description of values that can be stored or that can be exchanged between application components. Models define fields that can be serialized and unserialized.

Model Field

A Model Field defines a name, type, default-value and validator for a value in a Model.

Preferences

The persistent settings of a User that include UI preferences as well as Account and Account Groups.

Privileged Backend

Application component that executes privileged operations for the Backend.

Privileged Operation

An operation that is not allowed as normal User.

Provider

Provider is any service that allows clients to connect using OpenVPN.

Statusbar

The statusbar is a small informative bar that displays the application state and version. It is visible for the mainwindow.

Systray

Most desktop environments provide a notification area that allows applications to put an icon with a context-menu and occasional messages in there.

User

The user logged in on the computer. The user has no privileges to change the network configuration, but can access the internet and read and write files in the users home directory.

Architecture

This document describes the components of the application, their interaction and purpose. Names marked with a * are not final yet.

digraph G {
    subgraph cluster_backend {
        style=filled;
        color=lightgrey;
        "Backend Logic"[shape=box];
        "Backend API" -> "Backend Middleware" -> "Backend Logic";
        "Backend Logic" -> "Application Monitor"[arrowhead=none,arrowtail=none];
        "Backend Logic" -> "Key Storage"[arrowhead=none,arrowtail=none];
        "Backend Logic" -> "Logging"[arrowhead=none,arrowtail=none];
        "Backend Logic" -> "Privileged Model"[arrowhead=none,arrowtail=none];
        "Backend Logic" -> "Network Model"[arrowhead=none,arrowtail=none];
        "Backend Logic" -> "GUI Model"[arrowhead=none,arrowtail=none];
        label = "Backend App";
    }
    subgraph cluster_priv {
        style=filled;
        color=lightgrey;
        "Privileged Logic"[shape=box];
        "Privileged API" -> "Privileged Middleware" -> "Privileged Logic";
        label = "Privileged App";
    }
    subgraph cluster_unpriv {
        style=filled;
        color=lightgrey;
        "Unprivileged Logic"[shape=box];
        "Unprivileged API" -> "Unprivileged Middleware" -> "Unprivileged Logic";
        label = "Unprivileged App";
    }
    subgraph privileged_processes {
        style=filled;
        color=lightgrey;
        label = "Privileged Processes";
        "openvpn1";
        "openvpn2";
    }
    subgraph unprivileged_processes {
        style=filled;
        color=lightgrey;
        label = "Unprivileged Processes";
        "tor";
        "ssh";
    }
    subgraph cluster_net {
        style=filled;
        color=lightgrey;
        "Net Logic"[shape=box];
        "Net API" -> "Net Middleware" -> "Net Logic";
        label = "Net App";
    }

    "UI (Qt-Client)" -> "Backend API"[label="REST"];
    "UI (CLI-Client)" -> "Backend API"[label="REST"];
    "Privileged Logic" -> "Backend API"[label="REST"]
    "Privileged Logic" -> "openvpn1"[label="starts"];
    "Privileged Logic" -> "openvpn2"[label="starts"];
    "Unprivileged Logic" -> "tor"[label="starts"];
    "Unprivileged Logic" -> "ssh"[label="starts"];
    "Net Logic" -> "Backend API"[label="REST"];
    "Net Logic" -> "Remote Services";
    "Backend Logic" -> "Privileged API"[label="REST"];
    "Backend Logic" -> "Unprivileged API"[label="REST"];
    "Backend Logic" -> "Net API"[label="REST"];
}
UI

Qt-based interface that the user sees. The UI process acts as a stand alone process and is essentially stateless. All required information is fetched from the backend. This is to allow other frontends eg a cli to exist as well using the REST API the backend exposes. When the UI is killed, backend processes are not affected. For some exceptions (startup, shoutdown) the UI will signal the backend so it can update its model and decide what to do.

UIApp
  Acquire shared secret
    - Found: connect to BackendApp
    - Not found: start BackendApp and re-acquire shared secret
  While running:
    https://backend/gui/model -> JSON -> populate/update GUI model
      - Keep connection open to get model updates (long polling)
    Update UI based on model
    Handle UI events, translate them to REST API calls:
      https://backend/gui/action/[action-name]
Backend

API based invocation and maintenance of state of the connections. Accepts connections to a parameterized TCP port on localhost. The backend can be started on its own without any UI frontends present. E.g. in cases where the backend is installed as a service or started by a system management process - for example systemd - for headless operations. Creates management services that the Net and Privileged applications use to connect. Defines the data model for the application and synchronizes the data with a storage.:

BackendApp
  Acquire shared secret
    - Found: startup REST API
    - Not found: generate secret, startup REST API
  Load Model
  (Process Startup rules)
  While running:
    Process REST API
    Process cron Rules
  endpoints:
    /gui/model[/${collection}[/${instance}[/${attribute}]]]
      CREATE: signal user wants to create instance
      GET/READ: expose state, keep open for updates
      PUT/UPDATE: signal user want to update instance
      DELETE: signal user wants to delete instance
    /gui/action/${action-name}
      POST: signal user wants to execute procedure
    /priv/model[/${collection}[/${instance}[/${attribute}]]]
      CREATE: signal privileged wants to create instance
      GET/READ: expose state, keep open for updates
      PUT/UPDATE: signal privileged want to update instance
      DELETE: signal privileged wants to delete instance
    /priv/action/${action-name}
      POST: signal from privileged process events

The backend is separated into several modules. They are named by the actors that will access the modules. The actors usually request models and invoke controllers that modify model data. Additionally the actors may invoke processes that will return immediately and report their state through models. The logic which subroutines are invoked is implemented in the backend in controllers that act on events.

The following processes are handled by the backend:

Connect a single account
Connect a account group
Disconnect a single account
Disconnect a account group
Connect a single account

To connect a single account the backend will need to setup a connection in the privileged backend using the configuration and credentials stored for the account. Then the privileged backend is instructed to connect the connection and all events and log entries are collected for that connection. The backend will process those events and log entries to monitor the state of the connection and updates the gui model if appropriate.

Connect a account group

To connect a account group the backend needs to evaluate all accounts of that group and create a list of accounts to connect. It will connect each account in the user-defined order and advance to the next account when it detects that a connect operation has completed.

Net

Network connections that may be invoked by the User like download updates, post registration details, acquire visible IP etc.

The Net application runs as a dedicated user and connects to a parameterized backend port on localhost. It refuses to start when it cannot connect to the backend. It shuts down itself and all its child processes when the socket is closed by the backend. This behavior is similar to openvpn --management-client.

NetApp
  Acquire shared secret
    - Found: connect to BackendApp
    - Not found: shutdown / error-code
  Start REST API
  While running:
    process REST API
    check BackendApp is alive || shutdown

  endpoints:
    /config
      GET/READ: request remote configuration resource
    /update
      GET/READ: request remote update binary
    /news
      GET/READ: request remote news about binary
    /provider-list
      GET/READ: request remote provider list
    /support
      CREATE: post support information
Privileged

The privileged application runs as ROOT and connects to a parameterized backend port on localhost. (XXX this is the simple implementation which is OK to start with. It should be kept in mind that ROOT operations should always imply a privsep setup) It refuses to start when it cannot connect to the backend. It shuts down itself and all its child processes when the socket is closed by the backend. This behavior is similar to openvpn --management-client.

PrivilegedApp
  Acquire shared secret
    - Found: connect to BackendApp
    - Not found: shutdown / error-code
  Start REST API
  While running:
    process REST API
    check backendApp is alive || shutdown

  endpoints:
    /openvpn[/${connection-identifier}]
      CREATE: push complete configuration to privileged model, get
      connection-identifier. starts OpenVPN process
      GET/READ:
        - / get list of available connection-identifiers
        - /${connection-identifier} get state/output of connection
      UPDATE:
        - send (filtered, named) commands to existing OpenVPN process
      DELETE:
        - shutdown OpenVPN process
    /support[/${connection-identifier}]
      GET/READ: compile support information
    /${plugin}
      endpoint to be registered by a plugin

Privileged application that controls the invocation of software that needs elevated OS access. Exposes its interface with REST and ensures that only the backend process can use it. Collects system information only root can access (eg firewall rules, routing).

configures, starts and stops OpenVPN configures, starts and stops firewall configures dns leak prevention

REST

The processes use REST to communicate. Each process defines an interface that allows only specific calls. This makes it possible for reviewers to validate each component for security and functionality.

The frameworks evaluated for the IPC are:

  • bottlepy size: 69kb / 1dep route by @(/resource/<variable>) authentication: bottle-cork (556kb / 2dep) middleware: beaker (300kb / 2dep) ssl using custom ServerAdapter, no additional dep, allows socket config
  • cherrypy size: 435kb / 1dep route by object hierarchy authentication builtin middleware builtin ssl builtin, not for configuration through API
  • flask size: 1158kb / 4deps route by @(/resource/<int:variable>) authentication: bottle-cork (556kb / 2dep) middleware: beaker (300kb / 2dep) ssl using ssl_context windows uses
  • tornado size: 816kb / 6 deps should be used as server for other frameworks anyway route: regexp -> class mapping middleware: prepare member function ssl: builtin, pass ssl_context longpolling
  • webpy size: 90kb ssl: pyOpenSSL +1141kb / 6deps

Netsplice uses tornado because of the limited dependencies and its possibilities to configure the ssl-socket. Flask is too heavy-weight and Cherrypy’s SSL does not allow the configuration of the ciphers/protocols. Web.py is very minimal and contains too much ‘quick hack’ comments. Flask, Cherrypy, Bottle suggest using tornado when long-polling and SSL come into play. Tornado was initiated by facebook and is now an open source (Apache 2.0) project.

Process Separation

To ensure separation of duties Netsplice is divided into four general processes (NOTE: can be more esp when privsep is used). Each process defines an interface that allows only specific calls. This makes it possible for reviewers to validate each component for security and functionality.

The process that the user starts is the GUI-application. This application is located in the PATH of the OS and may be called from the start-menu (windows), as Starter (.application) on Linux and as entry-point for the Netsplice.app on OS X. As the GUI should be fairly stupid in regards to business logic one of its first actions is to check if there is a Backend-application. If there is no backend already started it needs to start one. If there is a backend process reuse it.

The Backend-application will be located in a library location, not accessible from PATH or being ‘hidden’ in the .app Resources for OS X. The GUI-application starts the Backend-application with a host (localhost) and a configured (free) port as parameter. It then uses a HttpClient to connect to the Backend- application to acquire a view-model that can be used to populate controls. The model endpoint is a long-polling request that is restarted every time it completes. Actions initiated by the user are signaled to the backend on separate REST endpoints.

Process communication

The processes ‘backend’, ‘privileged’ and ‘network’ are designed as REST services. Those services are secured using certificates generated by the backend and shared between processes. Each service uses a certificate authority to sign the service (server) key. The same CA is used to sign client- keys. The client validates the connection with this CA. The creation of certificates will occur when openssl validate returns errors (eg not found, expired) for any of the required certificates. After the client-certificates are generated the server-certificate-keys are deleted to prevent abuse of those certificates. The backend application will load backend.crt (+ key + ca) for its REST service and privileged-backend- client (+ key + ca) for the connection to the privileged process. The privileged process will load privileged.crt (+ key + ca) for its REST service and backend-privileged-client.crt for the connection to the backend process. The same applies for the network application. The GUI application will only load backend-gui-client.crt and connect to the backend service.

When the GUI starts the backend for the first time, the keys are created by the backend. The backend will not start its children (privileged and network) until the keys are created. The GUI will notice that the backend is not available during this period and shows a progress bar to the user detail the start-up procedure.

digraph G {
    subgraph cluster_backend {
        style=filled;
        color=lightgrey;
        "BackendDI"[shape=box];
        "Setup"[shape=box];
        "backend.ca.crt";
        label = "Backend App";
    }
    subgraph cluster_privileged {
        style=filled;
        color=lightgrey;
        "PrivilegedDI"[shape=box];
        "privileged.ca.crt";
        "privileged-backend-client.crt";
        "backend-privileged-client.crt";
        label = "Privileged App";
    }
    subgraph cluster_network {
        style=filled;
        color=lightgrey;
        "NetworkDI"[shape=box];
        "network.ca.crt";
        "network-backend-client.crt";
        "backend-network-client.crt";
        label = "Network App";
    }
    subgraph cluster_gui {
        style=filled;
        color=lightgrey;
        "GUIDI"[shape=box];
        "backend-gui-client.crt";
        label = "GUI App";
    }
    "BackendDI"->"Setup"
    "Setup"->"backend.ca.crt"[label="generates"]
    "Setup"->"privileged.ca.crt"[label="generates"]
    "Setup"->"network.ca.crt"[label="generates"]

    "backend.ca.crt"->"backend-gui-client.crt"[label="signs"]
    "backend.ca.crt"->"backend-privileged-client.crt"[label="signs"]
    "backend.ca.crt"->"backend-network-client.crt"[label="signs"]
    "privileged.ca.crt"->"privileged-backend-client.crt"[label="signs"]
    "network.ca.crt"->"network-backend-client.crt"[label="signs"]

    "GUIDI"->"backend-gui-client.crt"[label="uses"]
    "NetworkDI"->"backend-network-client.crt"[label="uses"]
    "PrivilegedDI"->"backend-privileged-client.crt"[label="uses"]
    "BackendDI"->"privileged-backend-client.crt"[label="uses"]
    "BackendDI"->"network-backend-client.crt"[label="uses"]

    "backend-gui-client.crt"->"BackendService"
    "backend-network-client.crt"->"BackendService"
    "backend-privileged-client.crt"->"BackendService"

    "privileged-backend-client.crt"->"PrivilegedService"
    "network-backend-client.crt"->"NetworkService"
}
Attack scenarios

A user application may be used to shutdown the OpenVPN connection exposing all network traffic of applications that rely on the channel.

This scenario should be impossible as long as the user-application does not kill the pid of a involved process. The REST services are secured using client-certificates.

A malicious application may read the secrets passed to the privileged application by recording the local traffic.

This scenario should be impossible as the communication between the processes is encrypted.

A real risk assessment should be executed in a later state of development.

Management server

A class util.ipc.server is used by the applications that expect clients to connect. It is used to setup the REST endpoints and accept connections using existing certificates. The server may contain dispatcher ‘links’ that are using the same ioloop to communicate (eg BackendService and Backend-Privileged-Client in one process). The server logic may use the dispatchers to pass messages. The server has no app-specific overload and may be used as in this example::

app = server(options.host, options.port, 'certificate-prefix')
app.set_owner(backend_dispatcher(backend_host, backend_port))
app.set_network(None)
app.set_privileged(None)
app.start([
    (r"/configuration", configuration_controller),
    ...
    ])
sys.exit(app.exec_())
Management client

A class util.ipc.service is used by the applications that connect to a util.ipc.server. It is used to setup the connection and serve as a base- class for specialized clients that expose a server-specific interface to the business logic. The base implementation wraps the GET/POST HttpClients with their SSL setup to convenient functions

A management client may be implemented like this::

class some_dispatcher(service):

    def __init__(self, host, port):
        service.__init__(
            self, host, port,
            'server-certificate-prefix', 'client-certificate-prefix')

    def handle_server_response(self, response):
        print 'server response', response

    def do_something_on_server(self):
        self.post('endpoint/on/server', '{}', self.handle_server_response)

The application may now use the management client as this::

remote = some_dispatcher(backend_host, backend_port)
remote.do_something_on_server()

The application needs to run remote.exec_() (blocking, eg in a thread) at some point or the requests are never dispatched to the server.

Design System

The lack of design consistency is one of the biggest problems in product development. A design system is the right solution to prevent inconsistencies.

The design system isn’t a standard carved in stone, nor is it a one-time deliverable. It’s the heart of your product development process, not an artifact of the process.

This document is not the style guide that describes ‘the colors’ or the roundness of the application, this document is about the consistency of the application.

The process to use this document is relatively simple:

# Compare pieces of the interface with their representation in code and
identify logical chunks.
# List all the pieces of the interface in categories derived from the code
environment. Add additional suggestions of categories where necessary.
# Take notes of any inconsistencies between elements. If possible list classes
and files for future reference.

This document is based on the great description of @marcintreder The Minimum Viable Design System.

The Minimum Viable Design System: https://medium.com/@marcintreder/the-minimum-viable-design-system-75ca1806848a

Building Blocks

The smallest, most general and most reusable pieces of the interface such as colors, typographic scales, grid definitions or icons.

Interface Inventory
Main Window

The Main Window is a list of accounts ordered in a hierarchy. It contains the main menu, the account list with a toolbar, a multi-purpose Create/Connect Button and a statusbar.

The Main Window is placed at the right side of the display by default and saves the location on shutdown.

Account List

The account list allows to connect, group, order and view the configured account items and account groups. During drag&drop indicators are shown where the drop will occur. The indicator is a transparent (50%) 20px height red (#FF0000) rectangle on the top or bottom of the account item.

Account Item

A account item has multiple states:

Disconnected (default): no color background Connecting: yellow (#FFB353) color background Connected: green (#008A48) color background Failed: red (#FF7F53) color background Disabled: blue (#055E97) foreground color

All items have a active (selected) state and a hovering state as well.

The active, not connected item gets the background color #055E97. Connecting and Connected use the inactive background color and set the left border to #055E97. The Failed state uses #FF4100 when active.

Account Group

Account groups show the Account items that are in it by adding a margin for all account items. When the group is active, a 4px solid blue (#044F7F) border around the group with a rounded (2px) corner is added. The name of the group is displayed in bold letters when the group is active.

Statusbar

The Statusbar is a mainwindow statusbar aligned to the right. It displays labels (copyright and version) a optional count of connections and a heartbeat indicator icon (connected green lines for heartbeat ok, only red dots for heartbeat failure)

Dialogs

Dialogs are displayed at the side of the main window with the most space (left when the main window is positioned on the right side of the display, right when the mainwindow is positioned on the left side of the display.

Dialogs have one or more actions at their bottom right that confirm/close the dialog contents.

Dialogs follow the Gnome Human Interface Guidelines:

Visual elements are organized from top-to-bottom and left-to-right. Bottom-right: ok/finish/continue, Bottom-left:cancel/back

The elements in the dialog should have 10px margin to the window border and avoid more margins when they are nested.

Gnome Human Interface Guidelines: https://developer.gnome.org/hig/stable/

Sub Dialogs

Dialogs opened from Dialogs are placed relative to the main window and will appear over the calling dialog. They need the user to confirm or cancel the input.

Confirm / Alert Dialogs

Use Confirm (Yes/No) dialogs for destructive actions Use Alert Dialogs (Ok) for messages to the user for actions that cannot be represented in another way.

Tabs

Tabs are the base elements for complex dialogs and group the contents in multiple panes. Tabs are always displayed on top with a descriptive text label. Tabs are not nested.

The only exception is the About dialog that uses tabs to display the various plugins and their text and optional sub-tabs after the main about text.

The tab contents have 10px margin to the widget border and avoid more margins when they are nested.

Pushbutton

Push Buttons have a general width of 120px.

Togglebutton

Toggle Buttons have a small icon that indicates the toggled state on the right side, no border and no outline. They have a increased left margin of 30px.

Checkbox

Checkboxes are displayed left of their buddy label.

Combobox

Comboboxes or Dropdown lists are displayed right of their label. The label is displayed in bold. The label is aligned right.

Lineedit

Lineedits are displayed right of their label. The label is displayed in bold

Key-Values Table
  • Lines are even-odd colored
Listview

In dialog listviews are operatingsystem styled. The one exception is the logviewer listview as it displays multiline elements and colors the entries depending to the loglevel.

Toolbars

Toolbars are displayed over the widget they control.

Lineedit in toolbars

Lineedits that let the user input a filter apply the filter while typing. The filter resets, when the data-background changes or a dedicated Clear Pushbutton is pressed. Filtering lineedits are above the items that are filtered.

Unconfirmed Storage

When the user inputs data, the change is commited without confirmation. This method is used in preferences and with the account list drag&drop.

Confirmed Storage

When the user inputs data, the user has to confirm (ok, create) or cancel the data. This method is used in account settings.

XXX we need to decide on one to have consistent experience.

Writing Style

Text plays an important role in user interfaces. Take the time to ensure that any text you use is clearly written and easy to understand.

  • Text should adopt a neutral tone and speak from the point of view of the product. Pronouns like “you” or “my” should therefore be avoided wherever possible. However, if they are unavoidable “your” is preferable to “my”.

  • Sentences should not be constructed from text in several controls, and each label should be treated as being self-contained. Sentences that run from one control to another will often not make sense when translated into other languages.

  • Header capitalization should be used for any headings, including header bar headings and page, tab and menu titles. It should also be used for short control labels that do not normally form proper sentences, such as button labels, switch labels and menu items. * Capitalize the first letter of: * All words with four or more letters. * Verbs of any length, such as “Be”, “Are”, “Is”, “See” and “Add”. * The first and last word. * Hyphenated words; for example: “Self-Test” or “Post-Install”.

    For example: “Create a Document”, “Find and Replace”, “Document Cannot Be Found”. Messages are in 3rd person and avoid direct user contact. (“After entering username” instead of “After you enter the username”)

Grid / Border Alignment
  • An alignment point is an imaginary vertical or horizontal line through your window that touches the edge of one or more labels or controls in the window. Minimize the number of these - the fewer there are, the cleaner and simpler your layout will appear, and the easier it will be for people to understand.
  • Align content and controls in your layout exactly. The eye is very sensitive to aligned and unaligned objects. If visual elements do not line up, it will be hard for someone to scan them. Elements that do not quite line up will be distracting.

Organize visual elements from top-to-bottom and left-to-right. This is the direction that people from western locales tend to read an interface, so that the items at the top-left will be encountered first. This ordering gives interfaces a hierarchy: those components that are viewed first are perceived to have priority over those that come after them. For this reason, you should place dominant controls above and to the left of the controls and content that they affect. Header bars are a key design pattern in this respect.

Labels
  • If possible, right-justify labels. This will avoid large gaps between labels and their associated controls. This type of right- justification is not possible if you have indented controls: here left- justification should be used instead.
Icons and Assets
Color Palette
Typographic Scales
UI Patterns

The pieces of the interface directly used to build experiences.

Templates

Core files (such as variables, icon font definitions etc.) to unify styling.

Modules

Full functionalities built out of components (e.g. toolbar, search)

Components

Independent and repeatedly used pieces of the interface built out of elements (e.g. toolbar item, page loader)

Elements

The smallest individual pieces of the interface which act as building blocks for components (e.g. buttons, form fields…).

Rules

Guidelines for implementing and growing the design system.

Design Principles
Implementation Guidelines
Security

The application can be paranoid and does not trust the environment it is in. The separation of concerns in separate backends is just one of many constructs that ensure that the user is safe.

Certificates/HTTPS

Local certificate based communication to avoid other users eavedropping or interfering.

Model

All data passed between the backends is described in models with validation.

No Logs

Avoid traces on the system.

Preferences

Not encrypted because its 600 and credentials are not stored/stored in secure os keystore. Preferences may be core-preferences or preferences of a plugin.

Privileged / Unprivileged / Network / Backend

Implement logic at the right location thus making the models reusable.

Backend

All the procedural works are done by the backend and it delegates it to subprocesses. Therefore the backend has the complexity and logic in modules, classes and methods. The other applications only provide their part.

Gui

The GUI is a initial interface for the backend. It dictates on some points what the backend has to provide, but uses methods that can now be accessed by gtk, ios, java or ruby. We choose Qt (PySide) and see the advantages and the issues of that solution. The GUI focuses on advanced users, therefore it has some advanced controls that need explanation.

Editorial Guidelines

API-Backend

This document describes the components of the backend application.

Every backend request url has the following elements:

Path Description
module The module that is responsible for handling the request. Modules are “event”, “gui”, “log”, “net”, “priv”, “report”.
action The action that is called in the module. Those are module dependent. “connect”, “update”, “shutdown”. Actions are verbs
model A model that is requested or should be modified. The exposed models are module dependent, not all models are exposed.
Events

All events that are received from the subprocesses are signaled to the backend / connected process using a REST request from the sub process to the backend.

Dispatcher Event Structure:

POST /module/priv/event
{
    "id" : "uuid-for-event",
    "index": 0,
    "date": 0,
    "name": "name_of_the_event",
    "type": "event-type",
    "data": {
        "optional": "arguments"
    }
}
Attribute Description
index Always-increasing number to ensure correct order in logs and prevent out-of-order event execution.
date Milliseconds since 1970-01-01T00:00:00Z
args The events that can be dispatched may need to send more complex structured data to the backend. Therefore the structure is not defined here.

All events that are sent (eg gui/events) use the same structure.

Log Messages

All log-messages that are captured from the sub processes are signaled to the backend / connected process using a REST request from the sub process to the backend.

Log Message Structure:

POST /module/priv/log
{
    "id" : "uuid-for-event",
    "index": 0,
    "date": 0,
    "level": "debug | info | warning | error | critical",
    "module": "openvpn",
    "context": "/file/under/exection.py:L123"
    "message": "message that needs to be logged"
}

The backend stores the log messages in memory and may decide to write them on disk or dispose of old items.

Attribute Description
index Always-increasing number to ensure correct order in logs and prevent out-of-order event execution.
date Milliseconds since 1970-01-01T00:00:00Z
level Loglevel that can be filtered in the User Interface
context Location where the log message was triggered
module The origin of the log message
Modules

The backend API exposes the modules that are registered. The modules do interfere and interconnect.

Event

The following actions are supported by the Event Module.

Not yet implemented.

GUI

The API for the GUI is described in a separated document. Backend GUI API

Log

The following actions are supported by the Log Module.

Net

The following actions are supported by the Net Module.

Module

The “module” Request allows receiving the current state of all network requests.

XXX not defined yet

REST Request:

GET /module/net

REST Response:

{

}
Preferences

The API for the preferences is described in a separated document. Backend Preferences API

Privileged

The following actions are supported by the Privileged Module.

Module

The “module” model request is used to expose the expected privileged state.

XXX not defined

REST Request:

GET /module/priv

REST Response:

{}
Log

The “log” request is used to receive logging information from the privileged process.

REST Request:

POST /module/priv/log
{
    "id" : "uuid-for-event",
    "index": 0,
    "date": 0,
    "level": "debug | info | warning | error | critical",
    "origin": "openvpn",
    "message": "message that needs to be logged"
}

REST Response:

No Response

Errors:

* 400: Validation Error, Value Error (bad JSON)
Event

The “event” request is used to receive event information from the privileged process. Events trigger dispatcher that may fetch details from the privileged process.

REST Request:

POST /module/priv/event
{
    "index": 0,
    "date": 0,
    "origin": "openvpn",
    "type": "type of event",
    "name": "name of event"
}

REST Response:

{}

Errors:

* 400: Validation Error, Value Error (bad JSON)
Report

The following actions are supported by the Report Module.

Debug

To create a connection using “curl” first disable HTTPS and CHECK_SIGNATURE in netsplice.config.ipc, then start the privileged App (as root) and invoke the endpoints. The following code assumes that you have multiple terminals open, root access and bash as shell:

Setup Server::

sed -i '|HTTPS = True|HTTPS = False' \
    src/netsplice/config/ipc.py
sed -i '|CHECK_SIGNATURE = True|CHECK_SIGNATURE = False' \
    src/netsplice/config/ipc.py
python src/netsplice/NetsplicePrivilegedApp.py \
    --host localhost --port 10000
sed -i '|HTTPS = False|HTTPS = True' \
    src/netsplice/config/ipc.py
sed -i '|CHECK_SIGNATURE = True|CHECK_SIGNATURE = True' \
    src/netsplice/config/ipc.py

List all Accounts::

curl localhost:10000/module/preferences/accounts

Add Account with minimal configuration::

curl localhost:10000/module/preferences/accounts -X POST \
    --data '{"name":"name",' \
    '"enabled":true,"autostart":false,"configuration":"remote localhost",' \
    '"import_configuration":"remote localhost"}'

Delete Account::

curl localhost:10000/module/preferences/account/1 -X DELETE

The sourcecode contains a test_api_backend.sh that contains useful tests for the api with some basic checks for the correct HTTP code and content.

API-Backend-GUI

This document describes the components of the backend application that are provided for a GUI. This API is part of the Backend-API.

The following actions are supported by the GUI Module.

Module

The “module” model-request is used to get the current state of the backend suitable for presentation.

REST Request:

GET /module/gui

REST Response:

{
    "connections": [],
    "credentials": [],
    "log": [],
    "check_connection": [],
    "events": []
}
Events

Long-Polling endpoint that responds when the gui-model has changed or a event has been queued by the backend.

REST Request:

GET /module/gui/events

REST Response:

No Response, use Module Request.
Module Actions

Update starts an available update. Restarts the application and all connections when the update is complete. TBD

REST Request:

GET /module/gui/actions/update

REST Response:

No Response

Errors:

  • 404 No Update Available

Shutdown the Application and its child processes. During shutdown all active connections are disconnected in their required order. The backend will wait for 1 second (see shutdown_action_controller.py) before it will close its ioloop.

REST Request:

POST /module/gui/actions/shutdown

REST Response:

No Response

Errors:

  • 403 Shutdown already in progress.
Log

The “log” request is used to fetch the complete or a filtered list of the logs accessible to the backend.

REST Request:

GET /module/gui/logs[?connection_id=uuid]

REST Response:

[
    {
        "index": "number-unique",
        "date": "DATETYPE",
        "level": "string: debug|info|warning|error|critical",
        "origin": "string",
        "message": "unsafe-string",
        "extra": "account and connection id",
        "formated_message": "message with code location",
        "code_location": "file and line"
    }
]

The “log” request allows to filter the log by adding an optional connection_id.

The “log” request allows to push log entries from the GUI to the backend where the log-entry will be stored and can be processed and filtered.

REST Request:

POST /module/gui/logs
    {
        "index": "number-unique",
        "date": "DATETYPE",
        "level": "string: debug|info|warning|error|critical",
        "origin": "string",
        "message": "unsafe-string",
        "extra": "account and connection id",
        "formated_message": "message with code location",
        "code_location": "file and line"
    }

REST Response:

None

Errors:

* 400: Validation Error, Value Error (bad JSON)

To delete all log entries, the GUI can emit a DELETE request. This clears all stored log entries in the backend.

REST Request:

DELETE /module/gui/logs

REST Response:

None
Accounts

Accounts can be used in the gui module to act on previously configured preferences.

Check account

Check an account configuration without persisting. A connection will be setup and the gui-model will update the check_connection instance.

REST Request:

POST /module/gui/accounts/check
{
    "type": "OpenVPN | SSH | Tor | Tinc | Wireguard",
    "configuration": "user-string",
}

REST Response:

Empty on Success

Errors:

* 400: Validation Error, Value Error (bad JSON)

To cancel an account-check the DELETE method is used on the same endpoint. The check connection will be disconnected and removed from the privileged process and the associated log messages will be cleared.

REST Request:

DELETE /module/gui/accounts/check

REST Response:

Empty on Success

Errors:

* 404: No check was active
Connect group

Connect all accounts for the given group in a predefined order. The order is evaluated and then executed. The backend will signal changes to the state of the connections/accounts using the events interface.

REST Request:

POST /module/gui/groups/<group_id>/connect
{
}

REST Response:

Empty on Success

Errors:

  • 400 ValidationError
  • 404 NotFoundError group_id not found
Disconnect group

Disconnect all accounts for the given group in a predefined order. The order is evaluated and then executed. The backend will signal changes to the state of the connections/accounts using the events interface.

REST Request:

POST /module/gui/groups/<group_id>/disconnect
{
}

REST Response:

Empty on Success

Errors:

  • 400 ValidationError
  • 404 NotFoundError group_id not found
Connect

Connect specific account. Accounts that the given account depends on will be connected before the selected account. The state change of the connection/account will be signaled using the events interface.

REST Request:

POST /module/gui/accounts/<account_id>/connect

REST Response:

Empty on Success

Errors:

  • 400 ValidationError
  • 403 AlreadyConnectedError
  • 404 NotFoundError account_id not found
Disconnect

Disconnect specific account. Accounts that depend on the given account will be disconnected before disconnecting. TBD

REST Request:

POST /module/gui/accounts/<account_id>/disconnect

REST Response:

Empty on Success

Errors:

  • 400 ValidationError
  • 403 NotConnectedError
  • 404 NotFoundError account_id not found
Reconnect

Reconnect specific Account. The OpenVPN instance will be requested to reconnect the connection.

REST Request:

POST /module/gui/accounts/<account_id>/reconnect

REST Response:

Empty on Success

Errors:

  • 400 ValidationError
  • 403 NotConnectedError
  • 404 NotFoundError account_id not found
Reset

Reset specific account. On connection failure, the account still has a connection that contains the error reasons. To reset those reasons and remove the connection instance reset is used.

REST Request:

POST /module/gui/accounts/<account_id>/reset

REST Response:

Empty on Success

Errors:

  • 400 ValidationError, ValueError
  • 403 ConnectedError
  • 404 NotFoundError account_id not found
Credential

When the backend requested a credential, this endpoint is used to transport the users data. A POST request with a credential JSON is used to respond, a DELETE request signals a user-cancelation that leads to the cancelation of the process that requested the credential.

REST Request:

POST /module/gui/credential
{
    "account_id": "account-id-uuid",
    "username": "user-provided-string-value",
    "password": "user-provided-string-value",
    "store_password": "never|session|keystore|local"
}

REST Response:

Empty on Success

Errors:

  • 400 ValidationError
  • 404 NotFoundError account_id not found

When the backend requests a credential, the user might decide not to enter the information. With the DELETE request the backend will be informed that the current process (eg connect list) has to be canceled.

REST Request:

DELETE /module/gui/credential

REST Response:

Empty on Success

API-Backend-Preferences

This document describes the components of the backend application that cover the preference retrieval and setting. This API is part of the Backend-API.

The “preferences” model request allows to receive and update application settings.

REST Request:

GET /module/preferences

REST Response:

{
"groups": [],
"grouped_accounts": [],
"accounts": [],
"ui": {
    "start_hidden": "boolean",
    "start_on_boot": "boolean",
    "show_welcome_on_start": "boolean",
    "theme": "theme-name",
    "locale": "locale-name",
    "loglevel": "debug | info | warning | errors"
    }
}
Events

Long polling events request to get notified when preferences change.

REST Request:

GET /module/preferences/events

REST Response:

No Response
Account

The “account” model requests exposes the persistent accounts that Netsplice manages.

Lists the available accounts or a specific account.

REST Request:

GET /module/preferences/accounts[/account_id]

REST Response for /module/preferences/accounts:

[
    {
        "id": "account-uuid",
        "name": "user-string",
        "type": "OpenVPN | Tor | Tinc | Wireguard",
        "configuration": "user-string",
        "import_configuration": "user-string",
        "enabled": "boolean",
        "autostart": "boolean"
    }
]

REST Response for /module/preferences/accounts/<account_id>:

{
    "id": "account-uuid",
    "name": "user-string",
    "type": "OpenVPN | Tor | Tinc | Wireguard",
    "configuration": "user-string",
    "import_configuration": "user-string",
    "enabled": "boolean",
    "autostart": "boolean"
}

Create an account with the given properties. The backend will persist the account.

REST Request:

POST /module/preferences/accounts
{
    "name": "user-string",
    "type": "OpenVPN | Tor | Tinc | Wireguard",
    "configuration": "user-string",
    "import_configuration": "user-string",
    "enabled": "boolean",
    "autostart": "boolean"
}

REST Response:

{
    "id": "{new-account-uuid}"
}

Errors:

  • 400: Validation Error, Value Error (bad JSON)

Update an existing account with the given properties. The account must not be connected.

REST Request:

PUT /module/preferences/accounts
{
    "id": "account-uuid"
    "name": "user-string",
    "configuration": "user-string",
    "import_configuration": "user-string",
    "enabled": "boolean",
    "autostart": "boolean"
}

REST Response:

{
    "id": "{account-uuid}"
}

Errors:

  • 400: Validation Error, Value Error (bad JSON)
  • 403: ConnectedError: Account is currently connected and cannot be changed

Delete an existing account.

REST Request:

DELETE /module/preferences/accounts/<account_id>

REST Response:

{
    "id": "{deleted-account-uuid}"
}

Errors:

  • 400: Validation Error, Value Error (bad JSON)
  • 404: NotFound Error: account_id not available
  • 403: ConnectedError: Account is currently connected and cannot be deleted
Backend and UI

Backend preferences are configurations that are influencing the behavior of the backend. UI preferences are configurations that are considered of cosmetic value like theme or confirmed dialogs. Those preferences are accessed using the value and complex controllers to allow simple generic extension of preference values.

Receive:

GET /module/preferences/complex/backend
GET /module/preferences/complex/ui

Update:

PUT /module/preferences/complex/backend
{
"complete": "backend_structure"
}
PUT /module/preferences/complex/ui
{
"complete": "ui_structure"
}
PUT /module/preferences/value/backend/max_connect_time
{
"value": 30
}
PUT /module/preferences/value/ui/start_hidden
{
"value": true
}

Backend Structure:

{
"max_connect_time": "integer",
"max_shutdown_time": "integer",
"ipc_certificate_valid_days": "integer",
"default_openvpn_version": "v1.2.3",
"graphical_sudo_application": "[/path/sudo/application]",
"store_password_default": "never or keystore or session"
}

UI Structure:

{
"shortcut_selected_rename": "F2",
"show_log_on_connection_failure": true,
"show_welcome_on_start": false,
"locale": "None",
"shortcut_show_logs": "ctrl+l",
"close_to_systray": false,
"logviewer_text_lines_per_item": 3,
"start_on_boot": false,
"statusbar_show_version": true,
"theme": "default",
"shortcut_selected_disconnect": "ctrl+d",
"shortcut_connect_all": "ctrl+g",
"statusbar_show_connections": false,
"shortcut_selected_connect": "ctrl+c",
"shortcut_help": "F1",
"statusbar_show_heartbeat": true,
"systray_animate_icon": true,
"shortcut_selected_edit": "F4",
"max_logviewer_items": 5000,
"start_hidden": false,
"shortcut_show_preferences": "ctrl+,",
"start_drag_distance": -1,
"logviewer_date": "timeago",
"systray_show_notifications": true,
"shortcut_create_connection": "ctrl+n",
"shortcut_quit": "ctrl+q",
"statusbar_show_product_owner": true
}
Complex preference

Sometimes a setting has a complex structure that needs the same access method as the value types. The rest interface is slightly different to the value model and requires to provide a valid structure of the JSON PUT.

REST Request:

GET /module/preferences/complex/<collection>/<name>

REST Response:

{
"complex": ["structure"]
}

Errors:

  • 400: Validation Error, Value Error (bad JSON / wrong type for value)
  • 404: NotFound Error: name is not a valid backend preference or name is not a marshalable.

Update a complex attribute.

REST Request:

PUT /module/preferences/complex/<collection>/<name>
{
"complex": ["structure"]
}

REST Response:

{
"complex": ["structure"]
}
Groups

Accounts may be grouped and ordered in those groups. The groups define the available groupings and grouped_accounts define how an account is associated to the group.

Get the list of available groups

REST Request:

GET /module/preferences/groups

REST Response:

[
    {
    "id": "group-id",
    "name": "name of group",
    "parent": "parent-group-id",
    "weight": "positive-integer"
    }
]

Get the information on the given group_id.

REST Request:

GET /module/preferences/groups/<group_id>

REST Response:

{
"id": "group-id",
"name": "name of group",
"parent": "parent-group-id",
"weight": "positive-integer"
}

Errors:

  • 400: Validation Error: group_id is not valid.
  • 404: NotFound Error: given group-id not found.

Create a new Group.

REST Request:

POST /module/preferences/groups
{
"name": "name of group",
"parent": "parent-group-id",
"weight": "positive-integer"
}

REST Response:

{
"id": "group-id"
}

Errors:

  • 400: Validation Error: a value is not valid.
  • 404: NotFound Error: given parent-group-id not found.

Update the information on the given group_id.

REST Request:

PUT /module/preferences/groups
{
"id": "group-id",
"name": "name of group",
"parent": "parent-group-id",
"weight": "positive-integer",
"collapsed": boolean
}

REST Response:

{
"id": "group-id"
}

Errors:

  • 400: Validation Error: group_id is not valid.
  • 403: NotAllowed Error: parent-group-id would cause circular or the group_id is immutable.
  • 404: NotFound Error: given group-id or parent-group-id not found.

Delete the given Group

REST Request:

DELETE /module/preferences/groups/<group_id>

REST Response:

{
"id": "group-id"
}

Errors:

  • 400: Validation Error: group_id is not valid.
  • 403: NotAllowed Error: group has children and cannot be deleted or the group is immutable.
  • 404: NotFound Error: given group-id not found.
Grouped Accounts

Grouped Accounts are the user-order of accounts in groups. Grouped accounts are derived from the accounts collection. They are created automatically with a new account and associated to the global group.

Get the list of available grouped_accounts.

REST Request:

GET /module/preferences/grouped_accounts

REST Response:

[
    {
    "id": "account-id",
    "name": "name of account",
    "group_id": "parent-group-id",
    "type": "account_type",
    "weight": "positive-integer"
    }
]

Get the a single grouped_account.

REST Request:

GET /module/preferences/grouped_accounts/<account_id>

REST Response:

{
"id": "account-id",
"name": "name of account",
"group_id": "parent-group-id",
"type": "account_type",
"weight": "positive-integer"
}

Errors:

  • 400: Validation Error: account_id is not valid.
  • 404: NotFound Error: given group-id or account_id not found.

Update a grouped account.

REST Request:

PUT /module/preferences/grouped_accounts/<account_id>
{
"id": "account-id",
"group_id": "parent-group-id",
"weight": "positive-integer"
}

REST Response:

{
"id": "account-id",
"group_id": "parent-group-id",
"weight": "positive-integer"
}

Errors:

  • 400: Validation Error: account_id is not valid.
  • 404: NotFound Error: given group-id or account_id not found.
Overrides

Override preferences are configurations that are considered before any connection is setup, allowing the user to specify default encryption expectations. Overrides are indexed by name & type and have a single value.

Get the value of a specific override value.

REST Request:

GET /module/preferences/overrides/<name>/<type>

REST Response:

{
"value": "anything"
}

Errors:

  • 400: Validation Error, Value Error bad name & type
  • 404: NotFound Error: name & type is not a valid override

Get the all override preferences.

REST Request:

GET /module/preferences/overrides

REST Response:

{
"config": [{
    "name": "remote",
    "type": "OpenVPN",
    "value": "localhost 1111"
    }],
"enabled": true
}

Create the value of a specific override name & type.

REST Request:

POST /module/preferences/override/<name>/<type>
{
"value": "new_value"
}

REST Response:

{
"value": "new_value"
}

Errors:

  • 400: Validation Error, Value Error bad name & type
  • 400: Not Unique Error name & type already existing
  • 404: NotFound Error: name & type is not a valid override

Update the value of a specific override name & type.

REST Request:

PUT /module/preferences/override/<name>/<type>
{
"value": "new_value"
}

REST Response:

{
"value": "new_value"
}

Errors:

  • 400: Validation Error, Value Error bad name & type
  • 404: NotFound Error: name & type is not a valid override

Remove the value of a specific override name & type combination.

REST Request:

DELETE /module/preferences/override/<name>/<type>

REST Response:

{
"name": "<name>",
"type": "<type>"
}

Errors:

  • 400: Validation Error, Value Error bad name & type
  • 404: NotFound Error: name & type is not a valid override
Plugins

Plugin Preferences allow the “external” components to register additional fields that can be modified by the user and accessed in the components.

Get preferences for a plugin. This is a readonly endpoint. To change attributes the attribute name needs to be provided in the url.

REST Request:

GET /module/preferences/plugin/<plugin_name>

REST Response:

{
"name": "plugin_name",
"field1": "field_value"
}

Errors:

  • 400: Validation Error, Value Error bad name
  • 404: NotFound Error: the given plugin name did not register a preference

Get a preference value for a attribute in the plugin preferences.

REST Request:

GET /module/preferences/plugin/<plugin_name>/attribute/<name>

REST Response:

{
"value": "current_value"
}

Errors:

  • 400: Validation Error, Value Error bad name & type
  • 404: NotFound Error: the given plugin name did not register a preference or
    the attribute did not exist.

Update a preference value for a attribute in the plugin preferences.

REST Request:

PUT /module/preferences/plugin/<plugin_name>/attribute/<name>
{
"value": "new_value"
}

REST Response:

{
"value": "new_value"
}

Errors:

  • 400: Validation Error, Value Error bad name & type
  • 404: NotFound Error: the given plugin name did not register a preference or
    the attribute did not exist.
Plugin Collection

Plugins are able to register for collections. This allows plugins to provide additional values for a collection instance. For example a account_item in the accounts collection adds the field ‘example’ which has to store different values for each account_item.

tcurl 200 “v2.” GET
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_1_id}/attribute/openvpn_version
tcurl 200 ‘“default_openvpn_version”’ GET
http://localhost:10000/module/preferences/plugin/openvpn
tcurl 200 ‘“openvpn_version”’ GET
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_1_id}
tcurl 200 ‘“default_signal”’ GET
http://localhost:10000/module/preferences/plugin/process_sniper
tcurl 200 ‘“kill_list”’ GET
http://localhost:10000/module/preferences/plugin/process_sniper/collection/accounts/instance_id/${account_1_id}
tcurl 400 “” PUT
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_1_id}/attribute/openvpn_version
tcurl 200 “v3.0.0” PUT
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_1_id}/attribute/openvpn_version “v3.0.0”
tcurl 200 “v4.0.0” PUT
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_2_id}/attribute/openvpn_version “v4.0.0”
tcurl 200 “v3.0.0” GET
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_1_id}/attribute/openvpn_version
tcurl 200 “v4.0.0” GET
http://localhost:10000/module/preferences/plugin/openvpn/collection/accounts/instance_id/${account_2_id}/attribute/openvpn_version
tcurl 200 ‘“kill_list”’ PUT
http://localhost:10000/module/preferences/plugin/process_sniper/collection/accounts/instance_id/${account_1_id} ‘{“name”:”process_sniper”,”version”:1,”kill_list”:[{“event”:”disconnect”, “commandline”: “firefox-beta”, “signal”:”SIGSTOP”}]}’
tcurl 200 ‘“firefox-beta”’ GET
http://localhost:10000/module/preferences/plugin/process_sniper/collection/accounts/instance_id/${account_1_id}

Get all preference values for a instance in the plugin collection preferences.

REST Request:

GET /module/preferences/plugin/<plugin_name>/collection/<collection_name>/instance_id/<instance_id>

REST Response:

{
"value": "current_value"
"name": "plugin_name"
}

Errors:

  • 400: Validation Error, Value Error bad plugin_name, collection_name or instance_id.
  • 404: NotFound Error: the given plugin name did not register a preference or the instance did not exist, the collection does not exist.

Get a preference value for a attribute in the plugin collection preferences.

REST Request:

GET /module/preferences/plugin/<plugin_name>/collection/<collection_name>/instance_id/<instance_id>/attribute/<name>

REST Response:

{
"value": "current_value"
}

Errors:

  • 400: Validation Error, Value Error bad plugin_name, collection_name or attribute_name
  • 404: NotFound Error: the given plugin name did not register a preference or the attribute did not exist, the collection does not exist or the attribute does not exist.

Update all preference values for a instance in the plugin collection preferences.

REST Request:

PUT /module/preferences/plugin/<plugin_name>/collection/<collection_name>/instance_id/<instance_id>
{
"value": "current_value"
"name": "plugin_name"
}

REST Response:

{
"value": "current_value"
"name": "plugin_name"
}

Errors:

  • 400: Validation Error, Value Error bad plugin_name, collection_name, instance_id or the given json does not match the plugins structure.
  • 404: NotFound Error: the given plugin name did not register a preference or the instance did not exist, the collection does not exist.

Update a preference value for a attribute in the plugin collection preferences.

REST Request:

PUT /module/preferences/plugin/<plugin_name>/collection/<collection_name>/instance_id/<instance_id>/attribute/<name>
{
"value": "new_value"
}

REST Response:

{
"value": "new_value"
}

Errors:

  • 400: Validation Error, Value Error bad plugin_name, collection_name, instance_id or the given json does not match the plugins structure.
  • 404: NotFound Error: the given plugin name did not register a preference or the attribute did not exist, the collection does not exist or the attribute does not exist.

API-Net

This document describes the components of the net application.

Download

Downloads are remote files stored to a local location. They are required to be https encrypted and the TLS fingerprint of the remote certificate is always checked before the transfer. Optionally a expected signature and its public key to verify the contents of the downloaded file. The remote file will be downloaded and after successful verification of the signature.

The network backend will notify the owning backend when the download progresses or runs in a error state using the event module.

REST Request:

POST /module/download
{
    "url": "url to download",
    "fingerprint": "TLS fingerprint of remote",
    "signature": "optional base64 encoded signature",
    "sign_key": "optional public key to verify signature",
    "destination": "local filesystem location for storage",
    "sha1sum": "optional sha1 checksum",
    "sha256sum": "optional sha256 checksum"
}

REST Response:

{
    "id": "id of download"
}

Cancel a active download by id or remove the reference for the download in the network backend.

REST Request:

DELETE /module/download/{id}

REST Response:

{
}

Errors:

* 400: Validation Error, Value Error (bad JSON)
* 404: Remote URL does not return
* 502: Remote Request Error (Bad Gateway)
* 504: Gateway timeout

Get the status of a download that was created with the POST request.

REST Request:

GET /module/download/{id}

REST Response:

{
    "url": "url of download",
    "start_date": "YYYY-MM-DDTHH:MM:SSZ",
    "complete": numeric_value(0-100),
    "bps": numeric_value,
    "errors": true/false,
    "error_message": null/"message from backend",
    "signature_ok": null/true/false,
    "checksum_ok": null/true/false
}

Errors:

* 400: Validation Error, Value Error (bad JSON)
* 404: Remote URL does not return
* 502: Remote Request Error (Bad Gateway)
* 504: Gateway timeout

API-Privileged

This document describes the components of the privileged application.

Every privileged request url has the following elements:

Path Description
module The module that is responsible for handling the request. Modules are “openvpn”, “systeminfo”.
action The action that is called in the module. Those are module dependent. “list-connections”, “connect”, “reconnect”, “disconnect”, “setup”, “delete”

Every privileged request url may have the following elements:

Parameter Description
{connection-id} unique-id for a connection that can be controlled
{process-id} unique-id for a process that can be controlled
Events

All events that are captured from the subprocesses are signaled to the backend / connected process using a REST request from the privileged process to the backend.

Dispatcher Event Structure:

POST /module/priv/event
{
    "id" : "uuid-for-event",
    "index": 0,
    "date": 0,
    "name": "name_of_the_event",
    "type": "event-type",
    "data": {
        "optional": "arguments"
    }
}
Attribute Description
index Always-increasing number to ensure correct order in logs and prevent out-of-order event execution.
date Milliseconds since 1970-01-01T00:00:00Z
args The events that can be dispatched may need to send more complex structured data to the backend. Therefore the structure is not defined here.
Log Messages

All log-messages that are captured from the privileged processes are signaled to the backend / connected process using a REST request from the privileged process to the backend.

Log Message Structure:

POST /module/priv/log
{
    "id" : "uuid-for-event",
    "index": 0,
    "date": 0,
    "level": "debug | info | warning | error | critical",
    "module": "openvpn",
    "context": "/file/under/exection.py:L123"
    "message": "message that needs to be logged"
}
Attribute Description
index Always-increasing number to ensure correct order in logs and prevent out-of-order event execution.
date Milliseconds since 1970-01-01T00:00:00Z
level Loglevel that can be filtered in the User Interface
context Location where the log message was triggered
module The origin of the log message
Modules

The privileged API exposes the modules that are registered. The modules do not interfere nor interconnect.

XXX should be /module/{module_name}/collectionplural/{collection_uuid}

OpenVPN Module

The following actions are supported by the OpenVPN Module.

List Connections

The “list-connections” action lists information about currently setup connections. For each connection, the response includes the connection id and whether or not it is currently connected.

REST Request:

GET /module/openvpn/connections

REST Response:

[
    {
       "id": "connection-id-1",
       "connected": false
    },
    {
       "id": "connection-id-2",
       "connected": true
    }
]
Connect

The “connect” action requests the privileged application to start a previously setup connection.

REST Request:

POST /module/openvpn/connections/<connection-id>/connect

REST Response:

{
   "id": "connection-id"
}

REST Errors

The connect action may return the following errors:

Code Description
404 Connection not found
403 Already connected / connecting
500 Generic failure to connect
502 OpenVPN communication failure
Delete

The “delete” action requests the privileged application to remove a previously setup connection. Delete only works for disconnected connections.

REST Request:

DELETE /module/openvpn/connections/<connection_id>

REST Response:

{
    "id": "connection-uuid"
}

REST Errors

The delete action may return the following errors:

Code Description
404 Connection not found
403 Still Connected cannot delete
500 Generic failure to delete
Disconnect

The “disconnect” action requests the privileged application to disconnect a previously connected connection. Disconnect only works for connected connections.

REST Request:

POST /module/openvpn/connections/<connection_id>/disconnect

REST Response:

{
    "id": "connection-id"
}

REST Errors

The Disconnect action may return the following errors:

Code Description
404 Connection not found
403 Not connected
500 Generic failure to disconnect
502 OpenVPN communication failure
Reconnect

The “reconnect” action requests the privileged application to reconnect a previously connected connection. Reconnect only works for connected connections.

REST Request:

POST /module/openvpn/connections/<connection_id>/reconnect

REST Response:

{
    "id": "connection-id"
}

REST Errors

The Reconnect action may return the following errors:

Code Description
404 Connection not found
403 Not connected
500 Generic failure to reconnect
502 OpenVPN communication failure
Setup

The “setup” action requests the privileged application to setup a connection with the required parameters. The privileged application stores the details for the connection in memory and allows future requests to connect or remove the configuration by name.

REST Request:

POST /module/openvpn/connections
{
    "config": "jsonencoded openvpn configuration"
}

REST Response:

{
   "id": "new-connection-id-to-be-used-in-future-requests"
}

REST Errors

The Setup action may return the following errors:

Code Description
400 Config is invalid
507 Insufficient storage to setup
Debug

To create a connection using “curl” first disable HTTPS and CHECK_SIGNATURE in netsplice.config.ipc, then start the privileged App (as root) and invoke the endpoints. The following code assumes that you have multiple terminals open, root access and bash as shell:

Setup Server::

sed -i '|HTTPS = True|HTTPS = False' src/netsplice/config/ipc.py
sed -i '|CHECK_SIGNATURE = True|CHECK_SIGNATURE = False' src/netsplice/config/ipc.py
python src/netsplice/NetsplicePrivilegedApp.py --host localhost --port 10000
sed -i '|HTTPS = False|HTTPS = True' src/netsplice/config/ipc.py
sed -i '|CHECK_SIGNATURE = True|CHECK_SIGNATURE = True' src/netsplice/config/ipc.py

Setup a Connection::

CONFIG={\"username\":\"\",\"password\":\"\",\"config\":\"$(echo $(cat /some/vpn/config/file|grep -v '^#' | sed 's|"|\\"|g' | sed 's|^|SSS|;s|$|EEE|g') | sed 's|EEE SSS|\\n|g;s|EEE$||;s|^SSS||')\"}

curl localhost:10000/module/openvpn/connections -X PUT --data "${CONFIG}" -v

Connect the setup-connection::

curl localhost:10000/module/openvpn/connections/1/connect -X POST -v

Disconnect the connected-connection::

curl localhost:10000/module/openvpn/connections/1/disconnect -X POST -v

Reconnect the connected-connection::

curl localhost:10000/module/openvpn/connections/1/reconnect -X POST -v

Delete the disconnected-connection::

curl localhost:10000/module/openvpn/connections/1 -X DELETE -v
Systeminfo Module

The following actions are supported by the Systeminfo Module.

Routes

The “routes” action requests the privileged application to extract information about the routes for reports.

REST Request:

GET /module/systeminfo/routes
{
}

REST Response:

[
    {
        "destination": "0.0.0.0",
        "gateway": "0.0.0.0",
        "genmask": "0.0.0.0",
        "flags": "UHG",
        "metric": 123,
        "ref": 0,
        "use": 0,
        "interface": "tun0"
    }
]
Interfaces

The “interfaces” action requests the privileged application to extract information about the interfaces configured on the host for reports.

REST Request:

GET /module/systeminfo/interfaces
{
}

REST Response:

[
    {
        "name": "tun0",
        "flags": ["UP", "POINTTOPOINT", "RUNNING", "NOARP", "MULTICAST"],
        "mtu": 1500,
        "inet": "172.16.17.18",
        "inet6": "fe80:ffff:ffff:ffff:ffff",
        "prefixlen": 64,
        "ether": "ff:ff:ff:ff:ff:ff"
        "netmask": "255.255.255.255",
        "destination": "172.16.17.1",
        "rx-packets": 1,
        "rx-bytes": 1500,
        "rx-errors": 0,
        "rx-dropped": 0,
        "rx-overruns": 0,
        "tx-packets": 1,
        "tx-bytes": 1500,
        "tx-errors": 0,
        "tx-dropped": 0,
        "tx-overruns": 0,
        "raw": "tun0: flags=...\n\tinet 172...\n\tinet6 ......"
    }
]

Marshaling

The REST interface requires the processes to communicate using JSON and pass more or less complex information. To generalize the method how those information is described a set of classes is implemented that all models need to inherit from. The marshaling base class implements methods to serialize and un-serialize JSON strings with validation and cleaning. There are two different types that are supported: Fields and Lists. Fields describe any basic types like integer, strings, boolean and allow the developer to specify a default value, if the field is required and multiple validators and cleaners. A List allows the developer to add multiple items of the same type and get them serialized as JSON array. A marshalable is also a field, so it is allowed to define nested structures. Marshalable defines the functions from_json and to_json that are used by controllers to create response messages. In addition a marshalable implements a commit function that can be used to signal other functions that the model has changed and can be evaluated.

A simple model can be defined like this::

class credential_item(marshalable):
    def __init__(self):
        marshalable.__init__(self)
        self.username = field()
        self.password = field()

This definition allows the user of the model to set and get values::

def model_creator():
    credential = credential_item()
    credential.username.set('john_doe')
    credential.password.set('pa55w0rd')
    credential.commit()

The credential instance can now be passed through the application and a consumer might access its values using::

def model_user(credential_model_instance):
    username = credential_model_instance.username.get()
    password = credential_model_instance.password.get()

All this could be easily done using a python-dictionary and would be even easier to write. So lets add a requirement to the username and the password::

class string_min_length_validator(validator):

    def __init__(self, min_length):
        validator(self)
        self.min_length = min_length

    def is_valid(self, value):
        if not isinstance(value, (basestring)):
            return False
        if len(value) < 8:
            return False
        return True


class email_validator(validator):

    def __init__(self, min_length):
        validator(self)
        self.min_length = min_length

    def is_valid(self, value):
        if not isinstance(value, (basestring)):
            return False
        if '@' not in value:
            return False
        return True


class not_too_simple_password_validator(validator):

    def __init__(self, min_length):
        validator(self)
        self.min_length = min_length

    def is_valid(self, value):
        if not isinstance(value, (basestring)):
            return False
        if 'qwertz' in value:
            return False
        return True


class credential_item(marshalable):

    def __init__(self):
        marshalable.__init__(self)
        self.username = field(
            validators=[email_validator()])
        self.password = field(
            validators=[
                string_min_length_validator(8),
                not_too_simple_password_validator()])

Using this definitions the developer ensures, that only values that are valid are stored in the model using set() and only values that are considered valid can be extracted using get() all other values will throw a ValidationError.

When using the JSON serialization and un-serialization functions to_json and from_json this rules also apply::

def signup_action(request):
    credential = credential_item()
    credential.from_json(request.body)
    db.create_user(credential.username.get(), credential.password.get())

now the valid POST will simply continue in the control-flow::

POST /signup
{
   "username": "john@doe.com",
   "password": "asdfghij"
}

while an invalid POST will raise an exception when the json is parsed::

POST /signup
{
    "username": "fred",
    "password": "qwertz"
}

Raises ValidationError(‘username is not valid’) that needs to be caught in the handler function and converted to a appropriate response code.

Model

digraph G {
  subgraph cluster_persistent {
      style=filled;
      color=lightgrey;
      label="persistent";
      Account[shape="box",label="Account\n+id\n+name\n+type\n+configuration\n+import_configuration\n+enabled\n+autostart"];
      GroupedAccount[shape="box",label="Grouped Account\n+group_id\n+type\n+name\n+weight\n+id"];
      Group[shape="box",label="Group\n+parent\n+collapsed\n+name\n+weight\n+id"];
      PreferencesP[shape="box",label="Preferences\n+autostart\n+autoconnect\n+save_connection_state\n+ui_language\n+account_hierarchy"];
      Override[shape="box",label="Override\n+enabled\n+[{name,type}]"];
  }
  subgraph cluster_transient {
      style=filled;
      color=lightgrey;
      label="transient";
      Event[shape="box",label="Event\n+index\n+date\n+origin\n+type\n+name\n+data"];
      LogItem[shape="box",label="Log Item\n+index\n+level\n+origin\n+message\n+formatted_message\n+code_location\n+extra"];
      LogItemExtra[shape="box",label="Log Item Extra\n+connection_id\n+account_id"];
      Connection[shape="box",label="Connection\n+id\n+account_id\n+type\n+connected\n+connecting\n+connect_failed\n+status\n+errors"];
      Credential[shape="box",label="Credential\n+username\n+password"];
      StatusItem[shape="box",label="Status Item\n+message\n+state"];
      StatusItemList[shape="box",label="Status Item List"];
      Log[shape="box",label="Log\n+logitems"];
      Backend[shape="box",label="Backend\n+network_active\n+privileged_active\n+started_on\n+log"];
      Gui[shape="box",label="GUI\n+connections\n+log_index\n+check_connection\n+events\n+credentials"];
      PreferencesT[shape="box",label="Preferences not Stored\n+credentials"];
  }

  PreferencesP -> GroupedAccount[label="loads"];
  PreferencesP -> Group[label="loads"];
  PreferencesP -> Account[label="loads"];
  GroupedAccount -> Account[label="references"];
  GroupedAccount -> Group[label="references"];

  PreferencesT -> Credential[label="holds"];
  Connection -> Account[label="references"]
  Connection -> Credential[label="load"]
  Connection -> StatusItemList[label="owns\nstatus and errors"];
  StatusItemList -> StatusItem;

  Log -> LogItem[label="owns"];
  LogItem -> LogItemExtra[label="owns"];

  Backend -> Connection[label="owns\ntransient"];
  Backend -> Log[label="owns\ntransient"];
  Backend -> Event[label="owns\ntransient"];


  Backend -> Preferences[label="owns\nloads"];
  Backend -> Gui[label="owns\ntransient"]
  Gui -> Connection[label="references"];
}
Account

Accounts contain the information how to create a connection. They are part of the preferences module. Accounts may be referenced using their id-field. Only the GroupedAccount loader will try to match accounts by name when they are not yet associated to a group. Accounts, Groups, GroupedAccounts are stored on disk every time the user changes their values.

digraph G {
  subgraph cluster_persistent {
      style=filled;
      color=lightgrey;
      label="Account";
      Account[shape="box",label="Account\n+id\n+name\n+type\n++configuration\n+import_configuration\n+enabled"];
      GroupedAccount[shape="box",label="Grouped Account\n+group_id\n+type\n+name\n+weight\n+id"];
      Group[shape="box",label="Group\n+parent\n+collapsed\n+name\n+weight\n+id"];
      Preferences[shape="box",label="Preferences\n+backend{}\n+ui{}"];
  }

  Preferences -> GroupedAccount[label="loads"];
  Preferences -> Group[label="loads"];
  Preferences -> Account[label="loads"];
  GroupedAccount -> Account[label="references"];
  GroupedAccount -> Group[label="references"];

}
Connection

Connections are the application information that tracks aggregated information about the state. Connections are updated by log messages and can be used to filter logs. Connections are not stored on disk. Connections have to reference a account that was used to setup the connection.

digraph G {
  subgraph cluster_transient {
      style=filled;
      color=lightgrey;
      label="Connection";
      Connection[shape="box",label="Connection\n+id\n+account_id\n+type\n+connected\n+connecting\n+connect_failed\n+status\n+errors"];
      StatusItem[shape="box",label="Status Item\n+message\n+state"];
      StatusItemList[shape="box",label="Status Item List"];
  }

  Connection -> Account[label="references"]
  Connection -> StatusItemList[label="owns status"];
  Connection -> StatusItemList[label="owns errors"];
  StatusItemList -> StatusItem;
}
Credential

Credentials are username password combinations that are stored for a account. The storage can be controlled to Never (so they can only be used once by the application and have to be rerequested every time) Session (so the application does not rerequest once they have worked) or OS Keystore (persist them in the keystore of the OS). Credentials are identified by the account-id. Only some connections need credentials, therefore the specific connection implementation will inherit the connection.credential dispatcher if required.

digraph G {
  subgraph cluster_transient {
      style=filled;
      color=lightgrey;
      label="Transient";
      Credential[shape="box",label="Credential\n+username\n+password"];
      Preferences[shape="box",label="Preferences"];
  }
  subgraph cluster_persistent {
      style=filled;
      color=lightgrey;
      label="Persistent";
      Account[shape="box",label="Account\n+id\n...."];
  }

  Credential -> Account[label="references by id"];
  Connection -> Credential[label="loads"];
  Preferences -> Credential[label="holds"];
}
Override

Overrides are persistent overloads that force certain keywords to the configuration. Overrides can be enabled and disabled and are identified by type and name.

Modules

Netsplice should be extensible and with this requirement its core parts are already modularized to define the pattern for future extensions. Each application consists of multiple modules that register REST endpoints, define module-models and implement dispatchers that do the module-specific work. A modularized structure looks like this::

src
 |- module1
 |   |- model
 |   |   |- request
 |   |   |   |- requestable_value_type1.py
 |   |   |   \- requestable_value_type2.py
 |   |   |- response
 |   |   |   |- value_type_for_return1.py
 |   |   |   \- value_type_for_return2.py
 |   |   |- validator
 |   |   |   \- validator_for_requestable_value.py
 |   |   |- __init__.py
 |   |   |- intern_module_model1.py
 |   |   \- intern_module_model2.py
 |   |- views
 |   |- __init__.py
 |   |- config.py
 |   |- intern_model_controller.py
 |   \- intern_model_implementation.py
 |- module2
 \- app.py

As you can see this cleanly separates the classes that are required to create a module that acts as a REST endpoint.

The module1/__init__.py will instanciate a model, endpoints and name value that can be accessed by users of the module. The app.py registers the module before it starts the REST ManagementServer. It is required to define endpoints and model::

from netsplice.util.ipc.route import get_module_route
from netsplice.backend.gui.model import model as module_model

name = "gui"

endpoints = get_module_route(
    'netsplice.backend',
    [
        (r'/module/gui', 'module'),
        (r'/module/gui/log', 'log'),
        (r'/module/gui/account', 'account'),
        (r'/module/gui/account/check', 'account_check'),
        (r'/module/gui/action/connect', 'connect_action'),
        (r'/module/gui/action/update', 'update_action'),
        (r'/module/gui/action/shutdown', 'shutdown_action'),
        (r'/module/gui/events', 'events'),
    ])

model = module_model()

As a module is used by other components of the application the interface that is accessed by those components should be very limited and should be implemented in module controllers that are triggered by events.

Application Modules

Netsplice is composed from multiple applications. Their layout is defined in the same way as a module. They are responsible to register their modules before the app is started::

app = application()

# Add Modules

app.add_module(mainwindow_module)
app.add_module(preferences_module)
app.add_module(logviewer_module)
app.add_module(systray_module)

app.start(get_route('netsplice.gui'))
sys.exit(app.exec_())
Models, Dispatcher, Events

Most of the code looks like boilerplate for constructs that could be expressed without creating so many files and functions. When implementing features inspect the code of the sibling implementations to decide if the code that is added handles generic passing of values or business logic. For example it is easy to add a persistent model value before it is written to the REST response, but this modification is actually a model-conversion that a dispatcher should decide. This requires you to define an additional function, name it and then use it in the controller.

Models

There are persistent and transient models. The transient models are used for data retrieval and transformation while the persistent models modify values. This is not a hard requirement. There are some core application models that can be inherited, e.g. logs and events and non-specific validators like boolean.

The module-model defines the interface for the module components that could be acted on. The module registers its event_loop on the model_changed/commit event and runs dispatchers based on the change. The attributes of the module-model are other models. Those definitions are separated from the models that can be requested and that will be responded.

Request and Response Models

To ensure that only specified information is transmitted between the processes a series of models that define requests and responses has to be defined. This allows the components to be developed independently and always specify the interfaces they expect. On the other hand a developer needs to implement various models of the same type in various locations to pass messages. A Request model should only define fields that are allowed to be passed into the application using JSON or request arguments. A Response Model should only define fields that are allowed to be returned as the response of a request. A Controller should use request and response models like this::

def post(self):
    request_model = username_password_model()
    request_model.from_json(self.request.body)
    # do something with request_model.username.get(), e.g. pass it to a module dispatcher
    account_id = self.application.get_module('register').create_account(
        request_model.username.get(), request_model.password.get())
    response_model = account_id_model()
    response_model.id.set(account_id)
    self.write(response_model.to_json())
    self.finish()

With a good documentation on both the request-model and the response model a user of the API can easily understand what values should be passed, how they are validated and what values the API will return.

Dispatcher

Business Logic implementations that accept values and modify models based on the values. Dispatchers connect multiple modules and orchestrate their usage. They expect that all platform specific code is ‘hidden’ in functions, they trust the values they are provided and the values that are generated in dispatchers are considered ‘safe’ i.e. no type-checking and conversion, minimal error handling (everything throws and is caught on an other level). Dispatcher functions are considered async.

Events

Application components and Modules communicate using events. Those can be subscribed by a dispatcher.

Plugins

To allow the implementation of multiple types of VPN’s, Netsplice was designed to have plugins. The plugins are part of the source code and will not be loaded into a compiled package.

To implement a plugin it needs to be registered in the src/netsplice/plugins/__init__.py. Registered plugins will be loaded when the application starts.

Read more in the Plugin API documentation.

In general everything can be extended, as the current language is python. Some more likely locations have helper functions that allow the extensions to grow in a structured way:

  • netsplice.backend.connections.register_plugin
  • netsplice.backend.preferences.model.plugins.register_plugin
  • netsplice.[backend|gui|privileged].event_loop.register_plugin_module
  • netsplice.[backend|gui|privileged].event_loop.register_module
  • netsplice.gui.account_edit.register_account_type
  • netsplice.gui.register_account_type_pixmap

To add and activate a plugin the following methods are used:

  • netsplice.ipc.application.add_plugin
  • netsplice.ipc.application.register_plugin_components
Plugin Structure

The structure of a plugin in general should look like this::

src/netsplice
   |- plugins
   |   |- plugin1
   |   |   |- component1 (eg pure-rest-endpoint)
   |   |   |   |- plugin_module
   |   |   |   |   |- model...
   |   |   |   |   \- __init__.py
   |   |   |   \- __init__.py
   |   |   |- component2 (eg gui)
   |   |   |   |- control_module
   |   |   |   |   |- views
   |   |   |   |   |   \- control.ui
   |   |   |   |   |- Makefile
   |   |   |   |   |- plugin_model.py
   |   |   |   |   \- __init__.py
   |   |   |   |- model
   |   |   |   |   |- request...
   |   |   |   |   |- response...
   |   |   |   |   |- plugin_model.py
   |   |   |   |   \- __init__.py
   |   |   |   |- Makefile
   |   |   |   \- __init__.py
   |   |   |- config
   |   |   |   |- plugin_private_config.py
   |   |   |   \- __init__.py
   |   |   |- res
   |   |   |   |- private_plugin_resources
   |   |   |   \- __init__.py
   |   |   \- __init__.py
   |   |- plugin2 ...
   |   \- __init__.py
   |- core_components
   \- *App.py

As you can see this mirrors the core structure and allows the development of core components that are moved to a plugin once all the core interfaces have been established.

Resources

A plugin may contain resources in the res directory such as images, locales, certificates - anything that is not a py-config. The plugin is responsible to load all files by itself. The only exception are the qtresources that are compiled along with the core resources.

Add Plugins

Plugins are added by the main application during startup. The main application references the plugins for later registration of the required components. All plugins are added to all applications.

Register Plugins

When the core modules are loaded, the plugins may register to those modules.

The registration is done in a order that respects the plugin dependencies if they are defined. Plugin dependencies are defined by module references. Each App will try to import plugin components and the registration method allows KeywordError to occur.

During the registration process most of the environment is available. The only exception is that the QApplication is not yet started, therefore it is not possible to communicate with the user and create widgets. All widget plugins need support of the attached widgets.

Plugin API

Read the Plugin API to learn how to implement extensions to the Application.

Process Launcher Developer

The privileged scripts that are provided with the installation are examples for common tasks. The content of those scripts should be reviewed by the user as the commands are executed with root privileges. Some of them should be in the python application, exposed as api to avoid logic in bash scripts. When there are use-cases, please expose us so we can integrate them.

Return Codes

Netsplice acts on the return-codes. A return code of 0 is a success, a nonzero return code is an error. The application handles the following return-codes special:

0: success, do nothing 32: fatal disconnect, disconnect the connection that triggered the script and show the logwindow. 64: fatal, show the logwindow

In order to display a useful message in the logwindow, the text must be written to stderr.

Basic

The most basic script will look like this:

test-script.sh
#!/bin/bash

set > /tmp/test_script.set.txt
urxvt -c "less /tmp/test_script.set.txt"

The following environment is created by the IPv6 IPredator.se connections:

Connected
openvpn_common_name=eeyaivohngik.openvpn.ipredator.se
openvpn_config=/tmp/tmprpq5oV
openvpn_daemon=0
openvpn_daemon_log_redirect=0
openvpn_daemon_pid=29031
openvpn_daemon_start_time=1496919656
openvpn_dev=tunX
openvpn_dev_type=tun
openvpn_foreign_option_1='dhcp-option DOMAIN ipredator.se'
openvpn_foreign_option_2='dhcp-option DNS 46.246.46.46'
openvpn_foreign_option_3='dhcp-option DNS 194.132.32.23'
openvpn_ifconfig_broadcast=46.246.49.255
openvpn_ifconfig_ipv6_local=2001:67c:1350:105::d
openvpn_ifconfig_ipv6_netbits=64
openvpn_ifconfig_ipv6_remote=2001:67c:1350:105::1
openvpn_ifconfig_local=46.246.49.180
openvpn_ifconfig_netmask=255.255.255.0
openvpn_link_mtu=1561
openvpn_local_port_1=0
openvpn_local_port_2=0
openvpn_local_port_3=0
openvpn_proto_1=udp
openvpn_proto_2=udp
openvpn_proto_3=udp
openvpn_redirect_gateway=1
openvpn_remote_1=ipv6.openvpn.ipredator.se
openvpn_remote_2=ipv6.openvpn.ipredator.me
openvpn_remote_3=ipv6.openvpn.ipredator.es
openvpn_remote_port_1=1194
openvpn_remote_port_2=1194
openvpn_remote_port_3=1194
openvpn_route_gateway_1=192.168.5.1
openvpn_route_ipv6_gateway_1=2001:67c:1350:105::1
openvpn_route_ipv6_network_1=2000::/3
openvpn_route_net_gateway=192.168.5.1
openvpn_route_netmask_1=255.255.255.255
openvpn_route_network_1=46.246.49.130
openvpn_route_vpn_gateway=46.246.49.1
openvpn_script_context=init
openvpn_tls_digest_0=74:e0:a0:36:08:b5:c8:c9:47:09:38:97:4b:b6:f6:4f:43:4b:41:79
openvpn_tls_digest_1=48:dc:0b:05:18:8d:91:6c:16:54:29:1d:4b:8f:8c:b1:43:d6:3a:99
openvpn_tls_id_0='C=SE, ST=Bryggland, L=Oeldal, O=Royal Swedish Beer Squadron, CN=eeyaivohngik.openvpn.ipredator.se, emailAddress=hostmaster@ipredator.se'
openvpn_tls_id_1='C=SE, ST=Bryggland, L=Oeldal, O=Royal Swedish Beer Squadron, OU=Internetz, CN=Royal Swedish Beer Squadron CA, emailAddress=hostmaster@ipredator.se'
openvpn_tls_serial_0=3837
openvpn_tls_serial_1=12078339272342960243
openvpn_tls_serial_hex_0=0e:fd
openvpn_tls_serial_hex_1=a7:9e:e1:93:0c:a6:f8:73
openvpn_trusted_ip=46.246.49.130
openvpn_trusted_port=1194
openvpn_tun_mtu=1500
openvpn_untrusted_ip=46.246.49.130
openvpn_untrusted_port=1194
openvpn_verb=3
Disconnected
openvpn_common_name=eeyaivohngik.openvpn.ipredator.se
openvpn_config=/tmp/tmprpq5oV
openvpn_daemon=0
openvpn_daemon_log_redirect=0
openvpn_daemon_pid=29031
openvpn_daemon_start_time=1496919656
openvpn_dev=tunX
openvpn_dev_type=tun
openvpn_foreign_option_1='dhcp-option DOMAIN ipredator.se'
openvpn_foreign_option_2='dhcp-option DNS 46.246.46.46'
openvpn_foreign_option_3='dhcp-option DNS 194.132.32.23'
openvpn_ifconfig_broadcast=46.246.49.255
openvpn_ifconfig_ipv6_local=2001:67c:1350:105::d
openvpn_ifconfig_ipv6_netbits=64
openvpn_ifconfig_ipv6_remote=2001:67c:1350:105::1
openvpn_ifconfig_local=46.246.49.180
openvpn_ifconfig_netmask=255.255.255.0
openvpn_link_mtu=1561
openvpn_local_port_1=0
openvpn_local_port_2=0
openvpn_local_port_3=0
openvpn_proto_1=udp
openvpn_proto_2=udp
openvpn_proto_3=udp
openvpn_redirect_gateway=1
openvpn_remote_1=ipv6.openvpn.ipredator.se
openvpn_remote_2=ipv6.openvpn.ipredator.me
openvpn_remote_3=ipv6.openvpn.ipredator.es
openvpn_remote_port_1=1194
openvpn_remote_port_2=1194
openvpn_remote_port_3=1194
openvpn_route_gateway_1=192.168.5.1
openvpn_route_ipv6_gateway_1=2001:67c:1350:105::1
openvpn_route_ipv6_network_1=2000::/3
openvpn_route_net_gateway=192.168.5.1
openvpn_route_netmask_1=255.255.255.255
openvpn_route_network_1=46.246.49.130
openvpn_route_vpn_gateway=46.246.49.1
openvpn_script_context=init
openvpn_tls_digest_0=74:e0:a0:36:08:b5:c8:c9:47:09:38:97:4b:b6:f6:4f:43:4b:41:79
openvpn_tls_digest_1=48:dc:0b:05:18:8d:91:6c:16:54:29:1d:4b:8f:8c:b1:43:d6:3a:99
openvpn_tls_id_0='C=SE, ST=Bryggland, L=Oeldal, O=Royal Swedish Beer Squadron, CN=eeyaivohngik.openvpn.ipredator.se, emailAddress=hostmaster@ipredator.se'
openvpn_tls_id_1='C=SE, ST=Bryggland, L=Oeldal, O=Royal Swedish Beer Squadron, OU=Internetz, CN=Royal Swedish Beer Squadron CA, emailAddress=hostmaster@ipredator.se'
openvpn_tls_serial_0=3837
openvpn_tls_serial_1=12078339272342960243
openvpn_tls_serial_hex_0=0e:fd
openvpn_tls_serial_hex_1=a7:9e:e1:93:0c:a6:f8:73
openvpn_trusted_ip=46.246.49.130
openvpn_trusted_port=1194
openvpn_tun_mtu=1500
openvpn_untrusted_ip=46.246.49.130
openvpn_untrusted_port=1194
openvpn_verb=3
Advanced

The scripts in the process_launcher libexec directory are too complex for just four lines. Therefore a `.subroutines.inc.sh` is used to have common methods on solving common problems. They output of errors, return the correct exit codes and provide basic ip-address handling. When scripts are provided with a `.debug` file, this file is sourced before the script is executed when the `DEBUG` environment variable is nonempty. This allows to check the script with the expected (or unexpected) environment variables.

Setting up a development environment

This document describes how to setup a environment that allows debugging and patching the sources of the project.

Cloning the repo

Note

Stable releases are in master branch. Development code lives in develop branch.

git clone https://XXX/netsplice.git netsplice
cd netsplice
git checkout master
Base Dependencies

Netsplice depends on these base libraries:

  • python 2.7
  • qt4 libraries
  • openssl
  • openvpn
Debian

In debian-based systems, install the following packages:

$ apt-get install git python-dev python-setuptools \
python-virtualenv \
python-pip python-openssl build-essential openvpn make \
pyside-tools python-pyside
Working with virtualenv
Intro

Virtualenv is the Virtual Python Environment builder.

It is a tool to create isolated Python environments.

The basic problem being addressed is one of dependencies and versions, and indirectly permissions. Imagine the user has an application that needs version 1 of LibFoo, but another application requires version 2. How can the user use both these applications? When installing everything into /usr/lib/python2.7/site-packages (or whatever the platform’s standard location is), it’s easy to end up in a situation where applications are unintentionally upgraded that shouldn’t be upgraded.

Read more about it in the project documentation page.

Create and activate environment

Create a virtualenv in any directory:

$ mkdir ~/Virtualenvs
$ virtualenv --system-site-packages ~/Virtualenvs/netsplice
$ source ~/Virtualenvs/netsplice/bin/activate
(netsplice)$

Note the change in the prompt.

Install python dependencies

Install python dependencies with pip. If this is executed inside the working environment, they will be installed avoiding the need for administrative permissions:

(netsplice)$ pip install -r pkg/requirements.pip
(netsplice)$ pip install -r pkg/requirements-pkg.pip
Make resources

Compile the resource files:

(netsplice)$ make all

This step is to be repeated each time .ui translations or a .qrc file is changed.

Debugging

If everything went well, it should now be possible to run the application by invoking make run. When no window is displayed, or for more verbose output, change the config.flags and config.log values. Sometimes it is useful to see backtraces. GDB allows stepping when python is compiled with -ggdb and not stripped. gdb attach PID to a running process and use the py- prefixed statements. When you are using eclipse for sourcecode editing the integrated debugger is able to step through the code when the virtualenv binaries are used.

Usually it is sufficient to write unit tests for the location you want to investigate and print the values in the process.

Elevated binaries

The application detects that it is running in source mode. This is required because the production mode can run `pksudo NetsplicePrivilegedApp` and register the app in the installation process so the user can securely elevate. When you execute from source this would allow all python scripts running as root. Therefore a developer is requested to build the openvpn binary from source and place it in the config.LIBEXEC_BUILD location e.g var/build/openvpn/v2.4.0/sbin/openvpn and setuid root this executable. This way the python scripts can be run as user while the elevated tools get the required permissions.

Platform binaries

Sometimes issues can only be reproduced on a $clean_platform but not on the development machine. Use the pkg/debian-build makefiles to build installers with debug-flags enabled or logger-statements added. When you need to have short turnaround times, it is usually worth skipping the full clean build by docker-compose run --rm --entrypoint /bin/bash slaveXXX and run only parts of the /usr/local/bin/build.sh script or the makefiles. To understand what is going on also read Package Debugging and Package Build

Logs

Logs are not written to the disk by default. During startup it is possible that the logs are not displayed because the GUI cannot access the backend. In this case it is useful to force the logger to write logs to the disk: mkdir -p ~/.config/Netsplice/logs/FORCE_LOG_FILES.

Development Workflow

This section documents the workflow that the Netsplice team follows and expects for the contributions.

While reading this guide, have in mind the two rules of contributing code:

  • The first rule of code contribution is: Nobody will push unreviewed code to the mainline branches.
  • The second rule of code contribution is: Nobody will push unreviewed code to the mainline branches.
Code formatting

In one word: PEP8.

autopep8 allows some magic formatting.

Dependencies

When a new dependency is introduced, please add it under pkg/requirements or pkg/test-requirements as appropiate, under the proper module section.

Git flow

We are basing our workflow on what is described in A successful git branching model.

However, we use a slightly modified setup in which each developer maintains her own feature branch in her private repo. After a code review, this feature branch is rebased onto the authoritative integration branch.

A couple of tools that help to follow this process are git-flow and git-sweep.

Code review and merges into integration branch

All code ready to be merged into the integration branch is expected to:

  • Have tests
  • Have documentation
  • Pass existing tests: do nosetests. Rebasing against the current tip of the integration when possible is preferred in order to keep a clean history.
Using Github

Fork the repository on github. Always create a branch from master with some descriptive name the following name:

bug/some_descriptive_text

or

feature/some_descriptive_text

Work there, push it, and create a pull request against the develop branch in the main repository. Now wait until another developer comments or merges the work. Join on #netsplice-dev at freenode to get quick responses.

The code will get reviewed/discussed by someone else on the team. In case that the discussion requests some more changes, do the following:

git checkout <your branch>

Edit

Simple commit, this doesn’t need a good commit message:

git commit -avm "Fix"

This will help reorder the commits and squash them (so that the final commit list has good representative messages):

git rebase -i develop

Since now the local history is rewritten a force push is required:

git push <your remote> +<your branch>

This will update the pull request automatically, but it won’t notify us about the update again, so add a comment saying so, or re-ping the reviewer.

Other methods

Feel free to use any other methods like format-patch and mail or whatever method.

PySide Resource files

Compiling resource/ui files

Refresh resource/ui files every time an image or a resource/ui (.ui / .qc) is changed. From the root folder:

% make gui

qrc and the qss/less files are defined as dependencies. That allows to make run and automatically get the changes.

Theming with qss

With qss it is possible to change the colors and size of most of the properties of the application. The basic theming contains widget backgrounds and general colors for the following widgets:

  • QToolTip
  • QWidget
  • QLineEdit
  • QTextEdit
  • QTableView
  • QScrollBar
  • QPushButton
  • QListView
  • QMainWindow
  • QCheckBox
  • QComboBox
  • QMenuBar
  • QMenu

Some widgets have been developed to suit our purposes and therefore contain nonstandard selectors:

  • list_item has the property active to indicate that it is selected
  • account_list_item has the properties connected, connecting
  • account_group_item is a list_item
  • account_list
  • QPushButton#action_indicator is a special button to collapse account_list_items

Read more about styling QT with QSS Stylesheet Customizing and Stylesheet Reference.

Account List

The Accountlist is a QScrollArea with nested account_group_items and account_list_items. Their tree style display is achieved using the natural widget nesting and the margins of that list. Each Account list item should be considered with the properties ‘connecting’, ‘connected’ combined with ‘active’.

_images/theme-account-list.png
Account List Item

A account_list_item is child in a QVLayout and therefore displayed with vertical spacing. The account_list_item should not have margins on the right side. To modify the height of the account_list_item, a config value needs to be adjusted as the layout cannot take the min-height infos from the qss. The config is gui.account_list.config.MAX_ITEM_HEIGHT. In this config file the icons for the account_types can configured with the animation icons for the connecting state.

_images/theme-account-list-item-dimensions.png

The widget margins define the tree-style display of the items. The spacing is highlighted in this illustration:

_images/theme-account-list-item-widget-spacing.png
QSS and LESS mixins

QSS is a subset of CSS and supports most features known to web developers. As advanced webdeveloper higher languages like LESS or SASS are useful to avoid repetive typing of constants. This concept is unknown to QSS. Therefore Netsplice adapted one of the most useful features, named mixins, to the theming. It loads and evaluates a less file and replaces all mixins in the QSS file.

default.less:

@basecolor: red;
@textcolor: white;

default.qss:

QWidget {
  background-color: @basecolor;
  color: @textcolor;
}
QPushButton {
  border: 1px solid @textcolor;
}

The above statements will be processed to qss before passing it to the QT style engine:

QWidget {
  background-color: red;
  color: white;
}
QPushButton {
  border: 1px solid white;
}

Other features of LESS are not implemented.

Themes

With adding a qss resource to the res-themes directory it is possible to completely alter the application. Please pull request your contribution.

Blue

A blue-theme has been created during development.

_images/theme-blue.png

Authors

Source Documentation

Project Classes

Almost all classes that are found in the src/netsplice tree.

netsplice.gui.mainwindow.mainwindow
class netsplice.gui.mainwindow.mainwindow.mainwindow(application)[source]

Bases: PySide.QtGui.QMainWindow

Main window for login and presenting status updates to the user.

Constructor for the main window.

new_updates = <PySide.QtCore.Signal object>
raise_window = <PySide.QtCore.Signal object>
backend_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
create_account_from_text = <PySide.QtCore.Signal object>
_about()[source]

Display the ‘About Netsplice’ dialog.

_account_create(parent_group_id=None)[source]

Account Create.

Create an account with the given parent group id. Initialize the account_edit module if not yet initialized. Reset the form contents and show the dialog.

_account_create_from_text(config_text)[source]

Account Create.

Create a account with the given config. Initialize the account_edit module if not yet initialized, reset the form contents and show the dialog.

_account_log(model_json)[source]

Display log for account only.

Configure the logviewer with an extra filter for account_id of model. When the logviewer already is open, reconfigure it and raise.

Parameters:model_json – account_list_item_model json string.
_account_settings(model_json)[source]

Account Settings.

Modify an account with the given model. The model has to contain an account with all attributes. Initialize the account_edit module if not yet initialized, reset the form contents, load the account_list_item and show the dialog.

_backend_available()[source]

Backend available.

The backend has become available. Notify the systray, request the backend preferences and model and hide the startup progress. When the backend already was up once, no notification will be shown.

_backend_connections_changed(connections, failed_connections)[source]

Backend Connections Changed.

The backend connections have changed. Update the private connections caches.

_backend_not_available()[source]

Backend Not Available.

The backend has become unavailable. Notify the systray when the backend does not come up after 10 cycles.

_backend_gone()[source]

Backend Gone.

The backend has shutdown, display a message to the user along with the latest log entries.

_backend_preferences_changed(model)[source]

Backend Preferences Changed.

The backend preferences have changed. Update the early gui settings (QSettings), remember the available accounts and set the shortcut keys.

_close_welcome()[source]

Close Welcome message.

Remember the decision of the user wether to show ‘Welcome’ message on every startup and show the accounts.

_connect_all()[source]

Connect All.

Connect all known accounts.

_connect_accounts()[source]

Connect Accounts.

Connect all known accounts when all accounts are in the global group. Otherwise the connect_accounts_action that triggered the event has added the group_id which has to be connected.

_connection_log(connection_id)[source]

Display log for connection only.

Configure the logviewer with an extra filter for connection_id of the model. When the logviewer already is open, reconfigure it and raise.

Param:connection_id: id of connection that should be shown.
_group_create(parent_group_id=None)[source]

Group Create.

Create a group with the given parent group id. Initialize the group_edit module if not yet initialized, reset the form contents and show the dialog.

_group_settings(model_json)[source]

Group Settings.

Modify a group with the given model. The model has to contain a group with with all attributes. Initialize the group_edit module if not yet initialized, reset the form contents, load the group_list_item and show the dialog.

_help()[source]

Display the Netsplice help dialog.

_notify_error_log(filter)[source]

Display log for given filter.

Configure the logviewer with a extra filter of model. When the logviewer already is open, reconfigure it and raise.

Param:filter: string (error-code) that should be shown.
_query_private_key_password(account_id)[source]

Display a Private-Key-Password Dialog.

Display a dialog that requests a password from the user. The result will be sent to the backend. Always recreates the dialog as the dialog deletes itself on close.

Parameters:account_id – id of account that requires the username/password.
_query_username_password(account_id)[source]

Display a Username-Password Dialog.

Display a dialog that requests username and password from the user. The result will be sent to the backend. Always recreates the dialog as the dialog deletes itself on close.

Parameters:account_id – id of account that requires the username/password.
_quit_final()[source]

Final steps to quit the app, starting from here we don’t care about running services or user interaction, just quit.

_raise_activity(activity_index)[source]

Raise Activity.

Set the widget-stack to the given activity index.

_raise_mainwindow()[source]

Raise Mainwindow

Triggered when we receive a RAISE_WINDOW event.

_set_plugin_menu_items()[source]

Set Plugin Menu Items.

Populate the main menu with actions defined by plugins.

_set_shortcut_keys()[source]

Set Shortcut Keys.

Set the key shortcuts.

_set_window_position()[source]

Set Window Position.

Center the main window based on the desktop geometry. When settings (QSettings) for the geometry are available, use the settings.

_show_application_log(log_filter=None)[source]

Display all available log items.

Show the window with the history of messages logged until now and display the new ones on receival. Configure an existing logviewer to set the given filters.

_show_preferences()[source]
TRIGGERS:
self.ui.btnPreferences.clicked (disabled for now) self.ui.action_preferences

Display the preferences window.

_show_systray()[source]

Sets up the systray icon.

_update_connect_accounts()[source]

Update the connect accounts button with the available groups as dropdown.

_startup_progress(message)[source]

Startup Progress.

Display a message from the backend stdout message to show current task in progress bar.

closeEvent(e)[source]

Reimplementation of closeEvent to close to tray.

accounts_activity()[source]

Raise account activity.

Public interface to allow other widgets to ensure that the accounts are displayed.

add_dialog(name, dialog_instance)[source]

Add Dialog.

Add the given dialog to the dialog_map.

Parameters:
  • name (string) – name of the dialog
  • dialog_instance (QDialog) – instance of a dialog.
ensure_invisible()[source]
TRIGGERS:
self._action_visible.triggered

Ensure that the window is hidden.

ensure_visible()[source]
TRIGGERS:
self._action_visible.triggered

Ensure that the window is visible and raised.

get_dialog(name)[source]

Get Dialog.

Return a registered dialog by name.

Parameters:name (string) – name of the dialog
Returns:QDialog – Instance of the dialog.
Raises:NotFoundError – No dialog with the given name found.
quit(disable_autostart=True)[source]

Start the quit sequence and wait for services to finish. Cleanup and close the main window before quitting.

Parameters:disable_autostart (bool) – whether we should disable the autostart feature or not
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.mainwindow.theme
class netsplice.gui.mainwindow.theme.theme[source]

Bases: object

Theme object.

Loads the theme, defines fallback/default.

Initialize object.

Sets and loads defaults.

_get_qss()[source]

Getter for QSS.

Transforms QSS with LESS variables to QSS that Qt can load.

_load_file_name()[source]

Load Theme from Filename.

Tries to load the theme by name. When the files cannot be found, the QSS/LESS members are not modified.

_load_rc_default()[source]

Load default Theme.

Loads the default theme that will be used when the name cannot be found as file or as resource.

_load_rc_name()[source]

Load theme from resource.

Loads a theme that is compiled as resource file. When no resource with the given name exists, QSS/LESS members are not modified.

change(name)[source]

Change the Theme.

Loads the given name QSS and LESS files from the resource store or filesystem. When neither resource nor file are available, the default theme is used.

netsplice.gui.systray.menu
class netsplice.gui.systray.menu.menu(parent=None, dispatcher=None)[source]

Bases: PySide.QtGui.QMenu

Custom system tray menu that handles the account items based on their state.

backend_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
_add_accounts(grouped_accounts_model, accounts_model)[source]

Add new accounts.

Iterate the accounts_model and create an account item for each item that can not be found in the current account list.

_add_groups(parent_group_menu, parent_item, groups_model)[source]

Add new groups.

Add the actions with the preferences groups.

_add_plugin_menu_items()[source]

Add Plugin Menu Items.

Add the items that are registered to the systray menu.

_backend_accounts_changed(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Slot for handling that the preferences have changed.

_backend_connections_changed(connection_list, failed_connection_list)[source]

Slot for handling that the backend model has changed.

_create_account()[source]

Create Account.

Handler to forward the menu action to the systray class.

_create_account_item(account_id)[source]

Create account item.

Create an account (menu) item in the correct group submenu.

_create_chain_item(parent_menu, chain_id)[source]

Create group menu.

Create a group menu item.

_create_group_menu(parent_group_menu, group_item)[source]

Create group menu.

Create a group menu item.

_find_account_action_by_id(account_id)[source]

Find account_action by id.

Find the given account_id in any of the group menus or the root menu.

_get_account_item_data(action)[source]

Get Account item data.

Return the account_list_item_model for the given action or None if the action is not related to a model.

_get_account_weight(account_item)[source]

Get account weight.

Return the weight for the item used for sorting.

_get_chain_weight(chain_item)[source]

Get chain weight.

Return the weight for the item used for sorting.

_get_group_weight(group_item)[source]

Get group weight.

Return the weight for the item used for sorting.

_hide_main_window()[source]

Hide main window.

Request the systray to hide the main window.

_recreate_model_menu(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Recreate model menu.

Remove accounts and groups and build the menu based on the models.

_remove_accounts()[source]

Remove accounts.

Remove all accounts that have a menu item.

_remove_chains()[source]

Remove chains.

Remove all chains that have a menu item.

_remove_groups()[source]

Remove Groups.

Remove all groups that have a menu item.

_show_main_window()[source]

Show Main Window.

Handler to forward the menu action to the systray class.

_toggle_account(account_item_model)[source]
_update_account_state(grouped_accounts)[source]

Update account state.

Create a state-hash that can be compared between model changes. Store the hash when the state has changed and return True. Return False when no change was detected.

_update_account_item_state(account_action)[source]

Update account_item state.

Update the state/label of the account_action based on the connection state.

_update_context_accounts(accounts_model)[source]

Update context accounts.

Sync the actions with the preferences accounts

_update_context_connections(connection_list_model_instance)[source]

Update context actions.

Update the actions based on their connected state.

_update_chain_state(chains_model)[source]

Update group state.

Create a state-hash that can be compared between model changes. Store the hash when the state has changed and return True. Return False when no change was detected.

_update_group_state(groups_model)[source]

Update group state.

Create a state-hash that can be compared between model changes. Store the hash when the state has changed and return True. Return False when no change was detected.

_quit()[source]

Quit.

Handler to forward the menu action to the systray class.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.systray.systray
class netsplice.gui.systray.systray.systray(parent=None, dispatcher=None)[source]

Bases: PySide.QtGui.QSystemTrayIcon

Custom system tray that changes its icon based on the connection state and notifies when the state changes.

_animate_connecting_icon()[source]

Animate connecting icon.

When the connecting state is False, stop the animation, otherwise rotate through the config.ICON_TRAY_CONNECTING icons with a timer scheduling the next icon.

_change_tooltip(connections)[source]

Change Tooltip.

Change the tooltip to display the accounts that are currently active.

Parameters:connections (list) – list of backend connections.
_change_tray_icon(connections)[source]

Change Tray Icon.

Change the icon in the system tray to reflect the current backend activity.

Parameters:connections (list) – list of backend connections.
_backend_connections_changed(connection_list, failed_connection_list)[source]

Backend Connections Changed.

Handler for the backend connections. Display messages, update tray icon and tooltip.

Parameters:connection_list (list) – list of backend connections.
_backend_accounts_changed(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Backend Accounts Changed.

Handler for the backend account change event.

Parameters:
  • accounts_model (list) – All configured accounts
  • groups_model (list) – All configured groups
  • grouped_accounts_model (list) – All configured accounts with a group parent (unchained)
  • chains_model (list) – All configured accounts with their chain relation.
_load_icons()[source]

Load Icons.

Load icons that are used for the system tray. This loader is called once on initialize so the icons are reused.

_show_notification_messages(connections, failed_connection_list)[source]

Show notification messages.

Evaluate which connections have changed and collect their account-names. Evaluate which state-change has happened. Display notification message based on the changed state.

_tray_activated(reason=None)[source]
TRIGGERS:
self.activated
Parameters:reason (int) – The reason why the tray got activated.

Display the context menu from the tray icon.

_update_icon()[source]

Update Icon.

Update the tray icon according to the current state.

create_account()[source]

Create Account.

Handler for the create account menu action.

hide_main_window()[source]

Hide Main Window.

Handler for the hide main window menu action.

quit()[source]

Quit.

Handler for the quit menu action.

set_backend_available(backend_available)[source]

Set Backend Available.

Notify the user that the backend is available and store the state so future calls to this function only notify when the state has changed.

set_connected(connected, account_names=[])[source]

Set Connected.

Set the internal connected state and update the icon.

set_connecting(connecting, account_names=[])[source]

Set Connecting.

Set the internal connecting state and update the icon.

set_connecting_icon()[source]

Set Connecting Icon.

Set the initial connecting_index and start the connecting animation.

set_service_tooltip(tooltip)[source]

Sets the service tooltip.

Parameters:
  • service (unicode) – service name identifier.
  • tooltip (unicode) – tooltip to display for that service.
show_connected_message(account_names)[source]

Show Connected Message.

Show a message that the given account_names are now connected.

Parameters:account_names (string) – Names of accounts.
show_connecting_message(account_names)[source]

Show Connecting Message.

Show a message that the given account_names are now connecting.

Parameters:account_names (string) – Names of accounts.
show_disconnected_message(account_names)[source]

Show Disconnected Message.

Show a message that the given account_names are now disconnected.

Parameters:account_names (string) – Names of accounts.
show_failed_message(account_names)[source]

Show Failed Message.

Show a message that the given account_names have failed.

Parameters:account_names (string) – Names of accounts.
show_main_window()[source]

Show Main Window.

Handler for the show main window menu action.

show_named_message(message, options={}, icon=PySide.QtGui.QSystemTrayIcon.MessageIcon.Information, timeout=10000)[source]

Show Named Message.

Display a message with options, a icon and a timeout. This method is used for displaying any message (failed/connected/disconnected).

Parameters:

message (dict) – Message with a title and a body. The body may contain named placeholders that are replaced with the options argument.

Keyword Arguments:
 
  • options (dict) – Dictionary with named arguments to be replaced in the message body (default: {dict()})
  • icon (QIcon) – Icon that may be used for the message. (default: {QSystemTrayIcon.Information})
  • timeout (number) – [description] (default: {10000})
show_quit_message()[source]

Show Quit Message.

Display a message that the application is going to quit.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.statusbar.statusbar
class netsplice.gui.statusbar.statusbar.statusbar(parent, dispatcher)[source]

Bases: PySide.QtGui.QStatusBar

Custom Statusbar.

_backend_connections_changed(connection_list, failed_connection_list)[source]
_backend_heartbeat_connected()[source]
_backend_heartbeat_error()[source]
_backend_heartbeat_warning()[source]
_backend_preferences_changed(model)[source]

Backend Preferences changed.

Display the elements of the statusbar according to the preferences.

_load_icons()[source]
_set_heartbeat_icon(pixmap)[source]
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.widgets.syntax_element
class netsplice.gui.widgets.syntax_element.syntax_element(syntax_class, start_pattern, end_pattern)[source]

Bases: object

netsplice.gui.widgets.scroll_label
class netsplice.gui.widgets.scroll_label.scroll_label(parent=None)[source]

Bases: PySide.QtGui.QWidget

Label wrapped in a scroll area.

Widget that wraps a QLabel into a QScrollArea with the required layouts to display longer messages (i.e. stackdumps in check account).

Initialize Widget.

Initialize Widget with layout and scroll area.

getText()[source]

Get Label Text.

Returns the text of the Label.

setAlignment(align_flags)[source]

Set alignment.

Pass alignment to label.

Parameters:align_flags (int) – or’d flags (Qt::AlignLeft)
setTextInteractionFlags(flags)[source]

Set text interaction flags.

Pass text interactionflags to label.

Parameters:flags (int) – or’d flags
setScrollbarAlwaysOn()[source]

Set scrollbar always on.

Ensure that the scrollbar is always displayed for the label.

setText(text)[source]

Set label text.

Set text in the label widget.

setWordWrap(new_state)[source]

Set word wrap.

Set that the text in the label wraps.

Set open external links.

Set to open external links displayed in the label.

text = <Property object>
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.widgets.syntax_highlighter
class netsplice.gui.widgets.syntax_highlighter.syntax_highlighter(editor)[source]

Bases: PySide.QtGui.QSyntaxHighlighter

Custom syntax highlighter

setup_default()[source]
add_keyword(highlighter_name, class_name, keyword)[source]

Add keyword to be highlighted with given class.

add_block(highlighter_name, class_name, start_pattern, end_pattern)[source]

Add block to be highlightd with given class.

add_format_range(syntax_class_instance, start_index, length)[source]

Apply format on given text position.

find_element_pattern(text, start_index, pattern)[source]

Match the given pattern on the text and return the match index. If the pattern is not matched it returns -1.

find_elements(text, start_index, element_list)[source]

Evaluates all syntax_elements in the element_list for the given text and applys the format if matched. Handles Block opening.

find_highlighters(text, start_index)[source]

Process highlighters in correct order. Ensures that the ‘default’ highlighter is executed after all other highlighters have been executed.

find_end_block(text, start_index, syntax_element_instance)[source]

Find End Block of a block started in find_elements. Only accepts the closing pattern to end the block otherwise applies the started blockformat for all characters.

get_syntax_element_index(syntax_element_instance)[source]

Used for setCurrentBlockState (a integer) that allows block formats. Returns the index of the given syntax_element_instance.

get_syntax_element_by_index(index)[source]

Used for previousBLockState() that allows block formats. Returns the element that matches the index. The index has to be created with the get_syntax_element_index function.

highlightBlock(text)[source]

Qt Overload function frequently! called by the QTextEditor whenever it may have changed and wants to update the highlight. Usualy the function is called for each line from top to bottom. Then only for lines that are edited.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.widgets.list_item
class netsplice.gui.widgets.list_item.list_item(parent)[source]

Bases: PySide.QtGui.QWidget

Custom Widget Listing Drag and Dropable Items.

Implements the base functionality for drag&drop and allows to customize the stylesheet for active items. Using the item in a (vertical) scroll area will provide navigation in the view while dragging.

Initialize Widget.

Set members and defaults.

selected = <PySide.QtCore.Signal object>
_end_scroll()[source]

End Scroll.

End automatic scroll timeouts.

_locate_scroll_area()[source]

Locate Scroll Area.

Traverse the parents until a scroll-area is found that can be manipulated by the timers.

_scroll(value)[source]

Scroll.

Timer callback. Change the vertical scrollbar by the given value.

_start_scroll_down()[source]

Start Scroll.

Start the timer that issues a scroll down if possible.

_start_scroll_timer(event)[source]

Start Scroll Timer.

Evaluate the event position to find one of three actions: up, down or stop scrolling.

_start_scroll_up()[source]

Start Scroll.

Start the timer that issues a scroll up if possible.

get_active()[source]

Get Active.

Returns if the item has been selected (eg clicked on).

closeEvent(event)[source]

Close Event.

This event occurs when the widget is deleted.

Parameters:event (Qt.QEvent) – Event details.
dragEnterEvent(event)[source]

Drag Enter Event.

Handle that anything has been dragged over the item. Ensure that the indicator is drawn.

dragLeaveEvent(event)[source]

Drag Leave Event.

Stop handling a drag event and ensure that indicators are reset.

dragMoveEvent(event)[source]

Drag Move Event.

Ensure that the indicators are drawn and give items the possibility to reject the drop.

dropEvent(event)[source]

Drop Event.

Handle a completed drop and ensure that any indicators are reset.

mousePressEvent(event)[source]

Mouse Press Event.

Initialize a possible drag action and emit a selected event.

mouseReleaseEvent(event)[source]

Mouse Release Event.

Resets the position used for evaluating if a drag action started.

mouseMoveEvent(event)[source]

Mouse Move Event.

Handles move events and starts drag if a drag_start_position is set with the manhattanLength.

paintEvent(paint_event)[source]

Paint Event.

Ensures the Highlight of the active state and when in drag mode calls painter for drop indicators.

accept_mime_data(mime_data)[source]

Accept Mime Data.

Return True when the given mime_data has the correct format and may be evaluated its contents.

Parameters:mime_data (QtCore.QMimeData) – Mime data of element to be dropped.
Returns:bool – True when the mime data can be accepted.
drag_end()[source]

Drag End.

Cleanup any state that was setup during dragging.

drag_move(position)[source]

Drag Move.

Evaluate how the drag would be handled when it is dropped at the given position. Used to prepare the painter.

Parameters:position (QtCore.QPoint) – Current point of the drag.
drop_mime_data(mime_data)[source]

Drop Mime Data.

Handle the dropped mime_data after drop has finished.

Parameters:mime_data (QtCore.QMimeData) – Mime data of the drop
Returns:True if the drop was handled with success.
Return type:bool
get_mime_data()[source]

Get Mime Data.

Return QtCore.QMimeData() that contains a dropable handle.

paint_drop_regions(painter)[source]

Paint drop regions based on the status of the widget.

Optional that allows drawing indicators.

Parameters:painter (QtGui.QPainter) – Painter of the widget.
set_active(state)[source]

Set Active.

Sets the list_item in active state and ensures that a property based stylesheet is reconsidered.

active = <Property object>
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.widgets.syntax_class
class netsplice.gui.widgets.syntax_class.syntax_class(class_name, font, color, background_color=None)[source]

Bases: object

get_text_format()[source]
netsplice.gui.widgets.ordered_string_item_list
class netsplice.gui.widgets.ordered_string_item_list.ordered_string_item_list(parent=None)[source]

Bases: PySide.QtGui.QWidget

Custom Widget with a listview and buttons to add, remove and order items.

textChanged = <PySide.QtCore.Signal object>
NEED_PREV = 1
NEED_NEXT = 2
NEED_COUNT = 4
NEED_NOT_EMPTY = 8
_activated(model_index)[source]

Activated.

An item in the list is activated.

_add_toolbar_action(selection_state, label, callback)[source]

Add Toolbar Action.

Add the given action that is enabled based on the selected item with the given label.

_changed(model_index=None, opt_start=0, opt_end=0, opt_dparent=None, opt_drow=None)[source]

Changed.

An item in the list is activated. This is a proxy for any changes in the model and any changes that are triggered by the widget.

_down()[source]

Down.

Move item down. Select the moved item.

_init_ui()[source]

Initialize UI.

Define layouts and widgets and connect them to member functions

_insert()[source]

Insert.

Insert a new item in the list. When the list is empty, insert as 0. Make the item the current item.

_remove()[source]

Remove.

Remove an item from the list. Select the item at the same row or the one before the removed item.

_setup_toolbar()[source]

Setup Toolbar.

Add Actions and Widgets to the toolbar.

_up()[source]

Up.

Move item up. Select the moved item.

_update_actions()[source]

Update Actions.

Update the Widget actions depending on the selected item. Always enable Insert. Enable Up/Down only if current is supporting it. Enable Remove only if model is not empty.

to_json()[source]

To JSON.

Return the current model as json string.

to_list()[source]

To List.

Return the current model as list.

from_json(json_string)[source]

From JSON.

Fill the model with the items from the JSON. The JSON needs to be an array of strings:

["A", "B", "C"]
from_list(item_list)[source]

From List.

Fill the model with the items in the list.

setText(json_string)[source]

Set Text.

Implement QLineEdit API for seamless replacement.

text()[source]

Text.

Implement QLineEdit API for seamless replacement.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.widgets.dialog
class netsplice.gui.widgets.dialog.dialog(parent=None)[source]

Bases: PySide.QtGui.QDialog

Dialog.

Common Dialog methods shared by all dialogs.

Initialize Dialog.

Initialize QDialog and members.

model_changed = <PySide.QtCore.Signal object>
values_changed = <PySide.QtCore.Signal object>
get_parent_mainwindow()[source]

Get Parent Mainwindow.

Iterate parents to find a QMainWindow widget. Return None if no QMainWindow is in parents.

get_parent_dialog()[source]

Get Parent dialog.

Iterate parents to find a dialog widget. Return None if no dialog is in parents.

reset_position()[source]

Reset Position.

Show window left or right of the mainwindow, depending on which side of the desktop the mainwindow is located.

show()[source]

Show Dialog.

Displays the window of the Dialog and ensures its size and position.

set_minimal()[source]

Set Minimal Size.

Avoid expanding the window width based on the screen size.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.widgets.text_editor
class netsplice.gui.widgets.text_editor.text_editor(parent=None)[source]

Bases: PySide.QtGui.QTextEdit

Custom editor with syntax highlighter and keyword tooltip.

setup_keywords(keyword_map)[source]

Map the keywords into doc and syntax highlighter.

setup_blocks(block_keywords)[source]

Map the keywords to match as blocks in the syntax highlighter.

event(event)[source]

Widget event handler to show documentation tooltip.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.about.about
class netsplice.gui.about.about.about(parent=None, dispatcher=None)[source]

Bases: netsplice.gui.widgets.dialog.dialog

Custom widget for displaying information about the application.

Initialize module.

Initialize the dialog and member variables.

init_core_text()[source]

Init Core Text.

Initialize the core application text.

init_plugin_text()[source]

Init Plugin Text.

Initialize the text for all Plugins.

show_logs()[source]

Show logs.

Display the logs window by triggering an event-loop signal that causes the mainwindow to raise the logwindow.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.event_loop
class netsplice.gui.event_loop.event_loop[source]

Bases: PySide.QtCore.QObject, netsplice.util.ipc.event_loop.event_loop

notify_error = <PySide.QtCore.Signal object>
connection_error = <PySide.QtCore.Signal object>
query_username_password = <PySide.QtCore.Signal object>
query_private_key_password = <PySide.QtCore.Signal object>
accounts_changed = <PySide.QtCore.Signal object>
connections_changed = <PySide.QtCore.Signal object>
gui_model_changed = <PySide.QtCore.Signal object>
log_changed = <PySide.QtCore.Signal object>
preferences_changed = <PySide.QtCore.Signal object>
preferences_plugins_changed = <PySide.QtCore.Signal object>
_connections_changed(connections, failed_connections)[source]

Connections changed.

Evaluates if the state of a connection has changed.

start()[source]
process_events(model_json)[source]

Process Events.

Process events from backend and emit signals modules may connect to. Ensure that json is not emitted multiple times, find events that are not yet processed (by index) and process them.

process_event(event_model_instance)[source]

Process Event.

Process single event from backend and emit signals modules may connect to.

process_plugin_event(event_model_instance)[source]

Process Plugin Event.

Process single event in plugins that have registered for the event’s origin.

Decorators:
gen.continue
Parameters:event_model_instance (model.event) – event to be processed
process_preferences_events(model_json)[source]

Process Preferences Events.

Emit events when the preferences have changed.

process_preferences_plugins_events(model_json)[source]

Process Plugins Events.

Emit events when the preferences for plugins have changed.

publish_logs()[source]

Send the log_items to the backend when the dispatcher is available.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.preferences.qt_settings
class netsplice.gui.preferences.qt_settings.qt_settings[source]

Bases: object

Settings object.

Loads the settings from ini/plist/conf and syncs the config values, defines fallback/default. Handles the load and store of values.

Initialize object.

Sets and loads defaults.

get_mainwindow_geometry()[source]

Get Mainwindow Geometry.

Return None or the last geometry of the mainwindow.

load()[source]

Load Startup Setting.

Loads the last configured theme from the qt settings. This prevents that the gui application is unconfigured on startup until the backend preferences are loaded.

store()[source]

Store Startup Setting.

Store the setting in the registry/plist/conf that is managed by QT. This allows the startup to load the correct values without waiting for the backend.

store_mainwindow_geometry(geometry)[source]

Store Mainwindow Geometry.

Store the geometry of the mainwindow to the settings so it can be restored at the same location on the desktop.

update_backend(backend_model)[source]

Update Backend.

Update backend settings.

update_gui(ui_model)[source]

Update Gui.

Update the config_gui variables from the ui-model.

netsplice.gui.preferences.preferences
class netsplice.gui.preferences.preferences.preferences(parent, dispatcher)[source]

Bases: netsplice.gui.widgets.dialog.dialog

Window that displays preferences of the application.

Initialize the widget.

backend_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
_backend_done()[source]

Backend done.

Request the new preferences.

_close()[source]

Close the dialog.

_setup_core_items()[source]

Setup Core Items.

Setup Tabs for general configuration of application - non plugin, global stuff.

_setup_plugins(plugins)[source]

Setup Plugins.

Create an action button and a key_value_editor for every given plugin. Register the created widgets so they can be switched and received.

_model_to_config(model)[source]

Model To Config.

Convert the given model attributes to a line-config that can be interpreted by the key_value_editor.

_plugin_model_to_config(model)[source]

Plugin Model to Config.

Helper that removes the ‘name’ field from the plugin values as this option should never change.

_preferences_changed(model)[source]

Preferences Changed.

Load the values provided by the backend when not currently editing a value.

_preferences_plugins_changed(model)[source]

Preferences Changed.

Load the values provided by the backend when not currently editing a value.

_values_changed(new_values)[source]

Update the values from the widget to the backend.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.group_edit.group_edit
class netsplice.gui.group_edit.group_edit.group_edit(parent=None, dispatcher=None)[source]

Bases: netsplice.gui.widgets.dialog.dialog

Custom Widget for editing and creating Groups.

Defines the widget that is used to configure groups.

Initialize Module.

Initialize the dialog and member variables.

create_done_signal = <PySide.QtCore.Signal object>
create_failed_signal = <PySide.QtCore.Signal object>
load_done_signal = <PySide.QtCore.Signal object>
load_failed_signal = <PySide.QtCore.Signal object>
model_loaded_signal = <PySide.QtCore.Signal object>
model_stored_signal = <PySide.QtCore.Signal object>
model_reset_signal = <PySide.QtCore.Signal object>
update_done_signal = <PySide.QtCore.Signal object>
update_failed_signal = <PySide.QtCore.Signal object>
_commit()[source]

Commit Handler.

Call group_type_editor to save a previously loaded account.

_backend_model_changed(model)[source]

Model Change Handler.

Handle when the backend model has changed. Used to display errors of the check function and enable/disable the commit button based on the active connection list.

_cancel()[source]

Cancel Handler.

Reset the control and close the window.

_config_changed()[source]

Config Changed.

Handle that a config value has been changed.

_connect_signals()[source]

Connect Signals.

Connect local and widget signals to member functions.

_create()[source]

Create Handler.

Create a new account with the current configuration.

_create_failed(errors)[source]

Create Failed.

Handle when create account returned with a error.

_create_done(response)[source]

Create Done.

Handle when create account has been successfully completed. Close the Window.

_disconnect_signals()[source]

Disconnect Signals.

Disconnect local and widget signals from member functions.

_init_ui()[source]

Initialize UI.

Connect the ui signals to handler functions.

_load_done(response)[source]

Load Done.

Handles when loading a account has been returned from the backend. Used because a account needs to request editable attributes that are not available to the default account model.

_load_failed(errors)[source]

Load failed.

Handle when loading account attributes failed.

_load_model(group_model_instance)[source]

Load Model.

Load account from the given model instance.

_reset_widgets()[source]

Reset Widgets.

Reset the account widgets to default values.

_update_done(response)[source]

Update done.

Handle when updating a existing account has completed. Closes the dialog window.

_update_failed(errors)[source]

Update failed.

Handle when updating a existing account has failed. Display the error.

_set_readonly(state)[source]

Set Readonly.

Set the group_edit dialog readonly while a connection is active.

create(parent_group_id=None)[source]

Create new Account.

Configures the dialog to create a new account with the optional given parent group id. Hide Commit Button and show Create Button

hideEvent(event)[source]

Hide Event.

Disconnect signals from widgets.

showEvent(event)[source]

Show Event.

Connect signals to backend and widgets.

load(group_id)[source]

Load existing Account.

Configures the dialog to edit a existing account identified by the given group_id. Hide Create button and show Commit button.

reset()[source]

Reset the Dialog.

Forget all user-entered and loaded data.

update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update Model.

The accounts changed in the backend. Store the model and make sure there is a root_item visible. When no accounts are available, display a message that allows the creation of a new account.

Parameters:
  • accounts_model (list) – list of all accounts
  • groups_model (list) – list of all groups
  • grouped_accounts_model (list) – list of all accounts with association to a group and a weight
  • chains_model (list) – list of all accounts in their chain location
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.group_edit.model.validator.group_name
class netsplice.gui.group_edit.model.validator.group_name.group_name[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account Names are simple nonempty Strings

netsplice.gui.group_edit.model.response.group_item
class netsplice.gui.group_edit.model.response.group_item.group_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.group_edit.model.response.group_id
class netsplice.gui.group_edit.model.response.group_id.group_id[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.group_edit.model.response.group_edit_item
class netsplice.gui.group_edit.model.response.group_edit_item.group_edit_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.help.help
class netsplice.gui.help.help.help(parent=None, dispatcher=None)[source]

Bases: netsplice.gui.widgets.dialog.dialog

Custom Widget for displaying information help the application.

Initialize Module.

Initialize the dialog and member variables.

init_core_text()[source]

Init Core Text.

Initialize the core application text.

show_logs()[source]

Show logs.

Display the logs window by triggering a event-loop signal that causes the mainwindow to raise the logwindow.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.backend_dispatcher
class netsplice.gui.backend_dispatcher.backend_dispatcher(host, port, host_owner, port_owner)[source]

Bases: PySide.QtCore.QObject, netsplice.util.ipc.service.service, netsplice.util.process.dispatcher.dispatcher

Backend dispatcher to be used by the main thread. All functions of this class are private and must not be called from the main thread. Only signals may be emitted that Qt promises to be thread safe. Most signals receive a done_signal and a failed_signal in order to handle the async requests.

model_changed = <PySide.QtCore.Signal object>
preferences_changed = <PySide.QtCore.Signal object>
preferences_plugins_changed = <PySide.QtCore.Signal object>
available = <PySide.QtCore.Signal object>
not_available = <PySide.QtCore.Signal object>
process_gone = <PySide.QtCore.Signal object>
heartbeat_connected = <PySide.QtCore.Signal object>
heartbeat_error = <PySide.QtCore.Signal object>
heartbeat_warning = <PySide.QtCore.Signal object>
plugins_changed = <PySide.QtCore.Signal object>
check_available = <PySide.QtCore.Signal object>
events_get = <PySide.QtCore.Signal object>
model_get = <PySide.QtCore.Signal object>
logs_delete = <PySide.QtCore.Signal object>
logs_get = <PySide.QtCore.Signal object>
logs_set = <PySide.QtCore.Signal object>
preferences_get = <PySide.QtCore.Signal object>
preferences_events_get = <PySide.QtCore.Signal object>
preferences_complex_set = <PySide.QtCore.Signal object>
preferences_plugin_get = <PySide.QtCore.Signal object>
preferences_plugin_set = <PySide.QtCore.Signal object>
preferences_plugin_collection_instance_get = <PySide.QtCore.Signal object>
preferences_plugin_collection_instance_set = <PySide.QtCore.Signal object>
preferences_value_set = <PySide.QtCore.Signal object>
shutdown = <PySide.QtCore.Signal object>
startup_progress = <PySide.QtCore.Signal object>
account_connect = <PySide.QtCore.Signal object>
account_create = <PySide.QtCore.Signal object>
account_delete = <PySide.QtCore.Signal object>
account_disconnect = <PySide.QtCore.Signal object>
account_get_for_edit = <PySide.QtCore.Signal object>
account_reconnect = <PySide.QtCore.Signal object>
account_reset = <PySide.QtCore.Signal object>
account_update = <PySide.QtCore.Signal object>
credential_cancel_request = <PySide.QtCore.Signal object>
credential_send = <PySide.QtCore.Signal object>
chain_connect = <PySide.QtCore.Signal object>
chain_create = <PySide.QtCore.Signal object>
chain_update = <PySide.QtCore.Signal object>
grouped_account_create = <PySide.QtCore.Signal object>
grouped_account_delete = <PySide.QtCore.Signal object>
grouped_account_update = <PySide.QtCore.Signal object>
group_connect = <PySide.QtCore.Signal object>
group_create = <PySide.QtCore.Signal object>
group_delete = <PySide.QtCore.Signal object>
group_disconnect = <PySide.QtCore.Signal object>
group_get = <PySide.QtCore.Signal object>
group_reconnect = <PySide.QtCore.Signal object>
group_update = <PySide.QtCore.Signal object>
groups_get = <PySide.QtCore.Signal object>
_account_connect(*args, **kwargs)[source]

Connect the account with the given account_id.

_account_create(*args, **kwargs)[source]

Create account with given properties.

_account_delete(*args, **kwargs)[source]

Delete the account with the given account_id.

_account_disconnect(*args, **kwargs)[source]

Disconnect the account with the given account_id.

_account_get_for_edit(*args, **kwargs)[source]

Get the account information with details like password for editing.

_account_reconnect(*args, **kwargs)[source]

Reconnect the account with the given account_id.

_account_reset(*args, **kwargs)[source]

Resets the account with the given account_id.

_account_update(*args, **kwargs)[source]

Update the account with the given account_id with the given values.

_credential_cancel_request(*args, **kwargs)[source]
_credential_send(*args, **kwargs)[source]
_check_available(*args, **kwargs)[source]

Check if the backend service is available with the ipc-status controller. The function will signal the state and continue doing so reschedule until the backend is available.

_check_log_ready_message()[source]

Check log ready message.

Check the log for a ready message from the backend process and return True when the message is found.

_check_log_startup_messages()[source]

Check log startup message.

Check the log for backend startup messages. When a message is found it is stored in the self._startup_message and can be emitted to the main window.

_chain_connect(*args, **kwargs)[source]

Connect chain.

Connect chain with its children.

_chain_create(*args, **kwargs)[source]

Create a new group entry.

_chain_update(*args, **kwargs)[source]

Update the given group.

_emit_failure(failed_signal, errors)[source]

Convenience function to make error handling less verbose.

_events_get(*args, **kwargs)[source]

Long-Polling call to backend that will return every time the backend- model changes.

_group_connect(*args, **kwargs)[source]

Connect all accounts of given group_id.

_group_create(*args, **kwargs)[source]

Create a new group entry.

_group_delete(*args, **kwargs)[source]

Delete a existing group entry.

_group_disconnect(*args, **kwargs)[source]

Disconnect all accounts of given group_id.

_group_get(*args, **kwargs)[source]

Get details for the given group_id.

_group_reconnect(*args, **kwargs)[source]

Disconnect all accounts of given group_id.

_group_update(*args, **kwargs)[source]

Update the given group.

_grouped_account_create(*args, **kwargs)[source]

Create a new group entry.

_grouped_account_delete(*args, **kwargs)[source]

Delete an existing grouped_account entry.

_grouped_account_update(*args, **kwargs)[source]

Update the given group.

_groups_get(*args, **kwargs)[source]

Get the list of available groups

_logs_delete(*args, **kwargs)[source]

Delete Logs.

Remove logs from backend.

_logs_get(*args, **kwargs)[source]

Gets logs from backend.

_logs_set(*args, **kwargs)[source]

Sends log_item to backend. This function does not process the response failures get swallowed.

_model_get(*args, **kwargs)[source]

Gets model from backend and notifies gui.

_plugins_changed(*args, **kwargs)[source]

Plugins Changed.

GUI thread signaled that the list of dispatcher-plugins has changed. Re-iterate the plugin list and create instances for plugins where no dispatcher exists yet.

_preferences_get(*args, **kwargs)[source]

Gets the preferences from backend and notifies gui.

_preferences_events_get(*args, **kwargs)[source]

Long-Polling call to backend that will return every time the backend- preferences-model changes.

_preferences_complex_set(*args, **kwargs)[source]

Set a specific preferene value.

_preferences_plugin_get(*args, **kwargs)[source]

Get all preference values for the plugin.

_preferences_plugin_collection_instance_get(*args, **kwargs)[source]

Get all preference values for the plugin.

_preferences_plugin_set(*args, **kwargs)[source]

Set a specific preference value.

_preferences_plugin_collection_instance_set(*args, **kwargs)[source]

Set a specific preference model.

_preferences_value_set(*args, **kwargs)[source]

Set a specific preferene value.

_schedule_check_available()[source]

Schedule check_available without producing an endless stack.

_schedule_exit()[source]

Allow events to be processed before the thread is stopped.

_schedule_events_get()[source]

Schedule get_events call without producing a endless stack.

staticMetaObject = <PySide.QtCore.QMetaObject object>
_schedule_logs_set(log_item_json)[source]

Schedule _logs_set call when backend is not yet available but gui wants to set logs.

_schedule_preferences_events_get()[source]

Schedule get_events call without producing a endless stack.

_shutdown(*args, **kwargs)[source]

Request the backend to shutdown.

service_shutdown(*args, **kwargs)[source]

Service Shutdown.

The dispatcher encountered an unrecoverable error and needs to shut down. The gui will be notified so it can display information to the user. Stop processing gen.coroutines in this thread to prevent crashes during shutdown and endless useless get’s.

netsplice.gui.account_edit.raw.account_type_editor
class netsplice.gui.account_edit.raw.account_type_editor.account_type_editor(parent=None, dispatcher=None)[source]

Bases: PySide.QtGui.QWidget, netsplice.gui.account_edit.abstract_account_type_editor.abstract_account_type_editor

RAW Account Type Editor for Accounts.

Skeleton implementation only.

Initialize Module.

Initialize bases and load ui.

config_changed = <PySide.QtCore.Signal object>
can_check()[source]

Evaluate form completeness.

Checks values in widget are complete to check the account.

can_commit()[source]

Evaluate form completeness for create or update.

Checks values in widget are complete to create or update the account.

create(account_name, enabled, default_route, autostart, group_id, create_done_signal, create_failed_signal)[source]

Create a new account.

Create a new account in the given group with the given name and the type values.

commit(account_id, account_name, enabled, default_route, autostart, commit_done_signal, commit_failed_signal)[source]

Update existing account.

Update an existing account with the given name and the type values.

set_model(account_model)[source]

Set Model.

Set Model for the widget.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_edit.account_edit
class netsplice.gui.account_edit.account_edit.account_edit(parent=None, dispatcher=None)[source]

Bases: netsplice.gui.widgets.dialog.dialog

Custom Widget for editing and creating Accounts.

Defines the basic functionality and uses tab_editor to proxy the specific functions.

Initialize Module.

Initialize the dialog and member variables.

create_done_signal = <PySide.QtCore.Signal object>
create_failed_signal = <PySide.QtCore.Signal object>
create_regroup_signal = <PySide.QtCore.Signal object>
load_done_signal = <PySide.QtCore.Signal object>
load_failed_signal = <PySide.QtCore.Signal object>
model_loaded_signal = <PySide.QtCore.Signal object>
model_stored_signal = <PySide.QtCore.Signal object>
model_reset_signal = <PySide.QtCore.Signal object>
update_done_signal = <PySide.QtCore.Signal object>
update_failed_signal = <PySide.QtCore.Signal object>
_account_profile_changed(account_profile)[source]

Account Profile Changed.

The account profile has been changed. Notify the plugin tabs about the change.

Parameters:account_profile (dict) – new account profile
_commit()[source]

Commit Handler.

Call tab_editor to save a previously loaded account.

_backend_model_changed(model)[source]

Model Change Handler.

Handle when the backend model has changed. Used to enable/disable the commit button based on the active connection list.

_cancel()[source]

Cancel Handler.

Reset the control and close the window.

_change_account_type(account_type)[source]

Change Account Type Handler.

Load the tabs for the account type.

_config_changed()[source]

Config Changed.

Handle that a config value has been changed.

_connect_signals()[source]

Connect Signals.

Connect local and widget signals to member functions.

_create()[source]

Create Handler.

Create a new account with the current configuration.

_create_failed(errors)[source]

Create Failed.

Handle when create account returned with a error.

_create_done(response)[source]

Create Done.

Handle when create account has been successfully completed. Close the Window.

_create_regroup(response)[source]

Create Regroup.

Create account done, regroup the account.

_disconnect_signals()[source]

Disconnect Signals.

Disconnect local and widget signals from member functions.

_get_state_html_image(state)[source]

Create Image Tag.

Create Image tag for the given state.

_format_error(errors)[source]

Format Error.

Formats an error message (from exceptions).

_init_ui()[source]

Initialize UI.

Connect the UI signals to handler functions.

_load_done(response)[source]

Load Done.

Handles when loading an account has been returned from the backend. Used because an account needs to request editable attributes that are not available to the default account model.

_load_failed(errors)[source]

Load failed.

Handle when loading account attributes failed.

_load_model(account_model_instance)[source]

Load Model.

Load account from the given model instance.

_reset_status()[source]

Reset Status.

Reset the status output to empty and hide the QLabels.

_reset_widgets()[source]

Reset Widgets.

Reset the account widgets to default values.

_update_done(response)[source]

Update done.

Handle when updating an existing account has completed. Closes the dialog window.

_update_failed(errors)[source]

Update failed.

Handle when updating an existing account has failed. Display the error.

_set_readonly(state)[source]

Set Readonly.

Set the account_edit dialog readonly while a connection is active.

_show_error(message, errors)[source]

Show Error.

Helper to display error messages alongside with exeption errors

create(parent_group_id=None)[source]

Create new Account.

Configures the dialog to create a new account with the optional given parent group ID. Hide Commit Button and show Create Button.

hideEvent(event)[source]

Hide Event.

Disconnect signals from widgets.

showEvent(event)[source]

Show Event.

Connect signals to backend and widgets.

load(account_id)[source]

Load existing Account.

Configures the dialog to edit a existing account identified by the given account_id. Hide Create button and show Commit button.

reset()[source]

Reset the Dialog.

Forget all user-entered and loaded data.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_edit.account_edit_tab_general
class netsplice.gui.account_edit.account_edit_tab_general.account_edit_tab_general(parent=None)[source]

Bases: PySide.QtGui.QWidget

Widget for editing general account properties.

Initialize Module.

Initialize the dialog and member variables.

config_changed = <PySide.QtCore.Signal object>
type_changed = <PySide.QtCore.Signal object>
account_profile_items_changed = <PySide.QtCore.Signal object>
account_profile_changed = <PySide.QtCore.Signal object>
change_account_profile_description = <PySide.QtCore.Signal object>
model_loaded_signal = <PySide.QtCore.Signal object>
_account_profile_changed(account_profile)[source]

Account Profile Changed

Slot that is connected to the account_profile_changed signal. Change the default_route flag in the tab.

Parameters:account_profile (dict) – dict with account_profile
_account_profile_items_changed()[source]

Account profile items changed.

Iterate all account profiles in the selectbox and find if the user has selected it for the current data.

_autostart_changed(new_state)[source]

Autostart Changed.

Change hint according to the new state.

_change_account_type()[source]

Change Account Type Handler.

Try to load the selected account_type, fallback if required.

_change_account_profile()[source]

Change Account Profile Handler.

Load the selected profile to the account configuration

_change_account_profile_description()[source]

Change Account Profile Handler.

Load the selected profile profile description to the hints display.

_config_changed()[source]

Config Changed.

Handle that a config value has been changed.

_connect_signals()[source]

Connect Signals.

Connect local and widget signals to member functions.

_default_route_changed(new_state)[source]

Autostart Changed.

Change hint according to the new state.

_disconnect_signals()[source]

Disconnect Signals.

Disconnect local and widget signals from member functions.

_enabled_changed(new_state)[source]

Enabled Changed.

Change hint according to the new state.

get_autostart()[source]

Get Autostart.

Return the state of the basic autostart checkbox.

_get_state_html_image(state)[source]

Create Image Tag.

Create Image tag for the given state.

_format_error(errors)[source]

Format Error.

Formats a error message (from exceptions).

_init_ui()[source]

Initialize UI.

Connect the ui signals to handler functions.

_load_done(response)[source]

Load Done.

Handles when loading a account has been returned from the backend. Used because a account needs to request editable attributes that are not available to the default account model.

_load_failed(errors)[source]

Load failed.

Handle when loading account attributes failed.

_load_model(account_model_instance)[source]

Load Model.

Load account from the given model instance.

_reset_status()[source]

Reset Status.

Reset the status output to empty and hide the QLabels.

_reset_widgets()[source]

Reset Widgets.

Reset the account widgets to default values.

_set_readonly(state)[source]

Set Readonly.

Set the account_edit dialog readonly while a connection is active.

_show_error(message, errors)[source]

Show Error.

Helper to display error messages alongside with exeption errors

hideEvent(event)[source]

Hide Event.

Disconnect signals from widgets.

showEvent(event)[source]

Show Event.

Connect signals to backend and widgets.

can_commit()[source]

Can Commit.

The widget is complete to commit.

change_account_type()[source]

Change account type.

Switch the selectbox and the associated widgets.

get_default_route()[source]

Get Default Route.

Return the state of the basic default route checkbox.

get_default_profile()[source]

Get Default Profile.

Return the default profile.

get_enabled()[source]

Get Enabled.

Return the state of the basic enabled checkbox.

get_name()[source]

Get Name.

Return the name of the account.

load(account_id)[source]

Load existing Account.

Configures the dialog to edit a existing account identified by the given account_id. Hide Create button and show Commit button.

reset()[source]

Reset the Dialog.

Forget all user-entered and loaded data.

set_mainwindow(mainwindow)[source]

Set Mainwindow.

Set the mainwindow widget to access the backend.

Parameters:mainwindow ([type]) – [description]
set_editor_proxy(editor_proxy)[source]

Set Editor Proxy.

Set the widget used to edit the type related properties.

Parameters:editor_proxy (account_type_editor_proxy) – widget
set_model(model)[source]

Set Model.

Set the model for the tab.

Parameters:model (model.account_item) – model of account currently edited.
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_edit.abstract_account_type_editor
class netsplice.gui.account_edit.abstract_account_type_editor.abstract_account_type_editor(type=None, dispatcher=None)[source]

Bases: object

Custom Editor with syntax highlighter and keyword tooltip.

Defines shared functionality and abstract functions.

Initialize.

Initialize.

is_account_profile(profile)[source]

Is Account Profile.

Return True when the current (import) configuration matches the profile default.

set_account_profile(profile)[source]

Set Account Profile.

Set the given profile as default for the account.

set_model(account_model)[source]

Set Model.

Set Model for the widget.

can_check()[source]

Evaluate form completeness.

Checks values in widget are complete to check the account.

can_commit()[source]

Evaluate form completeness for create or update.

Checks values in widget are complete to create or update the account.

check(check_done_signal, check_failed_signal)[source]

Check account values.

Check the type values for errors.

create(account_name, group_id, create_done_signal, create_failed_signal)[source]

Create a new account.

Create a new account in the given group with the given name and the type values.

commit(account_name, commit_done_signal, commit_failed_signal)[source]

Update existing account.

Update a existing account with the given name and the type values.

netsplice.gui.account_edit.model.validator.password
class netsplice.gui.account_edit.model.validator.password.password[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account passwords are simple nonempty Strings.

netsplice.gui.account_edit.model.validator.account_type
class netsplice.gui.account_edit.model.validator.account_type.account_type[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account types are simple nonempty Strings.

netsplice.gui.account_edit.model.validator.password_value
class netsplice.gui.account_edit.model.validator.password_value.password_value[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account password values are simple Strings.

netsplice.gui.account_edit.model.validator.group_name
class netsplice.gui.account_edit.model.validator.group_name.group_name[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Group Names are simple nonempty Strings.

netsplice.gui.account_edit.model.validator.username
class netsplice.gui.account_edit.model.validator.username.username[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account usernames are simple nonempty Strings

netsplice.gui.account_edit.model.validator.account_name
class netsplice.gui.account_edit.model.validator.account_name.account_name[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account Names are simple nonempty Strings

netsplice.gui.account_edit.model.validator.username_value
class netsplice.gui.account_edit.model.validator.username_value.username_value[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account usernames are simple nonempty Strings.

netsplice.gui.account_edit.model.response.account_edit_item
class netsplice.gui.account_edit.model.response.account_edit_item.account_edit_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.account_edit.model.request.account_check
class netsplice.gui.account_edit.model.request.account_check.account_check[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.account_edit.model.request.account_update
class netsplice.gui.account_edit.model.request.account_update.account_update[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.account_edit.model.request.account_create
class netsplice.gui.account_edit.model.request.account_create.account_create[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.account_edit.account_edit_tab_chain
class netsplice.gui.account_edit.account_edit_tab_chain.account_edit_tab_chain(parent=None)[source]

Bases: PySide.QtGui.QWidget

Widget for editing chain related account properties.

Initialize Module.

Initialize the dialog and member variables.

config_changed = <PySide.QtCore.Signal object>
model_stored_signal = <PySide.QtCore.Signal object>
store_done = <PySide.QtCore.Signal object>
store_failed = <PySide.QtCore.Signal object>
profile_changed = <PySide.QtCore.Signal object>
_config_changed()[source]

Config Changed.

Handle that a config value has been changed.

_connect_mode_changed()[source]

Connect Mode Changed.

The user selected a different connect_mode.

_connect_signals()[source]

Connect Signals.

Connect local and widget signals to member functions.

_disconnect_signals()[source]

Disconnect Signals.

Disconnect local and widget signals from member functions.

_failure_mode_changed()[source]

Failure Mode Changed.

The user selected a different failure_mode.

_get_connect_mode()[source]

Get connect_mode.

Return the current selected connect mode.

_get_failure_mode()[source]

Get failure_mode.

Return the current selected failure mode.

_get_model_id()[source]

Get Model Id.

Return the model_id from the current set model or editor_model.

Returns:None when no id was set (create mode)
Return type:(string)
_get_model_parent_id()[source]

Get Model Parent Id.

Return the parent id of the model chain instance.

Returns:None when no id was set (create mode)
Return type:(string)
_get_parent_id()[source]

Get parent_id.

Return the current selected parent_id.

_init_ui()[source]

Initialize UI.

Connect the ui signals to handler functions.

_parent_changed()[source]

Parent Changed.

Parent has changed in combo box.

_reset_status()[source]

Reset Status.

Reset the status output to empty and hide the QLabels.

_reset_widgets()[source]

Reset Widgets.

Reset the account widgets to default values.

_set_readonly(state)[source]

Set Readonly.

Set the account_edit dialog readonly while a connection is active.

_sort_key_name(dict_item)[source]

Sort Key Name.

Return the value for the name field in the dict_item.

Parameters:dict_item (dict) – dict with a name field.
_store_chain(account_id)[source]

Store Chain.

Handler for the account_edit dialog store event. This occurs when the account is created/updated shortly before the dialog is closed. The indices of the comboboxes are not available anymore.

Parameters:account_id (string) – account_id
_update_connect_modes()[source]

Update Connect Modes.

Update the connect modes combo box.

_update_failure_modes()[source]

Update Failure Modes.

Update the failure modes combo box.

_update_parent_accounts()[source]

Update Parent Accounts.

Update the combobox with possible parents, excluding self.

_update_text()[source]

Update Text.

Update summary text according to the current chain configuration.

hideEvent(event)[source]

Hide Event.

Disconnect signals from widgets.

showEvent(event)[source]

Show Event.

Connect signals to backend and widgets.

can_commit()[source]

Can Commit.

The widget is complete to commit.

reset()[source]

Reset the Dialog.

Forget all user-entered and loaded data.

set_mainwindow(mainwindow)[source]

Set Mainwindow.

Set the mainwindow widget to access the backend.

Parameters:mainwindow ([type]) – [description]
set_model(model)[source]

Set Model.

Set the model for the tab.

Parameters:model (model.account_item) – model of account currently edited.
update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update model.

Update the values of the account based on the latest model changes.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_edit.account_type_editor_proxy
class netsplice.gui.account_edit.account_type_editor_proxy.account_type_editor_proxy(parent=None)[source]

Bases: PySide.QtGui.QWidget

Account Type Editor Proxy.

Defines interface between account_edit and account_type_editor.

Initialize.

Initialize widget with a layout that will contain the specific editor.

config_changed = <PySide.QtCore.Signal object>
_config_changed()[source]

Config changed handler.

Proxy-emit the config changed event from the specialized account type editor.

is_account_profile(profile)[source]

Set Account Profile.

Set the given profile as default for the account.

set_account_type(account_type, dispatcher)[source]

Set Account Type.

Sets the account type and load the widget for that type.

set_account_profile(profile)[source]

Set Account Profile.

Set the given profile as default for the account.

set_model(account_model)[source]

Set Model.

Set Model for the widget.

can_check()[source]

Evaluate form completeness.

Check values in widget are complete to check the account.

can_commit()[source]

Evaluate form completeness for create or update.

Checks values in widget are complete to create or update the account.

check(check_started_signal, check_failed_signal)[source]

Check account values.

Check the type values for errors.

create(account_name, enabled, default_route, autostart, group_id, create_done_signal, create_failed_signal)[source]

Create a new account.

Create a new account in the given group with the given name and the type values.

commit(account_id, account_name, enabled, default_route, autostart, commit_done_signal, commit_failed_signal)[source]

Update existing account.

Update an existing account with the given name and the type values.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.group_item
class netsplice.gui.account_list.group_item.group_item(parent, mainwindow=None, model=None)[source]

Bases: netsplice.gui.widgets.list_item.list_item

Custom Widget for grouping grouped_chain_items. Group items are displayed in account_list and group_item and may contain grouped_chain_items.

account_create = <PySide.QtCore.Signal object>
account_item_dropped = <PySide.QtCore.Signal object>
account_item_dropped_on_group = <PySide.QtCore.Signal object>
account_log = <PySide.QtCore.Signal object>
account_settings = <PySide.QtCore.Signal object>
backend_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
group_create = <PySide.QtCore.Signal object>
group_dropped = <PySide.QtCore.Signal object>
group_settings = <PySide.QtCore.Signal object>
_account_dropped(dropped_account_id, account_id, weight)[source]

Account Dropped.

Handler for completed drop of dropped_account_id on account_id with the weight indicating before or after.

Parameters:
  • dropped_account_id (string) – id of dropped account
  • account_id (string) – id of account dropped on (self)
  • weight (integer) – weight of drop (< before > after)
_add_new_grouped_chain_items()[source]

Add grouped chain items.

Create a grouped_chain item in the current group for each item that is in the model but not in the widget.

_add_new_group_items()[source]

Add group items.

Create a group item in the current group for each item that is in the model but not in the widget.

_backend_done()[source]

Backend done.

Handler for any successful actions that may have modified the preferences.

_backend_model_changed(model)[source]

Model Changed.

Handle the backend model change. Find connection for the account_id representing this icon and update the status.

_backend_preferences_changed(model)[source]

Backend Preferences Changed.

The backend preferences have changed. Update the configured shortcuts for the widget.

Parameters:model (model.preferences) – New preferences model
_connect_actions()[source]

Connect Actions.

Connect the ui-element signals with member functions.

_collapse()[source]

Collapse.

Toggle collapse of the group. Do not collapse the root group and avoid storing the global group collapsed.

_connect()[source]

Group Connect.

Connect all accounts of the group

_delete()[source]

Delete.

Request the backend to the group when it is not root, global and after the user has confirmed.

_disconnect()[source]

Disconnect Group.

Disconnect all currently connected accounts of this group.

_new()[source]

New Group.

Emit to the mainwindow that the user wants to create a new group in the current group.

_order_weighted_widget(widget_item)[source]

Order weighted Widget.

Return the weight of the given widget item. This method is used for the sort.

Parameters:widget_item (list_widget_item) – Widget in the group.
Returns:integer – weight of the widget.
_reconnect()[source]

Reconnect Group.

Reconnect all currently connected accounts in the group.

_settings()[source]

Settings.

Emit to the mainwindow that the user wants to change the group settings.

_remove_abandoned_grouped_chain_items()[source]

Remove abandoned grouped chain items.

Remove Grouped Chain items that are no longer in the preferences.

_remove_abandoned_groups()[source]

Remove abandoned groups.

Remove groups that are no longer subgroups of the current group.

_set_shortcut_keys()[source]

Set Shortcut Keys.

Modify ui actions with preference values. The preferences values are already set to the config in main-window.

_sort_items()[source]

Sort Items.

Sort items in the group.

_update_actions()[source]

Update Actions.

Enable or disable actions depending on model state.

_update_grouped_chain_items()[source]

Update grouped chain items.

Remove, add and update all grouped_chain_items in the group.

_update_grouped_chain_models()[source]

Update grouped chain models.

Update model of all grouped_chain_items to the values in the current preferences.

_update_groups()[source]

Update Groups.

Remove, add and update subgroups.

_update_group_models()[source]

Update group models.

Update the models of all subgroups.

_update_title()[source]

Update Title.

Update Title of the group.

_update_state()[source]

Update State.

Update state of the group item. Change the action text for the context menu and the group indicator. Hide the root-group name.

contextMenuEvent(event)[source]

Context Menu Event.

Qt overload to build a custom context menu from the item’s actions and display the menu.

Parameters:event (Qt.Event) – event that triggered the context menu.
mouseDoubleClickEvent(event)[source]

Mouse double click event.

Qt overload to handle that the user double clicked on a group. Toggles the collapse state.

Parameters:event (Qt.event) – event that triggered this eventhandler.
accept_mime_data(mime_data)[source]

Accept Mime Data.

Handler for drag and drop to decide if the given mime_data can be accepted by the widget. Return True when the given mime_data has the correct format and may be evaluated its contents.

Parameters:mime_data (QtCore.QMimeData) – Mime data of element to be dropped.
Returns:bool – True when the mime data can be accepted.
Raises:NotUniqueError – When the info in the mimedata indicates that the drop will configure a invalid parent.
drag_end()[source]

Drag End.

Cleanup any state that was setup during dragging.

drag_move(position)[source]

Drag Move.

Evaluate how the drag would be handled when it is dropped at the given position. Evaluate if the drop is before, after or into the group.

Parameters:position (QtCore.QPoint) – Current point of the drag.
drop_mime_data(mime_data)[source]

Drop mime data.

Handle the dropped mime_data after drop has finished.

Parameters:mime_data (QtCore.QMimeData) – Mime data of the drop
Returns:bool – True if the drop was handled with success.
Raises:NotUniqueError – When a drop would create a invalid parent.
get_mime_data()[source]

Get Mime Data.

Return QtCore.QMimeData() that contains a dropable handle.

Returns:QtCore.QMimeData – Dropable handle.
paint_drop_regions(painter)[source]

Paint drop regions.

Paint drop regions based on the status of the widget.

Parameters:painter (QtGui.QPainter) – Painter of the widget.
closeEvent(event)[source]

Close Event.

Widget was closed.

Parameters:event (Qt.QEvent) – Event detail.
add_item(widget_item)[source]

Add Item.

Add widget to the group_items.

Parameters:widget_item (list_widget_item) – item to be added to the group_items
ensure_visible()[source]

Ensure Visible.

Evaluate the position of the group in the parent scroll area and make the group visible with a margin.

get_account_widgets()[source]

Get Items Widgets.

Return the account list items.

get_group_widgets()[source]

Get Items.

Return the account group items.

get_root()[source]

Get Root.

Return True when this item is the root-item.

has_item(model_instance)[source]

Has Item. Check if the layout contains a widget for the given model_instance

Parameters:model_instance (model.chain_item) – Item with id field.
Returns:bool – True when the item exists in the widget layout.
remove_item(widget_item)[source]

Remove Item.

Remove and delete item from the widget layout.

Parameters:widget_item (list_item_widget) – item to be removed.
rename()[source]

Rename.

Allow rename from the account_list by forwarding to private method.

reset()[source]

Reset.

Reset group

set_mainwindow(mainwindow)[source]

Set Main Window.

Set the mainwindow (with its backend) to the widget for receiving model updates and triggering actions.

Parameters:mainwindow (mainwindow) – main application widget.
set_model(model)[source]

Set Model.

Set model for the widget.

Parameters:model (model.group_item) – Group item that represents the item in the list.
set_root(is_root)[source]

Set Root.

Set the item root state. When root is true, the content margins are removed.

update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update Model

Update model cache instances with the given models. Update the group items after the collections are updated.

Parameters:
  • accounts_model (list) – All configured accounts
  • groups_model (list) – All configured groups
  • grouped_accounts_model (list) – All configured accounts with a group parent (unchained)
  • chains_model (list) – All configured accounts with their chain relation.
root = <Property object>
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.account_list
class netsplice.gui.account_list.account_list.account_list(parent=None)[source]

Bases: PySide.QtGui.QWidget

Widget Listing the Accounts organized in groups and chains.

account_list
    group_item(root) - invisible
        group_item(global) - immutable
            grouped_chain_item
                chain_item
                    account_item
                    chain_list
                        chain_item
                            account_item
backend_done = <PySide.QtCore.Signal object>
drop_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
account_item_dropped = <PySide.QtCore.Signal object>
account_item_dropped_on_group = <PySide.QtCore.Signal object>
account_create = <PySide.QtCore.Signal object>
account_log = <PySide.QtCore.Signal object>
account_settings = <PySide.QtCore.Signal object>
group_create = <PySide.QtCore.Signal object>
group_settings = <PySide.QtCore.Signal object>
group_dropped = <PySide.QtCore.Signal object>
selected = <PySide.QtCore.Signal object>
_backend_done()[source]

Backend Done.

Ensure that the toolbar actions are enabled correct. Slot that should be called whenever an item called a backend function.

_backend_model_changed(model)[source]

Backend model changed.

The backend model has changed - update the toolbar.

_connect()[source]

Connect Action.

Handler for the toolbar connect action.

_connect_actions()[source]

Connect Actions.

Connect signals with callbacks.

_create()[source]

Create Action Handler.

Trigger the account_create signal.

_create_root_item()[source]

Create Root Item.

Create item that holds all groups and connect the signals that will be emitted from the item. The item will be initialized with the current model and update itself when the backend changes.

_delete()[source]

Delete Action.

Handler for the toolbar delete action.

_disconnect()[source]

Disconnect Action.

Handler for the toolbar disconnect action.

_drop_account(dropped_account_id, account_id, weight)[source]

Drop Account.

Handle that a account was dropped with the given weight. Evaluates if this is a chain-drop or a group-drop

Parameters:
  • dropped_account_id (string) – id of the dropped account
  • account_id (string) – id of the account that received the drop
  • weight (int) – weight to indicate before/after drop
_drop_account_on_group(dropped_account_id, group_id, weight)[source]

Drop Account on Group.

Account was dropped on the given group

Parameters:
  • dropped_account_id ([type]) – [description]
  • group_id ([type]) – [description]
  • weight ([type]) – [description]
_drop_done(response)[source]

Drop Done.

Reget the preferences after the drop completed.

Parameters:response (string) – json of the api-response.
_drop_group(dropped_group_id, group_id, weight)[source]

Drop Group.

Drop a group on another group

Parameters:
  • dropped_group_id (string) – group that was dropped.
  • group_id (string) – group that received the drop.
  • weight (int) – weight for before/after position.
_get_current_widget()[source]

Return the current selected widget’s account or None.

_get_first_account_item(widget)[source]

Get Account Item.

Get Account Item from the given widget that may be the parent of a account_item.

Parameters:widget (QWidget) – Widget that may contain a account_item, None when there is no account item.
_item_selected(item_widget)[source]

Item Selected.

Mark the given item selected and update the toolbar.

Parameters:item_widget (QWidget) – list_item widget
_reconnect()[source]

Reconnect Action.

Handler for the toolbar reconnect action.

_select_first_item()[source]

Select First Item.

Select the first item. Try to find a account.

_setup_toolbar()[source]

Setup Toolbar.

Setup the actions and add them to the toolbar.

_show_logviewer()[source]

Show Logviewer Action.

Handler for the toolbar show log action.

_show_settings()[source]

Show Settings Action.

Handler for the toolbar settings action.

_update_toolbar_actions()[source]

Update Toolbar Actions.

Toggle enabled and disabled toolbar actions depending on the selected item.

contextMenuEvent(event)[source]

Context Menu Event.

Qt overload to build a custom context menu from the item’s actions and display the menu.

Parameters:event (Qt.Event) – event that triggered the context menu.
set_mainwindow(mainwindow)[source]

Set the mainwindow (with its backend) to the widget for receiving model updates and triggering actions.

Parameters:mainwindow (mainwindow) – main application widget.
update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update Model.

The accounts changed in the backend. Store the model and make sure there is a root_item visible. When no accounts are available, display a message that allows the creation of a new account.

Parameters:
  • accounts_model (list) – list of all accounts
  • groups_model (list) – list of all groups
  • grouped_accounts_model (list) – list of all accounts with association to a group and a weight
  • chains_model (list) – list of all accounts in their chain location
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.account_item
class netsplice.gui.account_list.account_item.account_item(parent, mainwindow=None, model=None)[source]

Bases: netsplice.gui.widgets.list_item.list_item

Custom Widget Listing the Accounts.

Item to be displayed in an account_group_item.

Initialize Module.

Setup default values for a account item.

account_create = <PySide.QtCore.Signal object>
account_item_dropped = <PySide.QtCore.Signal object>
account_item_dropped_in_chain = <PySide.QtCore.Signal object>
account_log = <PySide.QtCore.Signal object>
account_reset = <PySide.QtCore.Signal object>
account_settings = <PySide.QtCore.Signal object>
backend_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
chain_move_left = <PySide.QtCore.Signal object>
chain_move_right = <PySide.QtCore.Signal object>
chain_move_up = <PySide.QtCore.Signal object>
chain_move_down = <PySide.QtCore.Signal object>
chain_collapse = <PySide.QtCore.Signal object>
group_step_move_account = <PySide.QtCore.Signal object>
_account_item_dropped(dropped_account_id, account_id, weight)[source]

Account Dropped.

Account was dropped on this account item.

Parameters:
  • dropped_account_id (string) – id of dropped account
  • account_id (string) – id of account dropped on (self)
  • weight (integer) – weight of drop (< before > after)
_account_item_dropped_in_chain(dropped_account_id, account_id, weight)[source]

Account Dropped in Chain.

Account was dropped on this account item with the intend to make it a child of the target.

Parameters:
  • dropped_account_id (string) – id of dropped account
  • account_id (string) – id of account dropped on (self)
  • weight (integer) – weight of drop (< before > after)
_animate_connecting_icon()[source]

Animate connecting icon.

Self-calling function that sets the connecting icon of the item to the next state. Will reset, when the internal connecting state is False.

_backend_done()[source]

Backend Done.

Rerequest the current model

_backend_model_changed(model)[source]

Model Changed.

Handle the backend model change. Find connection for the account_id representing this icon and update the status.

_can_drop_account(account_id)[source]

Can Drop Account.

Evaluate if the given account_id can be dropped on/before/after the current account.

Parameters:account_id (string) – account_id to be dropped
Returns:bool – True when drop is ok, False if not
_cancel_connect()[source]

Cancel Connect.

Action handler that triggers a disconnect to the backend. Set internal state to get updated ui.

_collapse()[source]

Collapse.

Action handler that toggles the chain_list to collapse.

_connect()[source]

Connect.

Action handler that triggers connect to the backend. Set internal state to get updated ui.

_connect_all()[source]

Connect All.

Action handler that triggers connect of all chain children to the backend. Set internal state to get updated ui.

_connect_actions()[source]

Connect Actions.

Connect the ui-element signals with member functions.

_delete()[source]

Delete.

Action handler that triggers delete in the preference backend.

_disconnect()[source]

Disconnect.

Action handler that triggers disconnect in the backend. Set internal state to get updated ui.

_get_collapsed()[source]

Get Collapsed.

Return the collapsed state for properties.

_get_connected()[source]

Get Connected.

Return the connected state for properties.

_get_connecting()[source]

Get Connecting.

Return the connecting state for properties.

_get_default_route()[source]

Get Default Route.

Return the connecting state for properties.

_get_enabled()[source]

Get Enabled.

Return the enabled state for properties.

_get_failed()[source]

Get Failed.

Return the failed state for properties.

_get_connect_mode()[source]

Get Parent connect Mode.

Return the failed state for properties.

_group()[source]

Group.

Create a Group and move the current item into that group.

_grouped_account_sort_weight(grouped_account)[source]

Grouped Account Sort Weight.

Return the weight for the grouped account for sorting.

Parameters:grouped_account (model.grouped_account) – account to extract the weight from.
Returns:int – weight of grouped account.
_group_step_move_account(create_json)[source]

Group step move account.

Second step in grouping a account. Move the current account into the new group.

Parameters:create_json (string) – json of create group response (contains group_id)
_load_pixmaps()[source]

Load Pixmaps.

Load Pixmaps that will be used for certain states. This avoids loading them again and again.

_move_left()[source]

Move Left.

Action Handler for Moving in chains.

_move_right()[source]

Move Right.

Action Handler for Moving in chains.

_move_up()[source]

Move Up.

Action Handler for Moving in groups. Set a new weight that is before the previous item.

_move_down()[source]

Move Down.

Action Handler for Moving in groups. Set a new weight that is after the next item.

_new()[source]

New Account.

Trigger the new account dialog.

_reconnect()[source]

Reconnect.

Action Handler that triggers reconnect in the backend. Set the internal state to update the UI.

_reset()[source]

Reset.

Action handler that triggers a reset for the current account in the backend.

_set_connected(state)[source]

Set Connected.

Property setter that ensures recalculation of item stylesheet.

_set_connecting(state)[source]

Set Connecting.

Property setter that ensures recalculation of item stylesheet.

_set_enabled(state)[source]

Set Enabled.

Property setter that ensures recalculation of item stylesheet.

_set_default_route(state)[source]

Set Default Route.

Property setter that ensures recalculation of item stylesheet.

_set_failed(state)[source]

Set Failed.

Property setter that ensures recalculation of item stylesheet.

_set_connect_mode(mode)[source]

Set Parent connect mode.

Property setter that ensures recalculation of item stylesheet.

_set_shortcut_keys()[source]

Set Shortcut Keys.

Modify ui actions with preference values.

_set_state_connected(connection_model_instance)[source]

Set state connected.

Set the item to be connected by updating the ui appropriate and enabling and disabling actions.

_set_state_connecting()[source]

Set state connecting.

Set the item to be connecting by updating the ui appropriate and enabling and disabling actions.

_set_state_disconnected()[source]

Set state disconnected.

Set the item to be disconnected by updating the ui appropriate and enabling and disabling actions.

_set_state_failed()[source]

Set state failed.

Set the item to be failed by updating the ui appropriate and enabling and disabling actions.

_show_logviewer()[source]

Show Logviewer.

Action handler that shows the logviewer for the current account.

_show_settings()[source]

Show Settings.

Action handler that shows the settings for the current account.

_readable_velocity(bytes_per_second)[source]

Readable Velocity

Create a readable string for the given bytes_per_second.

1 MB = 1000000 bytes (= 10002 B = 106 B) is the definition recommended by the International System of Units (SI) and the International Electrotechnical Commission IEC.[2] This definition is used in networking contexts and most storage media, particularly hard drives, flash-based storage,[3] and DVDs, and is also consistent with the other uses of the SI prefix in computing, such as CPU clock speeds or measures of performance.

Parameters:bytes_per_second (int) – bytes per second
_ungroup()[source]

Ungroup.

Take the current item and place it in the parent group.

_update_velocity(connection_model_instance)[source]

Update Velocity.

Update text on connected item. Prefer UDP velocity, fallback to tap velocity.

contextMenuEvent(event)[source]

Context Menu Event.

Creates a QMenu with actions for an account.

mouseDoubleClickEvent(event)[source]

Double click Event.

Connects the account.

accept_mime_data(mime_data)[source]

Accept Mime Data.

Drag&Drop handler for mime-data that accepts dragged accounts that are not the current account list item.

get_mime_data()[source]

Get Mime Data.

Return the current account as dragable item.

drag_end()[source]

Drag End Handler.

Resets values that are used during drag & drop.

drag_move(position)[source]

Drag Move Handler.

Evaluates the given position on the item and sets before/after values based on the position.

drop_mime_data(mime_data)[source]

Drop Event Handler.

Emits an account_item_dropped signal with weight based on the drop position.

paint_drop_regions(painter)[source]

Painter for drop Regions.

Draws a transparent indicator on the item that hints the user on the drop position.

reset()[source]

Reset the account.

Changes the values to defaults after a previous failed connection.

set_collapsed(collapsed)[source]

Set Collapsed.

Set the account item collapsed (eg has chain children that are invisible).

Parameters:collapsed (bool) – collapse state.
set_mainwindow(mainwindow)[source]

Set Main Window.

Set the main window for the widget to access the backend dispatcher. The owning widget will call this method when the widget is created through the ui-file, otherwise the constructor is used. Connect the model updates with the backend of the mainwindow.

Parameters:mainwindow (gui.mainwindow) – instance of the gui mainwindow
set_model(model)[source]

Set Model.

Set model for the widget instance.

Parameters:model (model.account_list_item) – account_item with name and type
update_actions()[source]

Update Actions.

Update the actions of the contextmenu by evaluating the model values.

update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update model.

Update the values of the account based on the latest model changes.

update_status(connection_model_instance)[source]

Update Account Status.

Updates the actions during a active connection. Change velocity display, and call setters from model state.

Parameters:connection_model_instance (model.connection_list_item) – connection instance for the given account.
update_values(account_model_instance)[source]

Update Values.

Updates the visible attributes of the account: name and icon.

collapsed = <Property object>
connected = <Property object>
connecting = <Property object>
failed = <Property object>
user_enabled = <Property object>
connect_mode = <Property object>
default_route = <Property object>
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.new_item
class netsplice.gui.account_list.new_item.new_item(parent, mainwindow=None, group_item=None)[source]

Bases: PySide.QtGui.QWidget

Custom Widget for accepting external drops.

preferences_changed = <PySide.QtCore.Signal object>
dragEnterEvent(event)[source]

Drag enter event.

Event when something is dragged on the widget.

Parameters:event (Qt.QEvent) – Event that contains details about the drop
dragLeaveEvent(event)[source]

Drag Leave Event.

The user moved the dragable out of the widget.

Parameters:event (Qt.QEvent) – Event that contains details.
dropEvent(event)[source]

Drop Event.

Event that occurs when the user decided to drop the current drag on the widget.

Parameters:event (Qt.QEvent) – Event that contains details.
mousePressEvent(event)[source]

Mouse press event.

Event that occurs when the user clicks on the widget. Create a new account using the mainwindow.

Parameters:event (Qt.QEvent) – Event with details.
paintEvent(paint_event)[source]

Paint Event.

The widgets needs paint (due to update call). Draw a drop indicator when the widget is in_drag_mode.

Parameters:paint_event (Qt.QEvent) – Event with details.
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.grouped_chain_item
class netsplice.gui.account_list.grouped_chain_item.grouped_chain_item(parent, mainwindow=None, model=None)[source]

Bases: netsplice.gui.widgets.list_item.list_item

Widget to wrap ordered chain items (grouped_account_items). Each grouped_chain_item contains a single chain_item.

account_create = <PySide.QtCore.Signal object>
account_item_dropped = <PySide.QtCore.Signal object>
account_log = <PySide.QtCore.Signal object>
account_settings = <PySide.QtCore.Signal object>
closeEvent(event)[source]

Close Event.

Widget was closed.

Parameters:event (Qt.QEvent) – Event detail.
set_mainwindow(mainwindow)[source]

Set Main Window.

Set the main window for the widget to access the backend dispatcher. The owning widget will call this method when the widget is created through the ui-file, otherwise the constructor is used. Connect the model updates with the backend of the mainwindow.

Parameters:mainwindow (gui.mainwindow) – instance of the gui mainwindow
set_model(model)[source]

Set Model.

Set model for the widget instance.

Parameters:model (model.account_list_item) – account_item with name and type
update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update Model

Update model cache instances with the given models. Update the grouped_account items after the collections are updated.

Parameters:
  • accounts_model (list) – All configured accounts
  • groups_model (list) – All configured groups
  • grouped_accounts_model (list) – All configured accounts with a group parent (unchained)
  • chains_model (list) – All configured accounts with their chain relation.
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.chain_item
class netsplice.gui.account_list.chain_item.chain_item(parent, mainwindow=None, model=None)[source]

Bases: netsplice.gui.widgets.list_item.list_item

Custom Widget to display account_item’s in chains.

account_create = <PySide.QtCore.Signal object>
account_item_dropped = <PySide.QtCore.Signal object>
account_log = <PySide.QtCore.Signal object>
account_new = <PySide.QtCore.Signal object>
account_settings = <PySide.QtCore.Signal object>
backend_done = <PySide.QtCore.Signal object>
backend_failed = <PySide.QtCore.Signal object>
chain_move_left = <PySide.QtCore.Signal object>
chain_move_right = <PySide.QtCore.Signal object>
chain_move_up = <PySide.QtCore.Signal object>
chain_move_down = <PySide.QtCore.Signal object>
move_done = <PySide.QtCore.Signal object>
preferences_changed = <PySide.QtCore.Signal object>
_connect_widget(widget)[source]

Connect Widget.

Connect the given widget with signals.

Parameters:widget (QWidget) – list item widget
_drop_into_chain(dropped_account_id, account_id, weight)[source]

Drop Into chain.

Handle that dropped_account_id should have account_id as new chain_parent.

Parameters:
  • dropped_account_id (string) – account_id that was dropped
  • account_id (string) – account_id that is the new parent
  • weight (int) – position in the children
_move_done(result)[source]

Move Done.

Move in chain has completed, rerequest the preferences to ensure correct display.

Parameters:result (string) – backend response for move.
_move_left(account_id)[source]

Move Left.

Move the given account id to its parent parent with a weight that it will be positioned after its current parent

Parameters:account_id (string) – account to be moved.
_move_right(account_id)[source]

Move Right.

Move the given account_id to have the parent of its predecessor in the current list. Will use a weight to be last in the new parent.

Parameters:account_id (string) – account to be moved.
_move_up(account_id)[source]

Move Up.

Reduce weight for the given account_id.

Parameters:account_id (string) – account to be moved.
_move_down(account_id)[source]

Move Down.

Increase weight for the given account_id.

Parameters:account_id (string) – account to be moved.
_toggle_collapse()[source]

Toggle Collapse.

Toggle collapse for the chain list if there is one.

_update_chain_list_items()[source]

Update chain item.

Create the chain_list widget when the chain_item has children. When no children are available, the list widget is removed. When no mainwindow is available, this method does nothing.

_update_state()[source]

Update State.

Update state of the chain item with the current model values.

closeEvent(event)[source]

Close Event.

Widget was closed.

Parameters:event (Qt.QEvent) – Event detail.
accept_mime_data(accept_mime_data)[source]

Accept Mime Data.

Return True when the given mime_data has the correct format and may be evaluated its contents.

Parameters:mime_data (QtCore.QMimeData) – Mime data of element to be dropped.
Returns:bool – True when the mime data can be accepted.
update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update Model

Update model cache instances with the given models.

Parameters:
  • accounts_model (list) – All configured accounts
  • groups_model (list) – All configured groups
  • grouped_accounts_model (list) – All configured accounts with a group parent (unchained)
  • chains_model (list) – All configured accounts with their chain relation.
set_mainwindow(mainwindow)[source]

Set the mainwindow (with its backend) to the widget for receiving model updates and triggering actions.

Parameters:mainwindow (mainwindow) – main application widget.
set_model(model)[source]

Set Model.

Set model for the widget.

Parameters:model (model.group_item) – Group item that represents the item in the list.
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.account_list.chain_list
class netsplice.gui.account_list.chain_list.chain_list(parent, chain_item_type, mainwindow=None, model=None)[source]

Bases: netsplice.gui.widgets.list_item.list_item

Custom widget to have multiple chain_items on the same level.

account_item_dropped = <PySide.QtCore.Signal object>
account_create = <PySide.QtCore.Signal object>
account_log = <PySide.QtCore.Signal object>
account_settings = <PySide.QtCore.Signal object>
_add_new_chain_items()[source]

Add chain items.

Create a chain item in the current group for each item that is in the model but not in the widget.

_order_weighted_widget(widget_item)[source]

Order weighted Widget.

Return the weight of the given widget item. This method is used for the sort.

Parameters:widget_item (list_widget_item) – Widget in the group.
Returns:integer – weight of the widget.
_remove_abandoned_chain_items()[source]

Remove Abandoned Chain items.

Remove items that are no longer in the preferences of the backend.

_sort_items()[source]

Sort Items.

Sort items in the chain list.

_update_child_chain_items()[source]

Update Child Chain Items.

Update the items that are direct children in the chain_list. Remove abandoned items and add new items.

closeEvent(event)[source]

Close Event.

Widget was closed.

Parameters:event (Qt.QEvent) – Event detail.
accept_mime_data(accept_mime_data)[source]

Accept Mime Data.

Return True when the given mime_data has the correct format and may be evaluated its contents.

Parameters:mime_data (QtCore.QMimeData) – Mime data of element to be dropped.
Returns:bool – True when the mime data can be accepted.
add_item(widget_item)[source]

Add Item.

Add a widget to the layout

Parameters:widget_item (list_item_widget) – Widget to be added.
has_item(model_instance)[source]

Has Item.

Check if the layout contains a widget for the given model_instance

Parameters:model_instance (model.chain_item) – Item with id field.
Returns:bool – True when the item exists in the widget layout.
is_collapsed()[source]

Is Collapsed.

Return True if the list is in collapsed state.

remove_item(widget_item)[source]

Remove Item.

Remove and delete item from the widget layout.

Parameters:widget_item (list_item_widget) – item to be removed.
set_collapsed(state)[source]

Set Collapsed

Set the chain list visibility.

set_mainwindow(mainwindow)[source]

Set Main Window.

Set the mainwindow (with its backend) to the chain list widget for receiving model updates and triggering actions.

Parameters:mainwindow (mainwindow) – main application widget.
set_model(model)[source]

Set Model.

Set model for the widget.

Parameters:model (model.chain_item) – Chain item that represents the parent of all chain_items in the list.
update_model(accounts_model, groups_model, grouped_accounts_model, chains_model)[source]

Update Model

Update model cache instances with the given models. Update the chain items after the collections are updated.

Parameters:
  • accounts_model (list) – All configured accounts
  • groups_model (list) – All configured groups
  • grouped_accounts_model (list) – All configured accounts with a group parent (unchained)
  • chains_model (list) – All configured accounts with their chain relation.
staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.logviewer.log_list_view
class netsplice.gui.logviewer.log_list_view.log_list_view(parent)[source]

Bases: PySide.QtGui.QTreeView

Log List View.

Treeview with custom context menu.

Initialize the widget.

Initialize tree and actions.

_copy_all()[source]

Copy All.

Copy all log items as plaintext to clipboard.

_copy_selected()[source]

Copy Selected.

Copy selected log items as plaintext to clipboard.

get_all_as_text()[source]

Get All as Text.

Get all items as text from model.

get_selected_as_text()[source]

Get All as Text.

Get all items as text from model.

contextMenuEvent(event)[source]

Context Menu Event.

Protected Overload to create a context menu for the item.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.logviewer.model.log_gui_model
class netsplice.gui.logviewer.model.log_gui_model.log_gui_model(parent)[source]

Bases: PySide.QtCore.QAbstractItemModel

GUI Model to be used by a tree or listview.

Implements a filter mechanism and the configuration of the columns.

Initialize.

Parameters:parent – Owner of the model (usually logviewer)
add_log_item(log_item_instance)[source]

Add an item to the collection.

Adds the given item to the log_items.

columnCount(parent)[source]

Column count.

QAbstractItemModel override.

data(index, role)[source]

Data for Index and Role.

QAbstractItemModel override. Configures each index size and alignment. Returns background color and tooltip. Returns value for the cells.

data_date(log_item_instance)[source]

Date Data for log_item_instance.

Return the date formatted according to log_config.

format_log_item(log_item)[source]

Format a log item.

Returns a plaintext representation for the logitem.

headerData(section, orientation, role)[source]

Label data for header.

QAbstractItemModel override. Returns config values for columns.

index(row, column, parent)[source]

Return Index for row, column in the given parent.

QAbstractItemModel override. Parent is ignored as the log-model is a flat model.

is_hidden(row)[source]

Filter helper.

Evaluates if according to the configured filters the item in the given row should be hidden.

get_filter()[source]

Filter helper.

Evaluate a printable filter from the current settings.

parent(index)[source]

Return parent.

QAbstractItemModel override.

reset()[source]

Reset Model.

QAbstractItemModel override.

reset_default_size()[source]

Reset the default size with config values.

rowCount(parent)[source]

Row count.

QAbstractItemModel override. Returns the length of the log_items list for invalid parent (root) and 0 for any other index.

list_to_plain_text(index_list)[source]

Create plaintext for log items.

Returns a line for each item that is not filtered and is selected. Avoids multiple same items from index_lists created by qt.

set_extra_filter(extra_filter)[source]

Set extra filter.

Parameters:extra_filter – Dictionary with values in log_item extra.
set_level_filter(level_filter)[source]

Set level filter.

Parameters:level_filter – Dictionary with levels that should be filtered.
set_text_filter(text_filter, case_sensitive=False)[source]

Set text filter.

Parameters:
  • text_filter – text to be found in the message of a log_item
  • case_sensitive – ignore case for find.
to_plain_text()[source]

Create plaintext for log items.

Returns a line for each item that is not filtered.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.logviewer.logviewer
class netsplice.gui.logviewer.logviewer.logviewer(parent, dispatcher)[source]

Bases: netsplice.gui.widgets.dialog.dialog

Logviewer for backend logs.

Dialog that displays a history of the logged messages in the app.

Initialize the widget.

Parameters:
  • parent – The widget that owns the logviewer (usualy mainwindow)
  • dispatcher – backend dispatcher for model change events
_paste_ok = <PySide.QtCore.Signal object>
_paste_error = <PySide.QtCore.Signal object>
_backend_logs_deleted_signal = <PySide.QtCore.Signal object>
_backend_logs_failed_signal = <PySide.QtCore.Signal object>
_backend_logs_received_signal = <PySide.QtCore.Signal object>
_apply_filter()[source]

Apply the current filters to the listview.

Set rows visible or hidden based on the currently configured filters. Tests each row and aggregates and displays statistic values.

_backend_failure(message)[source]

Backend failure handler.

The backend signaled a failure receiving the latest log entries. Do nothing, the next request will work or we are shutting down anyway.

_backend_gone()[source]

Backend gone.

Take the local (stdout) logs and put them into the logview.

_backend_logs_received(model_json)[source]

Handle the requested log entries.

Loads all received entries. The requested entries should only be new items.

_backend_model_changed(model)[source]

Handle the change of the backend model.

Extract the log_index that indicates the last log_index in the backend model. When the last known log_index is smaller, request the backend to send the log with that offset.

_clear_log()[source]

Clear Log.

Clear all log items in gui and backend.

_close_gone_gui()[source]

Close gone Gui.

Close the application when the backend is gone.

_close_window()[source]

Close Window.

Close the logview window.

_connections_create()[source]

Make GUI Connections.

Connect signals with member functions.

_connections_disconnect()[source]

Disconnect GUI Connections.

Disconnect signals from member functions.

_filter_by_text(text)[source]

Set the text to use for filtering logs in the log window.

Parameters:text (str) – the text to compare with the logs when filtering.
_filter_changed()[source]

Set the log_model filters from the GUI values.

Enable or disable the action_clear_filter button.

_get_logs(index)[source]

Get Logs.

Request Backend to send logs that have at least the given index.

_hidden_ui_hint()[source]

Hidden ui hint.

Highlight the hidden filter so the user notifies that new items may be available but hidden.

_hidden_ui_hint_remove()[source]

Remove Hidden ui hint.

Remove a previously set highlight stylesheet.

_load_new_items(new_log_items)[source]

Load the previous logged messages in the widget.

Schedules a scroll_to_end when the scrollbar is currently at the end and ensures that only the maximum amount of items is displayed.

_logs_deleted()[source]

Handle Logs Deleted.

When the backend has deleted all logs, the items in the GUI model are removed.

_preferences_changed(model)[source]

Handle Preference model changed.

To store the accounts for displaying account filter.

_save_log_to_file()[source]

Safe the current filtered log as text to file.

Let the user save the current log to a file. Builds a string of log entries from the model, opens a save-as dialog and writes the file.

_schedule_scroll_to_end()[source]

Schedule that the listview is scrolled to the end.

Start a timer when no timer is active so the scroll happens after all items are added.

_schedule_update_date()[source]

Schedule that the listview date is updated.

Start a timer when no timer is active and the config is timeago

_update_date()[source]

Update date.

Update the date by forcing the list to redraw and therefore reget the DisplayRole

closeEvent(event)[source]

Widget Close event.

Disconnect connections to events that should be off when not visible.

hideEvent(event)[source]

Widget Hide event.

Disconnect connections to events that should be off when not visible.

showEvent(event)[source]

Widget Show event.

Create connections to events that should be off when not visible.

scroll_to_end()[source]

Scroll the listview to the end.

Set the verticalScrollBar to its maximum value.

_show_account_filter(account_id)[source]

Display account name for given account id.

Evaluate the account name for the given account_id and display the name in the status.

reset_filter()[source]

Reset the filters and the UI Filter configuration.

Reset all private filter values (extra). Reset all ui elements. Call filter changed so the reseted values are applied.

set_filter(filter=None)[source]

Set a filter to the current log view.

The filter is provided using a dict with the following structure:

{
    'extra': {...},  # extra keys used for filtering
    'text': 'string',  # string that will be displayed
    'case_sensitive': True,  # Boolean toggles case sensitivity
    'level': 'DEBUG,INFO'  # logger levels to be displayed
}

When keys are not in the dict, they will not change the filter. The filter will change the state of the UI. When filter is None or not a dict, all filters will be reset.

staticMetaObject = <PySide.QtCore.QMetaObject object>
netsplice.gui.model.group_item
class netsplice.gui.model.group_item.group_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.account_list
class netsplice.gui.model.account_list.account_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of account_list_item_model.

add_account(account_model_instance)[source]

Add Account.

Add the given (module) account_model_instance to the account_list by creating a new instance, populate it with values and append it to the collection.

delete_account(account_id)[source]

Delete Account.

Delete Account with given account_id.

find_by_id(account_id)[source]

Find by Id.

Return the account with the given id or raise a NotFoundError.

netsplice.gui.model.grouped_account_list
class netsplice.gui.model.grouped_account_list.grouped_account_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

add_account(group_id, account_id)[source]
find_by_id(account_id)[source]
find_by_group_id(group_id)[source]
netsplice.gui.model.group_list
class netsplice.gui.model.group_list.group_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

get_parent_groups(root_group)[source]

Get Parent Groups.

Return a list of groups that are parent to the given root_group. When a parent is listed multiple times, a NotUniqueError is raised.

:param root_group backend.preferences.model.group_item

find_by_id(group_id)[source]
find_root()[source]
find_global()[source]
find_children(group_id)[source]
netsplice.gui.model.ui
class netsplice.gui.model.ui.ui[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.grouped_account_item
class netsplice.gui.model.grouped_account_item.grouped_account_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.backend
class netsplice.gui.model.backend.backend[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.validator.account_type
class netsplice.gui.model.validator.account_type.account_type[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account Types are simple nonempty Strings.

netsplice.gui.model.validator.weight
class netsplice.gui.model.validator.weight.weight[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Weights are simple positive integers, never None.

netsplice.gui.model.validator.group_name
class netsplice.gui.model.validator.group_name.group_name[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Group Names are simple nonempty strings

netsplice.gui.model.validator.account_name
class netsplice.gui.model.validator.account_name.account_name[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account names are simple nonempty strings.

netsplice.gui.model.validator.account_name_value
class netsplice.gui.model.validator.account_name_value.account_name_value[source]

Bases: netsplice.util.model.validator.validator

is_valid(value)[source]

Account names are simple nonempty strings.

netsplice.gui.model.chain_list_item
class netsplice.gui.model.chain_list_item.chain_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.connection_list
class netsplice.gui.model.connection_list.connection_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of connection_list_item_model.

find_by_account_id(account_id)[source]

Find connection by account_id.

Iterate all connections in the list and return when the connection with the given account_id is found. Raise a NotFoundError if the given account_id could not be found.

find_by_id(connection_id)[source]

Find connection by connection id.

Iterate all connections in the list and return when the connection is found. Raise a NotFoundError if the given connection_id could not be found.

netsplice.gui.model.response.grouped_account_update
class netsplice.gui.model.response.grouped_account_update.grouped_account_update[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.group_item
class netsplice.gui.model.response.group_item.group_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.account_list
class netsplice.gui.model.response.account_list.account_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of account_list_item_model.

register_plugins(plugins)[source]
find_by_id(account_id)[source]
netsplice.gui.model.response.grouped_account_list
class netsplice.gui.model.response.grouped_account_list.grouped_account_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

netsplice.gui.model.response.group_list
class netsplice.gui.model.response.group_list.group_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

netsplice.gui.model.response.ui
class netsplice.gui.model.response.ui.ui[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.grouped_account_item
class netsplice.gui.model.response.grouped_account_item.grouped_account_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.backend
class netsplice.gui.model.response.backend.backend[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.connection_list
class netsplice.gui.model.response.connection_list.connection_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of connection_list_item_model.

find_by_account_id(account_id)[source]
netsplice.gui.model.response.group_id
class netsplice.gui.model.response.group_id.group_id[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.chain_item
class netsplice.gui.model.response.chain_item.chain_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.status_list_item
class netsplice.gui.model.response.status_list_item.status_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.gui
class netsplice.gui.model.response.gui.gui[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.preferences
class netsplice.gui.model.response.preferences.preferences[source]

Bases: netsplice.util.model.marshalable.marshalable

register_plugins(plugins)[source]
netsplice.gui.model.response.chain_list
class netsplice.gui.model.response.chain_list.chain_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of chain_item_model.

netsplice.gui.model.response.connection_list_item
class netsplice.gui.model.response.connection_list_item.connection_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.status_list
class netsplice.gui.model.response.status_list.status_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of status_list_item_model.

netsplice.gui.model.response.account_list_item
class netsplice.gui.model.response.account_list_item.account_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.group_update
class netsplice.gui.model.response.group_update.group_update[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.response.event_list
class netsplice.gui.model.response.event_list.event_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of event_model.

netsplice.gui.model.status_list_item
class netsplice.gui.model.status_list_item.status_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.gui
class netsplice.gui.model.gui.gui[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.preferences
class netsplice.gui.model.preferences.preferences[source]

Bases: netsplice.util.model.marshalable.marshalable

set_accounts(account_list_model)[source]
netsplice.gui.model.chain_list
class netsplice.gui.model.chain_list.chain_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

_get_weight(chain_model_instance)[source]

Get Weight.

Return weight for a chain item for sorting.

apply_values()[source]

Apply Values.

Apply values from the preferences to the config so other components can use the values.

find_by_id(account_id)[source]

Find By Id.

Find a chain for an account with the given account_id.

find_by_parent_id(parent_account_id)[source]

Find by Parent Id.

Return a chain that has the given parent_account_id defined as parent. When no account with the parent_account_id is found, a NotFoundError is raised.

get_account_list(chain_accounts, accounts)[source]

Get Account List.

Return a list of account_items that match the ids in the chain_accounts. This method operates on a given chain_accounts list to allow sorting and filtering before getting the accounts.

has_children(account_id)[source]

Has Children.

Check if there are chain children for the given account_id.

Parameters:account_id (string) – account_id to check.
Returns:bool – True when at least one children exists.
get_children(account_id)[source]

Get Children.

Return all direct children for the given account_id.

Parameters:account_id (string) – id for which the children should be compiled
Returns:list – sorted list of chains.
get_children_for_id(account_id)[source]

Get Chain for id

Return the all accounts that are chained to the given account_id.

[a -> b -> c -> d] for b returns [c, d] [a -> b -> c -> d] for a returns [b, c, d]

get_parents_for_id(account_id)[source]

Get Parents for id.

Return the parent chain for the given account_id.

[a -> b -> c -> d] for b returns [a]

get_siblings_for_id(account_id)[source]

Get Siblings for id.

Return the accounts that are chained directly to the given account_id.

[a -> b -> c -> d] for b returns [c] [a -> b -> c -> d] for a returns [b] [a -> b, a -> c] for a returns [b, c]

_order_weighted(chain_item)[source]
netsplice.gui.model.connection_list_item
class netsplice.gui.model.connection_list_item.connection_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

get_account_name(accounts=None)[source]

Get the account name for the current connection.

Find the account for the connections account_id and return the name of the account.

is_connected()[source]

Is Connected.

Model function that returns true when the connection is considered connected.

Returns:bool – True when connected or reconnected.
is_disconnected()[source]

Is Disconnected.

Model function that returns true when the connection is considered disconnected.

Returns:bool – True when disconnected.
is_connecting()[source]

Is Connected.

Model function that returns true when the connection is considered connecting.

Returns:bool – True when in a connecting state.
is_failed()[source]

Is Failed.

Model function that returns true when the connection is considered failed.

Returns:bool – True when failed.
netsplice.gui.model.status_list
class netsplice.gui.model.status_list.status_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of status_list_item_model.

netsplice.gui.model.account_list_item
class netsplice.gui.model.account_list_item.account_list_item[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.grouped_account_update
class netsplice.gui.model.request.grouped_account_update.grouped_account_update[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.preference_value
class netsplice.gui.model.request.preference_value.preference_value[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.chain_create
class netsplice.gui.model.request.chain_create.chain_create[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.chain_update
class netsplice.gui.model.request.chain_update.chain_update[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.group_id
class netsplice.gui.model.request.group_id.group_id[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.account_id
class netsplice.gui.model.request.account_id.account_id[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.group_create
class netsplice.gui.model.request.group_create.group_create[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.grouped_account_create
class netsplice.gui.model.request.grouped_account_create.grouped_account_create[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.request.group_update
class netsplice.gui.model.request.group_update.group_update[source]

Bases: netsplice.util.model.marshalable.marshalable

netsplice.gui.model.event_list
class netsplice.gui.model.event_list.event_list[source]

Bases: netsplice.util.model.marshalable_list.marshalable_list

Marshalable model that contains multiple items of event_model.

netsplice.gui.key_value_editor.key_value_table
class netsplice.gui.key_value_editor.key_value_table.key_value_table(parent=None)[source]

Bases: PySide.QtGui.QTableWidget

Key Value Table.

Custom Editor with keywords as column 0 and value as column 1. Additional Values may be set using the values property of the key_value_item. Items may have a key and value documentation that is exposed as tooltip and identified by the items key.

Initialize Widget.

Initialize widget defaults and members.

values_changed = <PySide.QtCore.Signal object>
value_checked = <PySide.QtCore.Signal object>
edit_row = <PySide.QtCore.Signal object>
row_selected = <PySide.QtCore.Signal object>
_commit_change(item)[source]

Commit Change.

Emit signal with new values if in edit_mode. Ends edit_mode.

_edit_row()[source]

Edit Row.

Signal handler that launches a row editor if one is registered.

_editor_closed(item)[source]

Editor Closed.

End Edit mode.

_editor_started(item)[source]

Editor Started.

Start Edit mode.

_get_restore_value(column, default_value)[source]

Get Restore Value.

Returns the default value for the given column. When there are multiple possible default value, return the first value that no other key contains.

_is_checkable(item)[source]

Test Checkable.

Test if the given item is checkable.

_selection_changed()[source]

Selection Changed.

Called, whenever the selection changes.

_set_row_changed(row)[source]

Set Row Changed.

Indicate the given row that its values differ from the default input.

_set_row_unchanged(row)[source]

Set Row Changed.

Indicate the given row that its values differ from the default input.

_set_row_removed(row)[source]

Set Row Removed.

Reset all styles and indicate the given row that its values exist in the defaults, but not in the current values.

_set_row_inserted(row)[source]

Set Row Removed.

Indicate the given row that its values exist in the defaults, but not in the settings.

set_status_column(column, state_map)[source]

Set State Column.

Set the column that is updated by the table based on the state of the row with the texts from the state_map.

set_value_column(column)[source]

Set Value Column.

Set the column that is used for values and for comparing to default values.

_toggle_checkstate(item)[source]

Toggle Checkstate.

Change the visual for checkable items. Emits change to editor.

add_row(key, values, key_tooltip=None, values_tooltip=None)[source]

Add Row.

Add a row with the given key and values. Optionally define tooltip for the key and values. Handles keys that have been marked as ‘removed’ by get_values Boolean values will be checkboxes.

get_selected_key()