Server IP : 85.214.239.14 / Your IP : 3.141.192.174 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /proc/2/root/proc/3/root/lib/python3/dist-packages/libcloud/compute/drivers/ |
Upload File : |
# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Enomaly ECP driver """ import time import os import socket import binascii from libcloud.utils.py3 import httplib from libcloud.utils.py3 import b from libcloud.utils.py3 import base64_encode_string # JSON is included in the standard library starting with Python 2.6. For 2.5 # and 2.4, there's a simplejson egg at: http://pypi.python.org/pypi/simplejson try: import simplejson as json except ImportError: import json from libcloud.common.base import Response, ConnectionUserAndKey from libcloud.compute.base import NodeDriver, NodeSize, NodeLocation from libcloud.compute.base import NodeImage, Node from libcloud.compute.types import Provider, NodeState, InvalidCredsError from libcloud.utils.networking import is_private_subnet # Defaults API_HOST = '' API_PORT = (80, 443) class ECPResponse(Response): def success(self): if self.status == httplib.OK or self.status == httplib.CREATED: try: j_body = json.loads(self.body) except ValueError: self.error = "JSON response cannot be decoded." return False if j_body['errno'] == 0: return True else: self.error = "ECP error: %s" % j_body['message'] return False elif self.status == httplib.UNAUTHORIZED: raise InvalidCredsError() else: self.error = "HTTP Error Code: %s" % self.status return False def parse_error(self): return self.error # Interpret the json responses - no error checking required def parse_body(self): return json.loads(self.body) def getheaders(self): return self.headers class ECPConnection(ConnectionUserAndKey): """ Connection class for the Enomaly ECP driver """ responseCls = ECPResponse host = API_HOST port = API_PORT def add_default_headers(self, headers): # Authentication username = self.user_id password = self.key base64string = base64_encode_string( b('%s:%s' % (username, password)))[:-1] authheader = "Basic %s" % base64string headers['Authorization'] = authheader return headers def _encode_multipart_formdata(self, fields): """ Based on Wade Leftwich's function: http://code.activestate.com/recipes/146306/ """ # use a random boundary that does not appear in the fields boundary = '' while boundary in ''.join(fields): boundary = binascii.hexlify(os.urandom(16)).decode('utf-8') L = [] for i in fields: L.append('--' + boundary) L.append('Content-Disposition: form-data; name="%s"' % i) L.append('') L.append(fields[i]) L.append('--' + boundary + '--') L.append('') body = '\r\n'.join(L) content_type = 'multipart/form-data; boundary=%s' % boundary header = {'Content-Type': content_type} return header, body class ECPNodeDriver(NodeDriver): """ Enomaly ECP node driver """ name = "Enomaly Elastic Computing Platform" website = 'http://www.enomaly.com/' type = Provider.ECP connectionCls = ECPConnection def list_nodes(self): """ Returns a list of all running Nodes :rtype: ``list`` of :class:`Node` """ # Make the call res = self.connection.request('/rest/hosting/vm/list').parse_body() # Put together a list of node objects nodes = [] for vm in res['vms']: node = self._to_node(vm) if node is not None: nodes.append(node) # And return it return nodes def _to_node(self, vm): """ Turns a (json) dictionary into a Node object. This returns only running VMs. """ # Check state if not vm['state'] == "running": return None # IPs iplist = [interface['ip'] for interface in vm['interfaces'] if interface['ip'] != '127.0.0.1'] public_ips = [] private_ips = [] for ip in iplist: try: socket.inet_aton(ip) except socket.error: # not a valid ip continue if is_private_subnet(ip): private_ips.append(ip) else: public_ips.append(ip) # Create the node object n = Node( id=vm['uuid'], name=vm['name'], state=NodeState.RUNNING, public_ips=public_ips, private_ips=private_ips, driver=self, ) return n def reboot_node(self, node): """ Shuts down a VM and then starts it again. @inherits: :class:`NodeDriver.reboot_node` """ # Turn the VM off # Black magic to make the POST requests work d = self.connection._encode_multipart_formdata({'action': 'stop'}) self.connection.request( '/rest/hosting/vm/%s' % node.id, method='POST', headers=d[0], data=d[1] ).parse_body() node.state = NodeState.REBOOTING # Wait for it to turn off and then continue (to turn it on again) while node.state == NodeState.REBOOTING: # Check if it's off. response = self.connection.request( '/rest/hosting/vm/%s' % node.id ).parse_body() if response['vm']['state'] == 'off': node.state = NodeState.TERMINATED else: time.sleep(5) # Turn the VM back on. # Black magic to make the POST requests work d = self.connection._encode_multipart_formdata({'action': 'start'}) self.connection.request( '/rest/hosting/vm/%s' % node.id, method='POST', headers=d[0], data=d[1] ).parse_body() node.state = NodeState.RUNNING return True def destroy_node(self, node): """ Shuts down and deletes a VM. @inherits: :class:`NodeDriver.destroy_node` """ # Shut down first # Black magic to make the POST requests work d = self.connection._encode_multipart_formdata({'action': 'stop'}) self.connection.request( '/rest/hosting/vm/%s' % node.id, method='POST', headers=d[0], data=d[1] ).parse_body() # Ensure there was no application level error node.state = NodeState.PENDING # Wait for the VM to turn off before continuing while node.state == NodeState.PENDING: # Check if it's off. response = self.connection.request( '/rest/hosting/vm/%s' % node.id ).parse_body() if response['vm']['state'] == 'off': node.state = NodeState.TERMINATED else: time.sleep(5) # Delete the VM # Black magic to make the POST requests work d = self.connection._encode_multipart_formdata({'action': 'delete'}) self.connection.request( '/rest/hosting/vm/%s' % (node.id), method='POST', headers=d[0], data=d[1] ).parse_body() return True def list_images(self, location=None): """ Returns a list of all package templates aka appliances aka images. @inherits: :class:`NodeDriver.list_images` """ # Make the call response = self.connection.request( '/rest/hosting/ptemplate/list').parse_body() # Turn the response into an array of NodeImage objects images = [] for ptemplate in response['packages']: images.append(NodeImage( id=ptemplate['uuid'], name='%s: %s' % (ptemplate['name'], ptemplate['description']), driver=self,) ) return images def list_sizes(self, location=None): """ Returns a list of all hardware templates @inherits: :class:`NodeDriver.list_sizes` """ # Make the call response = self.connection.request( '/rest/hosting/htemplate/list').parse_body() # Turn the response into an array of NodeSize objects sizes = [] for htemplate in response['templates']: sizes.append(NodeSize( id=htemplate['uuid'], name=htemplate['name'], ram=htemplate['memory'], disk=0, # Disk is independent of hardware template. bandwidth=0, # There is no way to keep track of bandwidth. price=0, # The billing system is external. driver=self,) ) return sizes def list_locations(self): """ This feature does not exist in ECP. Returns hard coded dummy location. :rtype: ``list`` of :class:`NodeLocation` """ return [NodeLocation(id=1, name="Cloud", country='', driver=self), ] def create_node(self, name, size, image): """ Creates a virtual machine. :keyword name: String with a name for this new node (required) :type name: ``str`` :keyword size: The size of resources allocated to this node . (required) :type size: :class:`NodeSize` :keyword image: OS Image to boot on node. (required) :type image: :class:`NodeImage` :rtype: :class:`Node` """ # Find out what network to put the VM on. res = self.connection.request( '/rest/hosting/network/list').parse_body() # Use the first / default network because there is no way to specific # which one network = res['networks'][0]['uuid'] # Prepare to make the VM data = { 'name': str(name), 'package': str(image.id), 'hardware': str(size.id), 'network_uuid': str(network), 'disk': '' } # Black magic to make the POST requests work d = self.connection._encode_multipart_formdata(data) response = self.connection.request( '/rest/hosting/vm/', method='PUT', headers=d[0], data=d[1] ).parse_body() # Create a node object and return it. n = Node( id=response['machine_id'], name=data['name'], state=NodeState.PENDING, public_ips=[], private_ips=[], driver=self, ) return n