/* +------------------------------------------------------------------------+ | PHP LXD Extension | +------------------------------------------------------------------------+ | Copyright (c)2017-2017 LXC.systems (https://github.com/lxc-systems/lxd)| +------------------------------------------------------------------------+ | This source file is subject to GNU General Public License v2.0 License | | that is bundled with this package in the file LICENSE. | | | | If you did not receive a copy of the license and are unable to | | obtain it through the world-wide-web, please send an email | | to license@lxd.systems so we can send you a copy immediately. | +------------------------------------------------------------------------+ | Authors: Lawrence Cherone| +------------------------------------------------------------------------+ */ namespace Lxd\Endpoints; use Lxd\Endpoint; final class Containers extends Endpoint { /** * @var - Base API endpoint */ const ENDPOINT = "containers"; /** * @var */ protected curl; /** * Class construct * * @param array config Config array which holds object configuration * @param curl * @return void */ public function __construct(array! config, curl) -> void { parent::__construct(config, curl, __CLASS__); } /** * List all containers on the server. * * * $lxd->containers->all(); *
* * @return array */ public function all() -> array { var ret = [], item; var response = this->curl->get(this->getBase()); if response["type"] === "error" { return response; } for item in (array) response["metadata"] { let ret[] = this->stripEndpoint(item); } return ret; } /** * List all containers on the server. * ** $lxd->containers->info('container-name'); *
* * @param string name Name of container * @return array */ public function info(string! name) -> array { return this->curl->get(this->getBase(Containers::ENDPOINT)."/".name); } /** * Get the current state of the container. * ** $lxd->containers->state('container-name'); *
* * @param string name Name of container * @return array */ public function state(string! name) -> array { return this->curl->get(this->getBase(Containers::ENDPOINT)."/".name."/state"); } /** * Main set state method for a container, methods below are shortcuts. * ** $lxd->containers->setState('container-name', 'start', 30, true, true, true); *
* * @param string name Name of container * @param string action State change action (stop, start, restart, freeze or unfreeze) * @param int timeout Time after which the operation is considered to have failed (default: no timeout) * @param boolean force Whether to force the operation by killing the container * @param boolean stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param boolean wait Wait for operation to finish * @return array */ public function setState( string! name, string! action, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { var response, options = [ "action" : action, "timeout" : timeout, "force" : force, "stateful" : stateful ]; let response = this->curl->put( this->getBase(Containers::ENDPOINT)."/".name."/state", options ); if response["type"] !== "error" && wait { let response = this->curl->get( this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait", [ "timeout" : timeout ] ); } return response; } /** * Start a container. * ** $lxd->containers->start('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function start( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->setState(name, "start", timeout, force, stateful, wait); } /** * Stop a container. * ** $lxd->containers->stop('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function stop( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->setState(name, "stop", timeout, force, stateful, wait); } /** * Shutdown a container - alias of stop. * ** $lxd->containers->shutdown('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function shutdown( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->stop(name, timeout, force, stateful, wait); } /** * Restart a container. * ** $lxd->containers->restart('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function restart( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->setState(name, "restart", timeout, force, stateful, wait); } /** * Reboot a container - alias of restart. * ** $lxd->containers->reboot('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function reboot( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->restart(name, timeout, force, stateful, wait); } /** * Freeze a container. * ** $lxd->containers->freeze('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function freeze( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->setState(name, "freeze", timeout, force, stateful, wait); } /** * Pause a container - alias of freeze. * ** $lxd->containers->pause('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function pause( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->freeze(name, timeout, force, stateful, wait); } /** * Unfreeze a container. * ** $lxd->containers->unfreeze('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function unfreeze( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->setState(name, "unfreeze", timeout, force, stateful, wait); } /** * Thaw a container - alias of unfreeze. * ** $lxd->containers->thaw('container-name', 30, true, true, true); *
* * @param string name Name of container * @param int timeout Time after which the operation is considered to have failed (default: 30 seconds) * @param bool force Whether to force the operation by killing the container * @param bool stateful Whether to store/restore runtime state (only valid for stop and start, default: false) * @param bool wait Wait for operation to finish * @return array */ public function thaw( string! name, int! timeout = 30, boolean! force = false, boolean! stateful = false, boolean! wait = false) -> array { return this->unfreeze(name, timeout, force, stateful, wait); } /** * Create a container from an image (local or remote). * * // Create container from image specified by alias * $lxd->containers->create( * "test", * [ * "alias" => "ubuntu/xenial/amd64", * ] * ); * * // Create container from image specified by fingerprint * $lxd->containers->create( * "test", * [ * "fingerprint" => "097e75d6f7419d3a5e204d8125582f2d7bdd4ee4c35bd324513321c645f0c415", * ] * ); * * // Create container based on most recent match of image properties * $lxd->containers->create( * "test", * [ * "properties" => [ * "os" => "ubuntu", * "release" => "14.04", * "architecture" => "x86_64", * ], * ] * ); * * // Create an empty container * $lxd->containers->create( * "test", * [ * "empty" => true, * ] * ); * * // Create container with custom configuration. * // - Set the MAC address of the container's eth0 device * $lxd->containers->create( * "test", * [ * "alias" => "ubuntu/xenial/amd64", * "config" => [ * "volatile.eth0.hwaddr" => "aa:bb:cc:dd:ee:ff", * ], * ] * ); * * // Create container and apply profiles to it * $lxd->containers->create( * "test", * [ * "alias" => "ubuntu/xenial/amd64", * "profiles" => ["migratable", "unconfined"], * ] * ); * * // Create container from a publicly-accessible remote image * $lxd->containers->create( * "test", * [ * "server" => "https://images.linuxcontainers.org:8443", * "alias" => "ubuntu/xenial/amd64", * ] * ); * * // Create container from a private remote image (authenticated by a secret) * $lxd->containers->create( * "test", * [ * "server" => "https://private.example.com:8443", * "alias" => "ubuntu/xenial/amd64", * "secret" => "my_secrect", * ] * ); * * @param string name The name of the container * @param array options Options to create the container * @param boolean wait Wait for operation to finish * @return array */ public function create(string! name, array! options, boolean! wait = false) -> array { var source, opts, response; let source = this->getSource(options); if (isset options["empty"] && empty options["empty"]) && empty source { throw "Source empty"; } if isset options["empty"] && empty options["empty"] { let opts = this->getOptions(name, options); let opts["source"] = source; } elseif isset options["empty"] && options["empty"] { let opts = this->getEmptyOptions(name, options); } elseif isset options["server"] && !empty options["server"] { let opts = this->getRemoteImageOptions(name, source, options); } else { let opts = this->getLocalImageOptions(name, source, options); } let response = this->curl->post(this->getBase(Containers::ENDPOINT), opts); if response["type"] !== "error" && wait { let response = this->curl->get( this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait", [ "timeout" : this->config["timeout"] ] ); } return response; } /** * Create a copy of an existing local container. * * // Copy container * $lxd->containers->copy('existing', 'new'); * * // Copy container and apply profiles to it * $lxd->containers->copy( * 'container-name', * 'copy-container-name', * 'profiles' => ['default', 'public'] * ); * * @param string name Name of existing container * @param string copyName Name of copied container * @param array options Options for copied container * @param bool wait Wait for operation to finish * @return array */ public function copy(string! name, string! copyName, array! options = [], boolean! wait = false) -> array { var opts, response; let opts = this->getOptions(copyName, options); let opts["source"]["type"] = "copy"; let opts["source"]["source"] = name; let response = this->curl->post(this->getBase(Containers::ENDPOINT), opts); if response["type"] !== "error" && wait { let response = this->curl->get( this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait", [ "timeout" : this->config["timeout"] ] ); } return response; } /** * Replace the configuration of a container * * Configuration is overwritten, not merged. Clients should * first call the info method to obtain the current configuration of a * container. The resulting objects metadata should be modified and then passed to * the relace method. * * // Change container to be ephemeral * $container = $lxd->containers->info('container-name'); * $container['metadata']['ephemeral'] = true; * $lxd->containers->replace('container-name', $$container['metadata']); * * @param string name Name of container * @param object container Container to update * @param boolean wait Wait for operation to finish * @return array */ public function replace(string! name, array! opts, boolean! wait = false) -> array { var response; let response = this->curl->put(this->getBase(Containers::ENDPOINT)."/".name, opts); if response["type"] !== "error" && wait { let response = this->curl->get( this->getBase(\Lxd\Endpoints\Operations::ENDPOINT)."/".response["metadata"]["id"]."/wait", [ "timeout" : this->config["timeout"] ] ); } return response; } /** * Private method for getting container [source] options. * * @param array options Options for payload */ final private function getSource(array! options) -> array { var only, opts, attr; if isset options["source"] { let only = [ "type", "mode", "source", "server", "operation", "protocol", "base-image", "certificate", "secret", "secrets", "alias", "fingerprint", "properties", "live" ]; let opts = array_intersect_key(options, array_flip(only)); return opts["source"]; } for attr in ["alias", "fingerprint", "properties"] { if isset options[attr] && !empty options[attr] { return [attr : options[attr]]; } } return []; } /** * Private method for getting container [default] options. * * @param string name Name for options * @param array options Options for payload * @return array */ final private function getOptions(string! name, array! options) -> array { var opts, only = [ "architecture", "profiles", "ephemeral", "config", "devices" ]; let opts = array_intersect_key(options, array_flip(only)); let opts["name"] = name; return opts; } /** * Private method for getting default container [empty] options. * * @param string name Name for options * @param array options Options for payload * @return array */ final private function getEmptyOptions(string! name, array! options) -> array { var attr, opts, attrs = [ "alias", "fingerprint", "properties", "server", "secret", "protocol", "certificate" ]; for attr in attrs { if isset options[attr] && !empty options[attr] { throw "empty => true is not compatible with ".attr; } } let opts = this->getOptions(name, options); let opts["source"]["type"] = "none"; return opts; } /** * Private method for getting default container [remote] options. * * @param string name Name for options * @param array source Source for payload * @param array options Options for payload * @return array */ final private function getRemoteImageOptions(string! name, array! source, array! options) -> array { var opts, remoteOptions, only = [ "server", "secret", "protocol", "certificate" ]; if isset options["protocol"] && !in_array(options["protocol"], ["lxd", "simplestreams"]) { throw "Invalid protocol. Valid choices: lxd, simplestreams"; } let remoteOptions = array_intersect_key(options, only->flip()); let opts = this->getOptions(name, options); let opts["source"] = source->merge(remoteOptions); let opts["source"]["type"] = "image"; let opts["source"]["mode"] = "pull"; return opts; } /** * Private method for getting default container [local] options. * * @param string name Name for options * @param array source Source for payload * @param array options Options for payload * @return array */ final private function getLocalImageOptions(string! name, array! source, array! options) -> array { var opts, attr, attrs = [ "secret", "protocol", "certificate" ]; for attr in attrs { if isset options[attr] && !empty options[attr] { throw "Only setting remote server is compatible with ".attr; } } let opts = this->getOptions(name, options); let opts["source"] = source; let opts["source"]["type"] = "image"; return opts; } }