host.plugin.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. <?php
  2. //Déclaration d'un item de menu dans le menu principal
  3. function host_menu(&$menuItems){
  4. global $myUser;
  5. if(!$myUser->can('host','read')) return;
  6. $menuItems[] = array(
  7. 'sort'=>3,
  8. 'url'=>'index.php?module=host',
  9. 'label'=>'Hébergement',
  10. 'icon'=> 'fab fa-ubuntu',
  11. 'color'=> '#d35400'
  12. );
  13. }
  14. //Cette fonction va generer une page quand on clique sur Hébergement dans menu
  15. function host_page(){
  16. global $_;
  17. if(!isset($_['module']) || $_['module'] !='host') return;
  18. $page = !isset($_['page']) ? 'list.machine' : $_['page'];
  19. $page = str_replace('..','',$page);
  20. $file = __DIR__.SLASH.'page.'.$page.'.php';
  21. if(!file_exists($file)) throw new Exception("Page ".$page." inexistante");
  22. require_once($file);
  23. }
  24. //Fonction executée lors de l'activation du plugin
  25. function host_install($id){
  26. if($id != 'fr.core.host') return;
  27. Entity::install(__DIR__);
  28. $dictionary = new Dictionary();
  29. $dictionary->slug = 'host_machine_os';
  30. if(Dictionary::rowCount(array('slug'=>$dictionary->slug)) ==0){
  31. $dictionary->label = 'Machine / Environnement : Système d\'exploitation';
  32. $dictionary->parent = 0;
  33. $dictionary->state = Dictionary::ACTIVE;
  34. $dictionary->save();
  35. foreach(array(
  36. 'other'=>'Autre',
  37. 'linux_ubuntu'=>'Linux - Ubuntu 12.4',
  38. 'linux_ubuntu'=>'Linux - Ubuntu 14.4',
  39. 'linux_ubuntu'=>'Linux - Ubuntu 16.4',
  40. 'linux_ubuntu'=>'Linux - Ubuntu 18.4',
  41. 'linux_ubuntu'=>'Linux - Ubuntu 20.4',
  42. 'linux_ubuntu'=>'Linux - Ubuntu 22.4',
  43. 'window'=>'Windows server',
  44. ) as $key=>$value){
  45. $item = new Dictionary();
  46. $item->slug = 'host_machine_os_'.$key;
  47. $item->label = $value;
  48. $item->parent = $dictionary->id;
  49. $item->state = Dictionary::ACTIVE;
  50. $item->save();
  51. }
  52. }
  53. }
  54. //Fonction executée lors de la désactivation du plugin
  55. function host_uninstall($id){
  56. if($id != 'fr.core.host') return;
  57. Entity::uninstall(__DIR__);
  58. $dictionary = Dictionary::bySlug('host_machine_os');
  59. if($dictionary!= false && $dictionary->id!=0){
  60. Dictionary::delete(array('parent'=>$dictionary->id));
  61. Dictionary::delete(array('id'=>$dictionary->id));
  62. }
  63. }
  64. //Déclaration des sections de droits du plugin
  65. Right::register("host",array('label'=>"Gestion des droits sur le plugin Hébergement"));
  66. //cette fonction comprends toutes les actions du plugin qui ne nécessitent pas de vue html
  67. function host_action(){
  68. require_once(__DIR__.SLASH.'action.php');
  69. }
  70. //Déclaration du menu de réglages
  71. function host_menu_setting(&$settingMenu){
  72. global $myUser;
  73. if(!$myUser->can('host','configure')) return;
  74. $settingMenu[]= array(
  75. 'sort' =>1,
  76. 'url' => 'setting.php?section=global.host',
  77. 'icon' => 'fas fa-angle-right',
  78. 'label' => 'Hébergement'
  79. );
  80. }
  81. //Déclaration des pages de réglages
  82. function host_content_setting(){
  83. global $_;
  84. if(file_exists(__DIR__.SLASH.'setting.'.$_['section'].'.php'))
  85. require_once(__DIR__.SLASH.'setting.'.$_['section'].'.php');
  86. }
  87. require_once(__DIR__.SLASH.'action.php');
  88. function host_ping_url($url){
  89. $response = array();
  90. try{
  91. if(!preg_match('/^https?:\/\//im', $url)) $url = 'http://'.$url;
  92. $request = @get_headers($url, true);
  93. $response['code'] = 500;
  94. $response['message'] = 'Erreur inconnue';
  95. if(!$request){
  96. $response['code'] = 500;
  97. $response['message'] = 'Impossible de requeter l\'url';
  98. return $response;
  99. }
  100. if(is_array($request) && isset($request[0])){
  101. $headers = array();
  102. // Make more sane response
  103. foreach($request as $h => $v) {
  104. if(is_int($h)){
  105. $headers[$h]['Status'] = $v;
  106. } else {
  107. if(is_string($v)) $headers[0][$h] = $v;
  108. }
  109. }
  110. $lastHeader = end($headers);
  111. $response['code'] = $lastHeader['Status'];
  112. preg_match('/HTTP[^\s]* ([0-9]*) (.*)/i',$lastHeader['Status'],$match);
  113. $response['code'] = $match[1];
  114. $response['message'] = $match[2];
  115. }else{
  116. $response['message'] = 'Pas de détails';
  117. }
  118. }catch(Exception $e){
  119. $response['code'] = 500;
  120. $response['message'] = $e->getMessage();
  121. }
  122. return $response;
  123. }
  124. function host_cron($time){
  125. global $conf,$_;
  126. if(!in_array(date('H:i', $time), array(5,10,15,20,25,30,35,40,45,50,55,0)) && !isset($_['force-host']) ) return;
  127. $errors = array();
  128. $resolved = array();
  129. require_once(__DIR__.SLASH.'Machine.class.php');
  130. require_once(__DIR__.SLASH.'MachineApplication.class.php');
  131. Plugin::need('client/Client');
  132. foreach (MachineApplication::loadAll(array('monitored'=>1), null, null, array('*'), 2) as $key => $application) {
  133. if(empty($application->url)) continue;
  134. $machine = $application->join('machine');
  135. $pendingDown = History::load(array('uid'=>$application->id,'scope'=>'host_application','meta'=>null,'type'=>'down'));
  136. foreach(explode(',',$application->url) as $url){
  137. $response = host_ping_url($url);
  138. echo $response['code'].' : '. $url.PHP_EOL;
  139. if($response['code']!=200){
  140. $item = new History();
  141. $item->uid = $application->id;
  142. $item->scope = 'host_application';
  143. $item->type = 'down';
  144. $item->comment = 'L\'application est en erreur avec le code '. $response['code'].' : '.$response['message'];
  145. if(!$pendingDown){
  146. $errors[] = array('machine'=>$machine,'application'=>$application,'ping'=>$response);
  147. $item->save();
  148. }
  149. }else{
  150. if(!is_object($pendingDown)) continue;
  151. $pendingDown->meta = json_encode(array('resolved' => time()));
  152. $pendingDown->save();
  153. $item = new History();
  154. $item->uid = $application->id;
  155. $item->scope = 'host_application';
  156. $item->type = 'up';
  157. $item->comment = 'L\'application est de nouveau opérationnelle après '.relative_time($item->created,time()).' d\'inactivité';
  158. $item->save();
  159. $resolved[]= array('machine'=>$machine,'application'=>$application,'ping'=>$response,'down'=>$pendingDown);
  160. }
  161. }
  162. }
  163. if(count($errors) > 0){
  164. if(count($errors)>1){
  165. $title = '🔥 '.count($errors).' noms de domaines ne répondent plus';
  166. $message = '<p>Les noms de domaines suivants sont down :<p><ul> ';
  167. }else{
  168. $title = '🔥 '.count($errors).' nom de domaine ne répond plus';
  169. $message = '<p>Le nom de domaine suivant est down :<p><ul> ';
  170. }
  171. foreach($errors as $error){
  172. $machine = $error['machine'];
  173. $application = $error['application'];
  174. $ping = $error['ping'];
  175. $json = json_encode(array(
  176. 'action' =>'putty_open',
  177. 'ip' => $machine->ip
  178. ));
  179. $message .= '<li style="padding:15px;border-bottom:1px solid #cecece;">';
  180. $message .= '<a href="'.$application->url.'">'.$application->url.'</a>';
  181. $message .= ' Hebergement : <strong><a href="dev://'.base64_encode($json).'">'.$machine->label.'</a></strong>';
  182. if(!empty($application->id)) $message .= '<br>Application : <strong>'.$application->label.'</strong>';
  183. if(!empty($machine->ip)) $message .= '<br> IP : <strong>'.$machine->ip.'</strong>';
  184. $message .= '<br> Erreur : <strong>'.$ping['code'].' : '.$ping['message'].'</strong>';
  185. $message .= '</li>';
  186. }
  187. $message .= '</ul>';
  188. // GESTION ENVOI NOTIFICATION
  189. Plugin::callHook('emit_notification',array(
  190. array(
  191. 'label' => $title,
  192. 'html' => $message,
  193. 'type' => "host_url_error",
  194. 'meta' => array('link' => ROOT_URL.'/index.php?module=host'),
  195. 'recipients' => array_unique(explode(',',$conf->get('host_url_notification'))) // recipients contient login
  196. )
  197. ));
  198. }
  199. if(count($resolved) > 0){
  200. if(count($resolved)>1){
  201. $title = '✔️ '.count($resolved).' noms de domaines répondent à nouveau';
  202. $message = '<p>Les noms de domaines suivants sont de nouveau en ligne :<p><ul> ';
  203. }else{
  204. $title = '✔️ '.count($resolved).' nom de domaine répond à nouveau';
  205. $message = '<p>Le nom de domaine suivant est de nouveau en ligne :<p><ul> ';
  206. }
  207. foreach($resolved as $resolvedItem){
  208. $machine = $resolvedItem['machine'];
  209. $application = $resolvedItem['application'];
  210. $ping = $resolvedItem['ping'];
  211. $down = $resolvedItem['down'];
  212. $json = json_encode(array(
  213. 'action' =>'putty_open',
  214. 'ip' => $machine->ip
  215. ));
  216. $message .= '<li style="padding:15px;border-bottom:1px solid #cecece;">';
  217. $message .= '<a href="'.$application->url.'">'.$application->url.'</a>';
  218. $message .= ' Machine : <strong><a href="dev://'.base64_encode($json).'">'.$machine->label.'</a></strong>';
  219. if(!empty($application->id)) $message .= '<br>Application : <strong>'.$application->label.'</strong>';
  220. if(!empty($machine->ip)) $message .= '<br> IP : <strong>'.$machine->ip.'</strong>';
  221. $message .= '<br> Erreur initiale: <strong>'.$down->comment.'</strong>';
  222. if(!is_array($down->meta)) $down->meta = json_decode($down->meta,true);
  223. $message .= '<br> Résolu depuis : <strong>'.date('d/m/Y H:i',$down->meta['resolved']).' (Temps de down: '.str_replace(array('Dans ','Il y a '),'',relative_time($down->created,$down->meta['resolved'])).')</strong>';
  224. $message .= '</li>';
  225. }
  226. $message .= '</ul>';
  227. // GESTION ENVOI NOTIFICATION
  228. Plugin::callHook('emit_notification',array(array(
  229. 'label' => $title,
  230. 'html' => $message,
  231. 'type' => "host_url_error",
  232. 'meta' => array('link' => ROOT_URL.'/index.php?module=host'),
  233. 'recipients' => array_unique(explode(',',$conf->get('host_url_notification'))) // recipients contient login
  234. )
  235. ));
  236. }
  237. }
  238. //Déclaration des settings de base
  239. //Types possibles : voir FieldType.class.php. Un simple string définit une catégorie.
  240. Configuration::setting('host',array(
  241. "Général",
  242. 'host_url_notification' => array("label"=>"Notifier en cas de down","type"=>"user","attributes"=>array('data-types'=>'"user,rank"','data-multiple'=>'"true"'))
  243. ));
  244. Right::register('host',array('label'=>"Gestion des droits sur le module hebergement"));
  245. //Droits ciblés sur les fiches client
  246. Right::register('host_sheet',array(
  247. 'label'=>'Gestion des droits sur une fiche hebergement particuliere',
  248. 'global'=> false,
  249. 'check' => function($action,$right){
  250. global $myUser;
  251. require_once(__DIR__.SLASH.'Machine.class.php');
  252. if($right->uid <= 0) throw new Exception('Id non spécifié');
  253. $machine = Machine::getById($right->uid);
  254. if(!$machine) throw new Exception('Machine inexistante');
  255. if(!$myUser->can('host','edit')) throw new Exception('Seul le un editeur d\'hébergement peut définir des droits pour cette fiche');
  256. if($machine->creator != $myUser->login) throw new Exception('Seul le créateur de la fiche peut ajouter des droits dessus');
  257. }
  258. ));
  259. function host_notification_type(&$types){
  260. $types['host_url_error'] = array(
  261. 'category' =>'Développement',
  262. 'label' =>'Machine en erreur',
  263. 'color' =>'#82c91e',
  264. 'icon' =>'fas fa-server',
  265. 'description' => "Lorsqu'un nom de domaine de machine ne répond plus",
  266. 'default_methods' => array(
  267. 'interface' => true,
  268. 'mail' => false
  269. )
  270. );
  271. }
  272. function host_history_type(&$types){
  273. require_once(__DIR__.SLASH.'MachineApplication.class.php');
  274. $types[MachineApplication::TYPE_DOWN] = array(
  275. 'slug'=>MachineApplication::TYPE_DOWN,
  276. 'label' => 'Application en erreur',
  277. 'icon' => 'fas fa-thumbs-down',
  278. 'color' => '#da6234');
  279. $types[MachineApplication::TYPE_UP] = array(
  280. 'slug'=>MachineApplication::TYPE_UP,
  281. 'label' => 'Application rétablie',
  282. 'icon' => 'far fa-thumbs-up',
  283. 'color' => '#28a745');
  284. }
  285. //Déclation des assets
  286. Plugin::addCss("/css/main.css");
  287. Plugin::addJs("/js/main.js");
  288. //Mapping hook / fonctions
  289. Plugin::addHook("install", "host_install");
  290. Plugin::addHook("uninstall", "host_uninstall");
  291. Plugin::addHook("menu_main", "host_menu");
  292. Plugin::addHook("page", "host_page");
  293. Plugin::addHook("menu_setting", "host_menu_setting");
  294. Plugin::addHook("content_setting", "host_content_setting");
  295. Plugin::addHook("cron", "host_cron");
  296. Plugin::addHook("notification_type", "host_notification_type");
  297. Plugin::addHook("history_type", "host_history_type");
  298. ?>