def deploy_node(self, **kwargs): """ Create a new node, and start deployment. In order to be able to SSH into a created node access credentials are required. A user can pass either a :class:`NodeAuthPassword` or :class:`NodeAuthSSHKey` to the ``auth`` argument. If the ``create_node`` implementation supports that kind if credential (as declared in ``self.features['create_node']``) then it is passed on to ``create_node``. Otherwise it is not passed on to ``create_node`` and it is only used for authentication. If the ``auth`` parameter is not supplied but the driver declares it supports ``generates_password`` then the password returned by ``create_node`` will be used to SSH into the server. Finally, if the ``ssh_key_file`` is supplied that key will be used to SSH into the server. This function may raise a :class:`DeploymentException`, if a create_node call was successful, but there is a later error (like SSH failing or timing out). This exception includes a Node object which you may want to destroy if incomplete deployments are not desirable. >>> from libcloud.compute.drivers.dummy import DummyNodeDriver >>> from libcloud.compute.deployment import ScriptDeployment >>> from libcloud.compute.deployment import MultiStepDeployment >>> from libcloud.compute.base import NodeAuthSSHKey >>> driver = DummyNodeDriver(0) >>> key = NodeAuthSSHKey('...') # read from file >>> script = ScriptDeployment("yum -y install emacs strace tcpdump") >>> msd = MultiStepDeployment([key, script]) >>> def d(): ... try: ... node = driver.deploy_node(deploy=msd) ... except NotImplementedError: ... print ("not implemented for dummy driver") >>> d() not implemented for dummy driver Deploy node is typically not overridden in subclasses. The existing implementation should be able to handle most such. :param deploy: Deployment to run once machine is online and availble to SSH. :type deploy: :class:`Deployment` :param ssh_username: Optional name of the account which is used when connecting to SSH server (default is root) :type ssh_username: ``str`` :param ssh_alternate_usernames: Optional list of ssh usernames to try to connect with if using the default one fails :type ssh_alternate_usernames: ``list`` :param ssh_port: Optional SSH server port (default is 22) :type ssh_port: ``int`` :param ssh_timeout: Optional SSH connection timeout in seconds (default is 10) :type ssh_timeout: ``float`` :param auth: Initial authentication information for the node (optional) :type auth: :class:`NodeAuthSSHKey` or :class:`NodeAuthPassword` :param ssh_key: A path (or paths) to an SSH private key with which to attempt to authenticate. (optional) :type ssh_key: ``str`` or ``list`` of ``str`` :param timeout: How many seconds to wait before timing out. (default is 600) :type timeout: ``int`` :param max_tries: How many times to retry if a deployment fails before giving up (default is 3) :type max_tries: ``int`` :param ssh_interface: The interface to wait for. Default is 'public_ips', other option is 'private_ips'. :type ssh_interface: ``str`` """ if not libcloud.compute.ssh.have_paramiko: raise RuntimeError('paramiko is not installed. You can install ' + 'it using pip: pip install paramiko') if 'auth' in kwargs: auth = kwargs['auth'] if not isinstance(auth, (NodeAuthSSHKey, NodeAuthPassword)): raise NotImplementedError( 'If providing auth, only NodeAuthSSHKey or' 'NodeAuthPassword is supported') elif 'ssh_key' in kwargs: # If an ssh_key is provided we can try deploy_node pass elif 'create_node' in self.features: f = self.features['create_node'] if not 'generates_password' in f and not "password" in f: raise NotImplementedError( 'deploy_node not implemented for this driver') else: raise NotImplementedError( 'deploy_node not implemented for this driver') node = self.create_node(**kwargs) max_tries = kwargs.get('max_tries', 3) password = None if 'auth' in kwargs: if isinstance(kwargs['auth'], NodeAuthPassword): password = kwargs['auth'].password elif 'password' in node.extra: password = node.extra['password'] ssh_interface = kwargs.get('ssh_interface', 'public_ips') # Wait until node is up and running and has IP assigned try: node, ip_addresses = self.wait_until_running( nodes=[node], wait_period=3, timeout=kwargs.get('timeout', NODE_ONLINE_WAIT_TIMEOUT), ssh_interface=ssh_interface)[0] except Exception: e = sys.exc_info()[1] raise DeploymentError(node=node, original_exception=e, driver=self) ssh_username = kwargs.get('ssh_username', 'root') ssh_alternate_usernames = kwargs.get('ssh_alternate_usernames', []) ssh_port = kwargs.get('ssh_port', 22) ssh_timeout = kwargs.get('ssh_timeout', 10) ssh_key_file = kwargs.get('ssh_key', None) timeout = kwargs.get('timeout', SSH_CONNECT_TIMEOUT) deploy_error = None for username in ([ssh_username] + ssh_alternate_usernames): try: self._connect_and_run_deployment_script( task=kwargs['deploy'], node=node, ssh_hostname=ip_addresses[0], ssh_port=ssh_port, ssh_username=username, ssh_password=password, ssh_key_file=ssh_key_file, ssh_timeout=ssh_timeout, timeout=timeout, max_tries=max_tries) except Exception: # Try alternate username # Todo: Need to fix paramiko so we can catch a more specific # exception e = sys.exc_info()[1] deploy_error = e else: # Script sucesfully executed, don't try alternate username deploy_error = None break if deploy_error is not None: raise DeploymentError(node=node, original_exception=deploy_error, driver=self) return node
def deploy_node(self, **kwargs): """ Create a new node, and start deployment. Depends on a Provider Driver supporting either using a specific password or returning a generated password. This function may raise a L{DeploymentException}, if a create_node call was successful, but there is a later error (like SSH failing or timing out). This exception includes a Node object which you may want to destroy if incomplete deployments are not desirable. >>> from libcloud.compute.drivers.dummy import DummyNodeDriver >>> from libcloud.compute.deployment import ScriptDeployment >>> from libcloud.compute.deployment import MultiStepDeployment >>> from libcloud.compute.base import NodeAuthSSHKey >>> driver = DummyNodeDriver(0) >>> key = NodeAuthSSHKey('...') # read from file >>> script = ScriptDeployment("yum -y install emacs strace tcpdump") >>> msd = MultiStepDeployment([key, script]) >>> def d(): ... try: ... node = driver.deploy_node(deploy=msd) ... except NotImplementedError: ... print ("not implemented for dummy driver") >>> d() not implemented for dummy driver Deploy node is typically not overridden in subclasses. The existing implementation should be able to handle most such. @inherits: L{NodeDriver.create_node} @keyword deploy: Deployment to run once machine is online and availble to SSH. @type deploy: L{Deployment} @keyword ssh_username: Optional name of the account which is used when connecting to SSH server (default is root) @type ssh_username: C{str} @keyword ssh_alternate_usernames: Optional list of ssh usernames to try to connect with if using the default one fails @type ssh_alternate_usernames: C{list} @keyword ssh_port: Optional SSH server port (default is 22) @type ssh_port: C{int} @keyword ssh_timeout: Optional SSH connection timeout in seconds (default is None) @type ssh_timeout: C{float} @keyword auth: Initial authentication information for the node (optional) @type auth: L{NodeAuthSSHKey} or L{NodeAuthPassword} @keyword ssh_key: A path (or paths) to an SSH private key with which to attempt to authenticate. (optional) @type ssh_key: C{str} or C{list} of C{str} @keyword timeout: How many seconds to wait before timing out. (default is 600) @type timeout: C{int} @keyword max_tries: How many times to retry if a deployment fails before giving up (default is 3) @type max_tries: C{int} @keyword ssh_interface: The interface to wait for. Default is 'public_ips', other option is 'private_ips'. @type ssh_interface: C{str} """ if not libcloud.compute.ssh.have_paramiko: raise RuntimeError('paramiko is not installed. You can install ' + 'it using pip: pip install paramiko') password = None if 'create_node' not in self.features: raise NotImplementedError( 'deploy_node not implemented for this driver') elif 'generates_password' not in self.features["create_node"]: if 'password' not in self.features["create_node"] and \ 'ssh_key' not in self.features["create_node"]: raise NotImplementedError( 'deploy_node not implemented for this driver') if 'auth' not in kwargs: value = os.urandom(16) kwargs['auth'] = NodeAuthPassword(binascii.hexlify(value)) if 'ssh_key' not in kwargs: password = kwargs['auth'].password node = self.create_node(**kwargs) max_tries = kwargs.get('max_tries', 3) if 'generates_password' in self.features['create_node']: password = node.extra.get('password') ssh_interface = kwargs.get('ssh_interface', 'public_ips') # Wait until node is up and running and has IP assigned try: node, ip_addresses = self.wait_until_running( nodes=[node], wait_period=3, timeout=NODE_ONLINE_WAIT_TIMEOUT, ssh_interface=ssh_interface)[0] except Exception: e = sys.exc_info()[1] raise DeploymentError(node=node, original_exception=e, driver=self) if password: node.extra['password'] = password ssh_username = kwargs.get('ssh_username', 'root') ssh_alternate_usernames = kwargs.get('ssh_alternate_usernames', []) ssh_port = kwargs.get('ssh_port', 22) ssh_timeout = kwargs.get('ssh_timeout', 10) ssh_key_file = kwargs.get('ssh_key', None) timeout = kwargs.get('timeout', SSH_CONNECT_TIMEOUT) deploy_error = None for username in ([ssh_username] + ssh_alternate_usernames): try: self._connect_and_run_deployment_script( task=kwargs['deploy'], node=node, ssh_hostname=ip_addresses[0], ssh_port=ssh_port, ssh_username=username, ssh_password=password, ssh_key_file=ssh_key_file, ssh_timeout=ssh_timeout, timeout=timeout, max_tries=max_tries) except Exception: # Try alternate username # Todo: Need to fix paramiko so we can catch a more specific # exception e = sys.exc_info()[1] deploy_error = e else: # Script sucesfully executed, don't try alternate username deploy_error = None break if deploy_error is not None: raise DeploymentError(node=node, original_exception=deploy_error, driver=self) return node
def deploy_node(self, **kwargs): """ Create a new node, and start deployment. Depends on a Provider Driver supporting either using a specific password or returning a generated password. This function may raise a L{DeploymentException}, if a create_node call was successful, but there is a later error (like SSH failing or timing out). This exception includes a Node object which you may want to destroy if incomplete deployments are not desirable. @keyword deploy: Deployment to run once machine is online and availble to SSH. @type deploy: L{Deployment} @keyword ssh_username: Optional name of the account which is used when connecting to SSH server (default is root) @type ssh_username: C{str} @keyword ssh_port: Optional SSH server port (default is 22) @type ssh_port: C{int} @keyword ssh_timeout: Optional SSH connection timeout in seconds (default is None) @type ssh_timeout: C{float} See L{NodeDriver.create_node} for more keyword args. >>> from libcloud.compute.drivers.dummy import DummyNodeDriver >>> from libcloud.deployment import ScriptDeployment, MultiStepDeployment >>> from libcloud.compute.base import NodeAuthSSHKey >>> driver = DummyNodeDriver(0) >>> key = NodeAuthSSHKey('...') # read from file >>> script = ScriptDeployment("yum -y install emacs strace tcpdump") >>> msd = MultiStepDeployment([key, script]) >>> def d(): ... try: ... node = driver.deploy_node(deploy=msd) ... except NotImplementedError: ... print "not implemented for dummy driver" >>> d() not implemented for dummy driver Deploy node is typically not overridden in subclasses. The existing implementation should be able to handle most such. """ # TODO: support ssh keys # FIX: this method is too long and complicated WAIT_PERIOD = 3 password = None if 'generates_password' not in self.features["create_node"]: if 'password' not in self.features["create_node"]: raise NotImplementedError, \ 'deploy_node not implemented for this driver' if not kwargs.has_key('auth'): kwargs['auth'] = NodeAuthPassword(os.urandom(16).encode('hex')) password = kwargs['auth'].password node = self.create_node(**kwargs) try: if 'generates_password' in self.features["create_node"]: password = node.extra.get('password') start = time.time() end = start + (60 * 15) # FIX: this should be soft-coded while time.time() < end: # need to wait until we get a public IP address. # TODO: there must be a better way of doing this time.sleep(WAIT_PERIOD) nodes = self.list_nodes() nodes = filter(lambda n: n.uuid == node.uuid, nodes) if len(nodes) == 0: raise DeploymentError(node, ("Booted node[%s] " % node + "is missing from list_nodes.")) if len(nodes) > 1: raise DeploymentError( node, ("Booted single node[%s], " % node + "but multiple nodes have same UUID")) node = nodes[0] if (node.public_ip is not None and node.public_ip != "" and node.state == NodeState.RUNNING): break ssh_username = kwargs.get('ssh_username', 'root') ssh_port = kwargs.get('ssh_port', 22) ssh_timeout = kwargs.get('ssh_timeout', 20) client = SSHClient(hostname=node.public_ip[0], port=ssh_port, username=ssh_username, password=password, timeout=ssh_timeout) while time.time() < end: try: client.connect() except (IOError, socket.gaierror, socket.error), e: # Retry if a connection is refused or timeout # occured client.close() time.sleep(WAIT_PERIOD) continue max_tries, tries = 3, 0 while tries < max_tries: try: n = kwargs["deploy"].run(node, client) client.close() raise except Exception, e: tries += 1 if tries >= max_tries: raise DeploymentError( node, 'Failed after %d tries' % (max_tries))
while tries < max_tries: try: n = kwargs["deploy"].run(node, client) client.close() raise except Exception, e: tries += 1 if tries >= max_tries: raise DeploymentError( node, 'Failed after %d tries' % (max_tries)) except DeploymentError: raise except Exception, e: raise DeploymentError(node, e) return n def _get_size_price(self, size_id): return get_size_price(driver_type='compute', driver_name=self.api_name, size_id=size_id) def is_private_subnet(ip): """ Utility function to check if an IP address is inside a private subnet. @type ip: C{str} @keyword ip: IP address to check
def deploy_node(self, **kwargs): """ Create a new node, and start deployment. Depends on a Provider Driver supporting either using a specific password or returning a generated password. This function may raise a L{DeploymentException}, if a create_node call was successful, but there is a later error (like SSH failing or timing out). This exception includes a Node object which you may want to destroy if incomplete deployments are not desirable. @keyword deploy: Deployment to run once machine is online and availble to SSH. @type deploy: L{Deployment} @keyword ssh_username: Optional name of the account which is used when connecting to SSH server (default is root) @type ssh_username: C{str} @keyword ssh_port: Optional SSH server port (default is 22) @type ssh_port: C{int} @keyword ssh_timeout: Optional SSH connection timeout in seconds (default is None) @type ssh_timeout: C{float} @keyword auth: Initial authentication information for the node (optional) @type auth: L{NodeAuthSSHKey} or L{NodeAuthPassword} See L{NodeDriver.create_node} for more keyword args. >>> from libcloud.compute.drivers.dummy import DummyNodeDriver >>> from libcloud.deployment import ScriptDeployment >>> from libcloud.deployment import MultiStepDeployment >>> from libcloud.compute.base import NodeAuthSSHKey >>> driver = DummyNodeDriver(0) >>> key = NodeAuthSSHKey('...') # read from file >>> script = ScriptDeployment("yum -y install emacs strace tcpdump") >>> msd = MultiStepDeployment([key, script]) >>> def d(): ... try: ... node = driver.deploy_node(deploy=msd) ... except NotImplementedError: ... print "not implemented for dummy driver" >>> d() not implemented for dummy driver Deploy node is typically not overridden in subclasses. The existing implementation should be able to handle most such. """ if not libcloud.compute.ssh.have_paramiko: raise RuntimeError('paramiko is not installed. You can install ' + 'it using pip: pip install paramiko') # TODO: support ssh keys password = None if 'create_node' not in self.features: raise NotImplementedError( 'deploy_node not implemented for this driver') elif 'generates_password' not in self.features["create_node"]: if 'password' not in self.features["create_node"]: raise NotImplementedError( 'deploy_node not implemented for this driver') if 'auth' not in kwargs: kwargs['auth'] = NodeAuthPassword(os.urandom(16).encode('hex')) password = kwargs['auth'].password node = self.create_node(**kwargs) if 'generates_password' in self.features['create_node']: password = node.extra.get('password') try: # Wait until node is up and running and has public IP assigned self._wait_until_running(node=node, wait_period=3, timeout=NODE_ONLINE_WAIT_TIMEOUT) ssh_username = kwargs.get('ssh_username', 'root') ssh_port = kwargs.get('ssh_port', 22) ssh_timeout = kwargs.get('ssh_timeout', 10) ssh_client = SSHClient(hostname=node.public_ip[0], port=ssh_port, username=ssh_username, password=password, timeout=ssh_timeout) # Connect to the SSH server running on the node ssh_client = self._ssh_client_connect(ssh_client=ssh_client, timeout=SSH_CONNECT_TIMEOUT) # Execute the deployment task self._run_deployment_script(task=kwargs['deploy'], node=node, ssh_client=ssh_client, max_tries=3) except Exception, e: raise DeploymentError(node, e)
def deploy_node(self, **kwargs): """Create a new node, and start deployment. This function may raise a :class:`DeploymentException`, if a create_node call was successful, but there is a later error (like SSH failing or timing out). This exception includes a Node object which you may want to destroy if incomplete deployments are not desirable. We use ``vagrant ssh-config <node>`` in order to get the SSH connection parameters for the deployment task. In order to do that, the node must be created first. The base implementation makes use of the SSH connection parameters *before* creating the node. Therefore we have to override it. :param deploy: Deployment to run once machine is online and available to SSH. :type deploy: :class:`Deployment` :param ssh_timeout: Optional SSH connection timeout in seconds (default is 10). :type ssh_timeout: ``float`` :param timeout: How many seconds to wait before timing out (default is 600). :type timeout: ``int`` :param max_tries: How many times to retry if a deployment fails before giving up (default is 3). :type max_tries: ``int`` :return: The node object for the new node. :rtype: :class:`VagrantNode` """ task = kwargs["deploy"] max_tries = kwargs.get("max_tries", 3) ssh_timeout = kwargs.get("ssh_timeout", 10) timeout = kwargs.get("timeout", SSH_CONNECT_TIMEOUT) node = self.create_node(**kwargs) ssh_config = self._vagrant_ssh_config(node.name) try: _, ip_addresses = self.wait_until_running( nodes=[node], wait_period=3, timeout=kwargs.get("timeout", NODE_ONLINE_WAIT_TIMEOUT), ssh_interface=ssh_config["host"])[0] self.log.info("Running deployment script on '%s' ..", node.name) self._connect_and_run_deployment_script( task=task, node=node, ssh_hostname=ip_addresses[0], ssh_port=ssh_config["port"], ssh_username=ssh_config["user"], ssh_password=None, ssh_key_file=ssh_config["key"], ssh_timeout=ssh_timeout, timeout=timeout, max_tries=max_tries) self.log.info(".. Finished deployment script on '%s' ..", node.name) except Exception as exc: raise DeploymentError(node=node, original_exception=exc, driver=self) return node