家用NAS系統openmediavault插件開發

openmediavault 插件開發

OpenMediaVault,簡稱omv,是一個開源的基於Debian Linux的下一代網絡附加存儲(NAS)解決方案。它包含衆多服務,如SSH,(S)FTP,SMB / CIFS,DAAP媒體服務器,RSync,BitTorrent客戶機等。 並具有通過插件可增強的模塊化設計框架特性。


OMV的插件開發,由三個部分組成

  • GUI

  • 配置文件與RPC

  • 模塊與shell腳本

GUI(web界面)

後臺自動掃描以下目錄及其子目錄

/var/www/openmediavault/js/omv/module/admin/
/var/www/openmediavault/js/omv/module/user/
/var/www/openmediavault/js/omv/module/public/

1.在service目錄下新加一個目錄example

/var/www/openmediavault/js/omv/module/admin/service/example

2.新增一個node(左側導航欄的一個tree對象),創建/var/www/openmediavault/js/omv/module/admin/service/example/EXample.js

// Register a node in the navigation tree.
//
// id: 
//     Set the ID of the node.
// path: 
//     Parent path in the navigation view.
// Text: 
//     Service name/title. This is displayed in the navigation.
// icon16: 
//     16x16 pixel icon that is displayed in the navigation tree.
// iconSvg:
 //     SVG icon that is displayed in the navigation view.
 
 OMV.WorkspaceManager.registerNode({
    id: 'example',
    path: '/service',
    text: _('Example'),
    icon16: 'images/example.png',
    iconSvg: 'images/example.svg'});

注:test:_()這裏面的內容能跟隨頁面自動翻譯成各國語言

3.node的主視圖(workspace)

從workspace類派生,創建Settings.js

Ext.define('OMV.module.admin.service.example.Settings', {
    extend: 'OMV.workspace.form.Panel',    
    
    // This path tells which RPC module and methods this panel will call to get 
    // and fetch its form values.
    
    rpcService: 'Example',
    rpcGetMethod: 'getSettings',
    rpcSetMethod: 'setSettings',    
    
    // getFormItems is a method which is automatically called in the 
    // instantiation of the panel. This method returns all fields for 
    // the panel.
    
    getFormItems: function() {        
            return [{           
            
             // xtype defines the type of this entry. Some different types
            // is: fieldset, checkbox, textfield and numberfield. 
            
            xtype: 'fieldset',
            title: _('General'),
            fieldDefaults: {
                labelSeparator: ''
            },            
            // The items array contains items inside the fieldset xtype.
            items: [{
                xtype: 'checkbox',              
                  
                // The name option is sent together with is value to RPC
                // and is also used when fetching from the RPC.
                
                name: 'enable',
                fieldLabel: _('Enable'),                
                // checked sets the default value of a checkbox.
                checked: false
            },
            {
                xtype: 'numberfield',
                name: 'max_value',
                fieldLabel: _('Max value'),
                minValue: 0,
                maxValue: 100,
                allowDecimals: false,
                allowBlank: true
            }]
        }];
    }
});
// Register a panel into the GUI.
//
// path: 
//     We want to add the panel in our example node. 
//     The node was configured with the path /service and the id example.
//     The path is therefore /service/example.//// className: 
//     The panel which should be registered and added (refers to
 //     the class name).
 
 OMV.WorkspaceManager.registerPanel({
    id: 'settings',
    path: '/service/example',
    text: _('Settings'),
    position: 10,
    className: 'OMV.module.admin.service.example.Settings'});

注:定義了rpc,此時在頁面刷新會報錯。“RPC service not found (name=Example)”,此時需要添加rpc文件/usr/share/openmediavault/engined/rpc/example.inc,內容見第二部分

配置文件config.xml和RPC

Config.xml

/etc/openmediavault/config.xml儲存了插件設置項,可以在製作debian包時,寫在postinst腳本里

#!/bin/shset -e

. /etc/default/openmediavault
. /usr/share/openmediavault/scripts/helper-functions

case "$1" in
    configure)
        SERVICE_XPATH_NAME="example"
        SERVICE_XPATH="/config/services/${SERVICE_XPATH_NAME}"

        # Add the default configuration
        if ! omv_config_exists "${SERVICE_XPATH}"; then
            omv_config_add_element "/config/services" "${SERVICE_XPATH_NAME}"
            omv_config_add_element "${SERVICE_XPATH}" "enable" "0"
            omv_config_add_element "${SERVICE_XPATH}" "max_value" "0"
        fi

        # Activate package triggers. These triggers are only set during the
        # package installation.
        dpkg-trigger update-fixperms
        dpkg-trigger update-locale
    ;;

    abort-upgrade|abort-remove|abort-deconfigure)
    ;;

    *)        echo "postinst called with unknown argument" >&2
        exit 1
    ;;esac
    
    #DEBHELPER#
    
    exit 0

The RPC = Remote Procedure Call

RPC文件存儲在/usr/share/openmediavault/engined/rpc目錄下,inc結尾的php文件,rpc文件連接了web GUI和config.xml,使得界面可以控制config文件
example.inc

<?php

require_once 'openmediavault/config.inc';
require_once 'openmediavault/error.inc';
require_once 'openmediavault/notify.inc';
require_once 'openmediavault/rpcservice.inc';

class OMVRpcServiceExample extends OMVRpcServiceAbstract{    

      /**
     * The main event message path.
     *
     * @var string
     */
    private $eventMessagePath = 'org.openmediavault.services.example';    /**
     * Get the base XPath of the service. This is a helper function to avoid 
     * "magic numbers".
     *
     * @return string
     */
     
    private function getXpath()
    {        return '/config/services/example';
    }   
    
     /**
     * Get the name of the RPC service.
     *
     * @return string
     */
     
    public function getName()
    {        return 'Example';
    }   
    
     /**
     * Initialize the RPC service. The RPC methods are registered in this
     * function with $this->registerMethod.
     *
     * @return void
     */
     
    public function initialize()
    {        $this->registerMethod('getSettings');        
             $this->registerMethod('setSettings');
    }    
    
    public function getSettings($params, $context)
    {        
       // $xmlConfig is needed when reading and writing from the configuration.
        global $xmlConfig;        
        
        // Validate the RPC caller context.
        //
        // validateMethodContext takes the currentcontext as the first
        // parameter. The second paramter is the valid context and that can be
        // OMV_ROLE_ADMINISTRATOR, OMV_ROLE_USER or OMV_ROLE_EVERYONE.
        // This is used to make sure that the right user accesses the method.
        $this->validateMethodContext($context, ['role' => OMV_ROLE_ADMINISTRATOR]);        
        // Get the configuration object.
        $object = $xmlConfig->get($this->getXpath());        
        
        // If no data was found, throw an exception and provide the XPath that
        // failed.
        if (is_null($object)) {            
                throw new OMVException(
                OMVErrorMsg::E_CONFIG_GET_OBJECT_FAILED,                
                $this->getXpath()
            );
        }        
        
        // Modify the result data.
        // boolval and intval converts strings and numbers to their boolean
        // and integer value.
        $object['enable'] = boolval($object['enable']);        
        $object['max_value'] = intval($object['max_value']);       
         return $object;
    }    
    
    public function setSettings($params, $context)
    {        
            global $xmlConfig;       
            $this->validateMethodContext($context, array(            
                   "role" => OMV_ROLE_ADMINISTRATOR
        ));       
        
         // Validate the parameters of the RPC service method.
        //
        // OpenMediavault uses JSON Schema to validate parameters. A more
        // detailed specification is provided here http://json-schema.org/
        $this->validateMethodParams(            
             $params,            '{
                "type": "object",
                "properties": {
                    "enable": {
                        "type": "boolean"
                    },
                    "max_value":{ 
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100
                    }
                }
            }'
        );        
        
        // Update the configuration object.
        $object = [            
                   'enable' => boolval($params['enable']),            
                   'max_value' => $params['max_value'],
        ];        
        
        // Update the configuration file. If it fails it throws an exception.
        if ($xmlConfig->replace($this->getXpath(), $object) === false) {            
                     throw new OMVException(
                                OMVErrorMsg::E_CONFIG_SET_OBJECT_FAILED,                
                                $this->getXpath()
            );
        }        
        
        // Notify configuration changes.
        //
        // This will notify event listeners such as the service module
        // to perform certain tasks. The most common one is to mark the
        // service as dirty.
        
        $dispatcher = &OMVNotifyDispatcher::getInstance();        
        $dispatcher->notify(OMV_NOTIFY_MODIFY, $this->eventMessagePath, $object);        
        
        return $object;
    }
}

// Register the RPC service.
$rpcServiceMgr = &OMVRpcServiceMgr::getInstance();
$rpcServiceMgr->registerService(new OMVRpcServiceExample());

模塊與shell腳本

module

RPC用來實現界面修改和獲取配置文件,模塊用來監控和使其修改生效。模塊目錄在/usr/share/openmediavault/engined/module
Example.inc

<?php

require_once 'openmediavault/config.inc';
require_once 'openmediavault/error.inc';
require_once 'openmediavault/initscript.inc';
require_once 'openmediavault/module.inc';

class OMVModuleExample extends OMVModuleServiceAbstract implements
    OMVINotifyListener,    
    OMVIModuleServiceStatus{    
    
    /**
     * The main event message path.
     *
     * @var string
     */
    private $eventMessagePath = 'org.openmediavault.services.example';
    }   
    
     /**
     * Get the base XPath of the service. This is a helper function to avoid 
     * "magic numbers".
     *
     * @return string
     */
    private function getXpath()
    {        return '/config/services/example';
    }   
    
     /**
     * Get the module name.
     *
     * @return string
     */
    public function getName()
    {        return 'example';
    }    
    
    /**
     * Get the module status.
     *
     * @return array
     *
     * @throws OMVException
     */
    public function getStatus()
    {        
         global $xmlConfig;        
         // Get the configuration object.
        $object = $xmlConfig->get($this->getXpath());  
             
         if (is_null($object)) {            
                 throw new OMVException(
                OMVErrorMsg::E_CONFIG_GET_OBJECT_FAILED,                
                $this->getXpath()
            );
        }        
        
        // Return the status of the service. This information is displayed
        // under Diagnostics/Services.
        return array(           
               'name' => $this->getName(),            
               'title' => gettext('Example'),            
               'enabled' => boolval($object['enable']),            
               'running' => false
        );
    }   
    
     /**
     * Generate the configuration.
     *
     * @return void
     *
     * @throws OMVException
     */
    public function applyConfig()
    {        
                global $xmlConfig;        
                $cmd = sprintf('export LANG=C; omv-mkconf %s 2>&1', $this->getName());        
                if (0 !== $this->exec($cmd, $output)) {            
                      throw new OMVException(
                                OMVErrorMsg::E_EXEC_FAILED,                
                                $cmd,
                                implode(PHP_EOL, $output)
            );
        }
    }    
    
    /**
     * Bind listeners.
     *
     * @param OMVNotifyDispatcher $dispatcher
     * @return void
     */
    public function bindListeners(OMVNotifyDispatcher $dispatcher)
    {        
         $moduleMgr = &OMVModuleMgr::getInstance();        
         // Add listeners here. The most common thing is to monitor configuration
        // changes on the service. When the config is changed the module
        // sets itself as dirty (as seen below). Setting a module as dirty
        // makes the apply button appear in the web interface (which in turn
        // calls the applyConfig function on each module with a dirty state).
        
        $dispatcher->addListener(
            OMV_NOTIFY_MODIFY,           
             $this->eventMessagePath,
            [$this, 'setDirty']
        );
    }
}

// Register the module.
$moduleMgr = &OMVModuleMgr::getInstance();
$moduleMgr->registerModule(new OMVModuleExample());

注:模塊註冊了notify,rpc在修改配置文件後會通知notify文件爲dirty狀態,從而在頁面觸發apply change提示。

Shell腳本生成配置文件

在module中的applyconfig函數中,執行了omv-mkconf example命令,該命令調用了/usr/share/openmediavault/mkconf/example腳本
example

set -e. /etc/default/openmediavault
. /usr/share/openmediavault/scripts/helper-functions
OMV_EXAMPLE_XPATH="/config/services/example"OMV_EXAMPLE_CONF="/tmp/example.conf"

cat <<EOF > ${OMV_EXAMPLE_CONF}enable    = $(omv_config_get "${OMV_EXAMPLE_XPATH}/enable")
max_value = $(omv_config_get "${OMV_EXAMPLE_XPATH}/max_value")
EOF

exit 0

注:需要755權限,執行完該腳本將會生成/tmp/example.conf文件。

至此,一個openmediavault的簡單插件源碼已經制作完成,接下來只需要製作包就可以啦。

refer:https://forum.openmediavault.org/index.php/Thread/5600-DIY-Plugin-Development/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章