BaseJob.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <?php
  2. namespace ModulesGarden\ProxmoxAddon\App\Jobs;
  3. use Illuminate\Database\Capsule\Manager as DB;
  4. use MGProvision\Proxmox\v2\models\Node;
  5. use MGProvision\Proxmox\v2\VmFactory;
  6. use ModulesGarden\ProxmoxAddon\App\Models\RangeVm;
  7. use ModulesGarden\ProxmoxAddon\App\Models\TaskHistory;
  8. use ModulesGarden\ProxmoxAddon\App\Models\VmModel;
  9. use ModulesGarden\ProxmoxAddon\App\Models\Whmcs\ActivityLog;
  10. use ModulesGarden\ProxmoxAddon\App\Models\Whmcs\ToDoList;
  11. use ModulesGarden\ProxmoxAddon\App\Repositories\RangeVmRepository;
  12. use ModulesGarden\ProxmoxAddon\App\Services\ApiService;
  13. use ModulesGarden\ProxmoxAddon\App\Services\EmailService;
  14. use ModulesGarden\ProxmoxAddon\App\Services\Vps\AdditionalDiskService;
  15. use ModulesGarden\ProxmoxAddon\App\Services\Vps\AdditionalMountPointService;
  16. use ModulesGarden\ProxmoxAddon\App\Services\Vps\AgentService;
  17. use ModulesGarden\ProxmoxAddon\App\Services\Vps\ContainerService;
  18. use ModulesGarden\ProxmoxAddon\App\Services\Vps\HostingService;
  19. use ModulesGarden\ProxmoxAddon\Core\Models\Whmcs\Configuration;
  20. use ModulesGarden\ProxmoxAddon\Core\ModuleConstants;
  21. use ModulesGarden\ProxmoxAddon\Core\Queue\Job;
  22. use ModulesGarden\ProxmoxAddon\Core\UI\Traits\WhmcsParams;
  23. use ModulesGarden\ProxmoxAddon\Core\Traits\Smarty;
  24. use function ModulesGarden\ProxmoxAddon\Core\Helper\sl;
  25. abstract class BaseJob extends Job
  26. {
  27. use ApiService;
  28. use WhmcsParams;
  29. use HostingService;
  30. use Smarty;
  31. /**
  32. * @var VmModel
  33. */
  34. protected $vmModel;
  35. protected $params = [];
  36. /**
  37. * @var EmailService
  38. */
  39. protected $emailService;
  40. /**
  41. * @var ContainerService
  42. */
  43. protected $containerService;
  44. /**
  45. * @var AgentService
  46. */
  47. protected $agentService;
  48. /**
  49. * @var AdditionalDiskService
  50. */
  51. protected $additionalDiskService;
  52. /**
  53. * @var AdditionalMountPointService
  54. */
  55. protected $additionalMountPointService;
  56. protected function initServices()
  57. {
  58. }
  59. /**
  60. * @return VmModel
  61. */
  62. public function getVmModel(){
  63. return $this->vmModel = VmModel::ofHostingId($this->model->rel_id)->where('id', $this->model->custom_id)->firstOrFail();
  64. }
  65. public function initVm(){
  66. $vm = (new VmFactory())->fromVmModel($this->getVmModel());
  67. sl('Vm')->setVm($vm);
  68. sl('Vm')->setVmModel($this->getVmModel());
  69. logModuleCall(
  70. 'proxmoxCloud',
  71. __FUNCTION__,
  72. $vm,
  73. 'Debug',
  74. $vmModel
  75. );
  76. return $this;
  77. }
  78. public function initParams()
  79. {
  80. if (!$this->model->rel_id)
  81. {
  82. new \InvalidArgumentException(sprintf("Job model: #%s rel_id cannot be empty", $this->model->id));
  83. }
  84. if (!function_exists('ModuleBuildParams'))
  85. {
  86. require_once ModuleConstants::getFullPathWhmcs('includes') . DIRECTORY_SEPARATOR . "modulefunctions.php";
  87. }
  88. $this->params = \ModuleBuildParams($this->model->rel_id);
  89. sl("whmcsParams")->setParams($this->params);
  90. if(function_exists('\ModulesGarden\Servers\ProxmoxCloudVps\Core\Helper\sl')){
  91. \ModulesGarden\Servers\ProxmoxCloudVps\Core\Helper\sl("whmcsParams")->setParams($this->params);
  92. }
  93. $this->setHostingId($this->params['serviceid']);
  94. return $this;
  95. }
  96. protected function sleep($seconds = 60)
  97. {
  98. $this->model->setWaiting();
  99. $this->model->setRetryAfter(date("Y-m-d H:i:s", strtotime("+{$seconds} seconds")));
  100. $this->model->increaseRetryCount();
  101. }
  102. protected function vmidExistInWhmcs($vmid)
  103. {
  104. //vps
  105. $cfv = 'tblcustomfieldsvalues';
  106. $cfn = 'tblcustomfields';
  107. $h = 'tblhosting';
  108. $query = DB::table($cfv)
  109. ->rightJoin($cfn, "{$cfn}.id", "=", "{$cfv}.fieldid")
  110. ->leftJoin($h, "{$h}.id", "=", "{$cfv}.relid")
  111. ->where("{$cfn}.fieldname", "like", "vmid%")
  112. ->where("{$cfv}.value", $vmid)
  113. ->whereIn("{$h}.domainstatus", ['Active', "Suspended"]);
  114. if ($query->count())
  115. {
  116. return true;
  117. }
  118. //cloud
  119. try
  120. {
  121. $query = VmModel::where("vmid", $vmid);
  122. return $query->count() > 0;
  123. }
  124. catch (\Exception $ex)
  125. { // table does not exist
  126. }
  127. return false;
  128. }
  129. protected function findFreeVmid($vmid)
  130. {
  131. for ($i = $vmid; $i <= 1000000; $i++)
  132. {
  133. if ($this->vmidExistInWhmcs($i))
  134. {
  135. continue;
  136. }
  137. try
  138. {
  139. $res = $this->api()->get("/cluster/nextid", ['vmid' => $i]);
  140. }
  141. catch (\Exception $ex)
  142. {
  143. continue;
  144. }
  145. if ($res == $i)
  146. {
  147. return $i;
  148. }
  149. }
  150. throw new \Exception("Unable to obtain vmid");
  151. }
  152. protected function isVmRange()
  153. {
  154. if (RangeVm::ofServerId($this->getWhmcsParamByKey("serverid"))->count())
  155. {
  156. return true;
  157. }
  158. return Configuration::where("setting", "proxmoxVPSMinimumVMID")->count() > 0;
  159. }
  160. protected function nextVmid()
  161. {
  162. $data = $this->api()->get("/cluster/nextid", []);
  163. $vmid = (int)$data ? (int)$data : 100;
  164. $vmid = $this->findFreeVmid($vmid);
  165. if ($this->isVmRange())
  166. {
  167. $rageVm = new RangeVmRepository($this->getWhmcsParamByKey('serverid'));
  168. if (!$rageVm->has() && !$rageVm->getMin())
  169. {
  170. return $vmid;
  171. }
  172. else
  173. {
  174. if (!$rageVm->getMax() && $rageVm->getMin())
  175. {
  176. $from = (int)$rageVm->getMin();
  177. $to = (int)$rageVm->getMin() * 100;
  178. }
  179. else
  180. {
  181. $from = (int)$rageVm->getMin();
  182. $to = (int)$rageVm->getMax();
  183. }
  184. }
  185. for ($i = $from; $i <= $to; $i++)
  186. {
  187. try
  188. {
  189. if ($this->vmidExistInWhmcs($i))
  190. {
  191. continue;
  192. }
  193. $res = $this->api()->get("/cluster/nextid", ['vmid' => $i]);
  194. if ((int)$res == $i)
  195. {
  196. $vmID = $i;
  197. break;
  198. }
  199. }
  200. catch (\Exception $ex)
  201. {
  202. continue;
  203. }
  204. }
  205. if (!$vmID)
  206. {
  207. throw new \Exception("VM Ranges have been exited for this server. Please setup VM Ranges", 321);
  208. }
  209. }
  210. return $vmID ? $vmID : $vmid;
  211. }
  212. protected function createTaskHistory($taskId, $action)
  213. {
  214. $type = str_replace(["qemu", "lxc"], ["VM", "CT"], $this->getVmModel()->virtualization);
  215. $task = new TaskHistory();
  216. $task->fill([
  217. 'hosting_id' => $this->getWhmcsParamByKey("serviceid"),
  218. 'upid' => $taskId,
  219. 'name' => sprintf("%s %s - %s", $type, $this->getVmModel()->vmid, $action),
  220. 'vmid' => $this->getVmModel()->vmid,
  221. 'node' => $this->getVmModel()->node,
  222. 'status' => 0
  223. ])->save();
  224. }
  225. protected function getModelData()
  226. {
  227. return unserialize($this->model->data);
  228. }
  229. protected function putModelDataAndSave(array $newData)
  230. {
  231. $data = $this->getModelData();
  232. $data += $newData;
  233. $this->setModelDataAndSave($data);
  234. return $this;
  235. }
  236. protected function setModelDataAndSave(array $data)
  237. {
  238. $this->model->data = serialize($data);
  239. $this->model->save();
  240. return $this;
  241. }
  242. /**
  243. * @return \MGProvision\Proxmox\v2\models\Task
  244. * @throws \MGProvision\Proxmox\v2\ProxmoxApiException
  245. */
  246. protected function getTask()
  247. {
  248. $taskId = $this->getModelData()['taskId'];
  249. //Init API service
  250. if($this->getModelData()['templateNode']){
  251. $node = new Node($this->getModelData()['templateNode']);
  252. }else{
  253. $node = new Node($this->getModelData()['node']);
  254. }
  255. $node->setApi($this->api());
  256. return $node->task($taskId);
  257. }
  258. protected function isTask()
  259. {
  260. return !is_null($this->getModelData()['taskId']);
  261. }
  262. protected function isDone()
  263. {
  264. $taskId = $this->getModelData()['taskId'];
  265. if (!$taskId || !$this->getModelData()['node'])
  266. {
  267. return false;
  268. }
  269. $task = $this->getTask();
  270. if ($task->getStatus() == "running")
  271. {
  272. $this->log->success(sprintf("Waiting to finish. Task Id %s Node: %s ", $task->getUpid(), $task->getNode()));
  273. return false;
  274. }
  275. //Failed
  276. if ($task->getExitstatus() && $task->getExitstatus() != "OK")
  277. {
  278. $this->log->error($task->getExitstatus());
  279. $this->failed($task->getExitstatus());
  280. $data = $this->getModelData();
  281. unset($data['taskId'], $data['node']);
  282. $this->setModelDataAndSave($data);
  283. return false;
  284. }
  285. return true;
  286. }
  287. protected function isTaskRunning()
  288. {
  289. return $this->isTask() && $this->getTask()->isRunning();
  290. }
  291. protected function isFailed()
  292. {
  293. $task = $this->getTask();
  294. return $task->getExitstatus() && $task->getExitstatus() != "OK";
  295. }
  296. protected function failed($error)
  297. {
  298. if ((int)$this->model->retry_count != 21)
  299. {
  300. return;
  301. }
  302. if (!preg_match("/Create/", $this->model->job) || preg_match("/Clone/", $this->model->job))
  303. {
  304. return;
  305. }
  306. //create new entery on to do list
  307. if ($this->configuration()->isToDoList())
  308. {
  309. $title = sprintf('Creation Failed - Service ID: %s', $this->getWhmcsParamByKey('serviceid'));
  310. if (ToDoList::ofTitle($title)->pending()->count())
  311. {
  312. return;
  313. }
  314. $entity = new ToDoList();
  315. $entity->fill(
  316. [
  317. 'date' => date("Y-m-d H:i:s"),
  318. "duedate" => date("Y-m-d H:i:s"),
  319. 'title' => $title,
  320. "status" => "Pending",
  321. "description" => $error,
  322. "admin" => 0,
  323. ]
  324. );
  325. $entity->save();
  326. }
  327. //send admin message
  328. if ($this->configuration()->getServiceCreationFailedTemplateId())
  329. {
  330. //init email template
  331. $this->emailService->template($this->configuration()->getServiceCreationFailedTemplateId());
  332. //check if already send
  333. $description = printf('Email Sent to Admin (%s) - Service ID: %s', $this->emailService->getVars()['messagename'], $this->getWhmcsParamByKey('serviceid'));
  334. if (ActivityLog::ofDescription($description)->today()->count() > 0)
  335. {
  336. return;
  337. }
  338. //email vars
  339. global $customadminpath;
  340. $adminDir = $customadminpath ? $customadminpath : "admin";
  341. $adminAreaLink = $GLOBALS['CONFIG']['SystemURL'] . "/{$adminDir}";
  342. $hosting = $GLOBALS['CONFIG']['SystemURL'] . "/{$adminDir}/clientsservices.php?id=" . $this->getWhmcsParamByKey('serviceid');
  343. $emailVars = [
  344. "client_id" => $this->getWhmcsParamByKey('clientsdetails')['id'],
  345. "service_id" => $this->getWhmcsParamByKey('serviceid'),
  346. "service_product" => "<a href='{$hosting}/'>{$hosting}</a>",
  347. "service_domain" => $this->getWhmcsParamByKey('domain'),
  348. "error_msg" => $error,
  349. "whmcs_admin_link" => "<a href='{$adminAreaLink}/'>{$adminAreaLink}</a>",
  350. ];
  351. $this->emailService->vars($emailVars)->sendToAdmin();
  352. logActivity($description);
  353. }
  354. }
  355. /**
  356. * @return \ModulesGarden\ProxmoxAddon\App\Models\Job:
  357. */
  358. protected function getParentModel()
  359. {
  360. if (is_null($this->model->parent_id))
  361. {
  362. throw new \InvalidArgumentException("The Parent Id is not valid");
  363. }
  364. if ($this->parent)
  365. {
  366. return $this->parent;
  367. }
  368. return $this->parent = \ModulesGarden\ProxmoxAddon\App\Models\Job::ofId($this->model->parent_id)->firstOrFail();
  369. }
  370. protected function getParentModelData()
  371. {
  372. return unserialize($this->getParentModel()->data);
  373. }
  374. }