BaseJob.php 13 KB

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