Yet another method of reading the JSON-RPC interface.
The example below reads the result of the latest softwareAudit and displays it as a table. Information about installed sw products are merged in.
All needed files (.html, .js, .css) should be hosted below /usr/share/opsiconfd/static/AngularExample folder. This way no other Webserver is involved and cross scripting complaints by the browser are avoided.
The location would then read https://opsi:4447/AngularExample.
Of course its helpful to load the angular-min.js (or angular.js for development) from an appropriate location. The example uses a local copy.
opsiconfd will currently answer /rpc? requests with Encoding “deflate”, but will anounce it as “gzip”. This currently prevents any browser from even reading the opsiconfd answer. The responsible Worker.py needs a small change to answer requests from a browser and Winst.exe (and other legacy opsi clients) as well. The issue is known and a patch is mentioned in the forums.
Most requests require a logon, some even special rights. The script user should be member of a new group opsireader and the acl.conf file should be prepended with appropriate lines:
getProducts_.* : sys_group(opsireader); sys_group(opsiadmin); opsi_depotserver; self; opsi_client productOnClient_get.* : sys_group(opsireader); sys_group(opsiadmin); opsi_depotserver;self; opsi_client auditSoftwareOnClient_get.* : sys_group(opsireader); sys_group(opsiadmin); opsi_depotserver; self; opsi_client product_get.* : sys_group(opsireader); sys_group(opsiadmin); opsi_depotserver; self; opsi_client
When called as /AngularExample/?{“dns”:“clientID.domain.local”} the SoftwareAudit of the given clientId is displayed. OPSI client names may be entered in an input field at any time and are queried asynchronously.
The strange format of the query string as json has its reason: it is needed for the AndroidApp named OPSI Admin. The App can read barcodes/QRcodes formatted this way in order to open the details of the identified client. Nice feature when the barcode sticks on the client.
What if the barcode appears right on the client's screen? Try this Java app: Opsi-Barcode
/usr/share/opsiconfd/static/AngularExample ├ index.html ├ angular.js ├ swinv.js └ swinv.css
Where all other is loaded from..
<!DOCTYPE html> <html ng-app="opsi-swinv"> <head> <link rel="stylesheet" type="text/css" href="swinv.css" > <script type="text/javascript" src="angular.js"></script> <!-- chenge to angular-min for production --> <!-- script type="text/javascript" src="angular-resource.js"></script --> <script type="text/javascript" src="swinv.js"></script> <script type="text/javascript" src="swpattern.js"></script> </head> <body ng-controller="swinvController as sw"> <div class="swlist"> <input id="inputId" ng-model="sw.clientId"></input> <button ng-click="sw.refresh(sw.clientId)">refresh list</button> <table border > <tr><th>Name</th><th>Version</th><th>Lizenz</th><th>Installationsgrund</th></tr> <tr ng-repeat="swp in sw.products" ng-show="sw.nonMS(swp.name)"> <td>{{swp.name}} </td> <td> {{swp.version}}</td> <td>{{sw.reason(swp.name)}}</td> <td class={{sw.csswarn(swp.name)}}><span ng-show="!sw.isOpsi(swp.name)">{{sw.reason(swp.name)}} </span>{{sw.opsiProdId(swp.name)}}</td> </tr> </table> <br/><label><input type="checkbox" ng-model="sw.showopsiinstalled" />installed OPSI products on this client <span>({{sw.clientId}})</span></label> <div ng-show="sw.showopsiinstalled"> <table border> <tr><th>OpsiProduct</th></tr> <tr ng-repeat="inst in sw.installed"> <td>{{inst}}</td> </tr> </table> </div> <br/><label><input type="checkbox" ng-model="sw.showallprods" />available products</label> <table ng-show="sw.showallprods" border width="100%"> <tr><th>productInfo</th><th>productId</th><th>...............................................</th></tr> <tr ng-repeat="prod in sw.productinfos"> <td>{{prod.productId}}</td> <td>{{prod.name}}</td> <td></td> </tr> </table> </div> </body> </html>
The AngularJS mimic..
(function(){ var app = angular.module('opsi-swinv', []); app.controller('swinvController',function($http,$filter){ this.showallprods=0; this.showopsiinstalled=0; baseurl='/rpc'; logonurl='https://testerMemberOfGroupopsireader:unGuessablePassword@'+window.location.host+'/rpc'; this.clientId="defaultclient.domain.local"; this.auditParams={'id':'jsonrpc','method':'auditSoftwareOnClient_getObjects','params':[]}; this.auditParams.params.push([]); this.auditParams.params.push({"clientId":"defaultclient.domain.local"}); me=this; me['installed']=[]; me['productinfos']=[]; this.jsonParmDns=function(){ this.search=window.location.search; if (this.search.match(/dns/)){ // we believe in barcode like {"dns":"<clientId>"} var qsearch=decodeURIComponent(this.search).match(/{.+dns.+:.+}/); var jsonsearch=decodeURIComponent(qsearch); // get the json return angular.fromJson(jsonsearch).dns; // return JSON.parse(jsonsearch).dns; } return null; }; this.opsicall=function(field,method,arg){ Params={'id':'1','method':method,'params':[]}; if(arg)Params.params[0]=arg; $http.post(baseurl,Params) .success(function(response,stat,head,conf){ // alert("success: "+$filter('json')(response)); me.opsires="success: "+method; me.opsiconf=conf; if(response.error) alert(response.error.message); else me[field]=response.result; }) .error(function(err,stat,head,conf){ me.opsires="error"; alert("err: "+(err)+stat+head+$filter('json')(conf)); me.opsistat=stat; me.opsihead=head; me.opsiconf=conf; }); }; this.logon=function(){ $http.post(logonurl,this.logonParams) .success(function(response){ // alert("success: "+$filter('json')(response)); // call after logon success me.getproductinfos(); me.refresh(me.clientId); }) .error(function(response){ alert("err: "+(response)); }); }; this.getprods=function(id){ this.opsires="calling.. "+id; if(id)this.auditParams.params[1].clientId=id; $http.post(baseurl,this.auditParams) .success(function(response,stat,head,conf){ // alert("success: "+response); if(response.error)alert(response.error.message); me.products=response.result; me.opsistat=stat; me.opsihead=head; me.opsiconf=conf; }) .error(function(err,stat,head,conf){ me.opsires="error"; alert("err: "+(err)+stat+head+$filter('json')(conf)); me.opsistat=stat; me.opsihead=head; me.opsiconf=conf; }); }; // get products on Client with id this.getinstalled=function(id){ this.opsicall('installed','getInstalledLocalBootProductIds_list',id); }; // get all available OPSi products this.getproductinfos=function(){ this.opsicall('productinfos','getProducts_listOfHashes'); }; this.logon(); this.clientId=this.jsonParmDns(); // this.getproductinfos(); this.refresh=function(id){ if(null != id){ this.getprods(id); this.getinstalled(id); } }; this.nonMS=function(name){ // return true; if(name.match(/AddressBook/)) return false; if(name.match(/^Connection Manager/)) return false; if(name.match(/^DirectDrawEx/)) return false; if(name.match(/^DXM_Runtime/)) return false; if(name.match(/^Fontcore/)) return false; if(name.match(/KB[0-9]+/)) return false; if(name.match(/Microsoft/)) return false; if(name.match(/^MPlayer2/)) return false; if(name.match(/^Outlook/)) return false; if(name.match(/^[iI][Ee]/)) return false; if(name.match(/^Windows/)) return false; if(name.match(/^MobileOptionPack/)) return false; if(name.match(/^SchedulingAgent/)) return false; if(name.match(/^WIC/)) return false; return true; } this.pattern=swpattern; this.reason=function(name){ for(p in this.pattern){ if(name.match(p)) return this.pattern[p]; } }; // guess the corresponding OPSI productId from the Windows Product name this.opsiProdId=function(name){ for(i=0; i<this.productinfos.length;i++){ p=this.productinfos[i]; if(p.name && name.match(p.name)||name.match(p.productId)) return p.productId; } return null; }; // convenience: is this an OPSI product? this.isOpsi=function(name){ return (null != this.opsiProdId(name)); }; // mark rows w/o reason/license this.csswarn=function(name){ if(this.isOpsi(name) || this.reason(name)) return "ng-binding"; else return "warn"; }; }); var swpattern={'^Audacity':'OpenSource', '^Java':'Oracle', '^Mozilla ':'Mozilla Public', '^OpenOff':'OpenSource', '^Google':'Google Public', '^Adobe Flash':'Adobe Public', '^OpenSSL':'OpenSource', '^Python':'OpenSource' }; })();
some tweaking of the appearance - anything goes..
body { font-family: "Comic Sans M$", Arial, Helvetica, sans-serif; } table{ width: 100%; border-collapse: collapse; } td, th { font-size: 1em; border: 1px solid #aa0093; padding: 3px 7px 2px 7px; } th { font-size: 1.1em; text-align: left; padding-top: 5px; padding-bottom: 4px; background-color: #d30c6b; color: #ffffff; } label{ background-color: #d30c6b; color: #ffffff; } td.warn { /* enhance unlicensed products*/ color: #000000; background-color: yellow; } .ng-binding{ // background-color: blue; font-family: Courier; }