BaseJob.php 12 KB

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