def _populate_hosts_and_request_paths(self): """ OpenStack uses a separate host for API calls which is only provided after an initial authentication request. """ if not self.auth_token: aurl = self.auth_url if self._ex_force_auth_url != None: aurl = self._ex_force_auth_url if aurl == None: raise LibcloudError('OpenStack instance must ' + 'have auth_url set') osa = OpenStackAuthConnection(self, aurl, self._auth_version, self.user_id, self.key, tenant_name=self._ex_tenant_name, timeout=self.timeout) # may throw InvalidCreds, etc osa.authenticate() self.auth_token = osa.auth_token self.auth_token_expires = osa.auth_token_expires self.auth_user_info = osa.auth_user_info # pull out and parse the service catalog self.service_catalog = OpenStackServiceCatalog( osa.urls, ex_force_auth_version=self._auth_version) # Set up connection info url = self._ex_force_base_url or self.get_endpoint() (self.host, self.port, self.secure, self.request_path) = \ self._tuple_from_url(url)
def __init__(self, user_id, key, secure=True, host=None, port=None, timeout=None, ex_force_base_url=None, ex_force_auth_url=None, ex_force_auth_version=None, ex_force_auth_token=None, ex_tenant_name=None, ex_force_service_type=None, ex_force_service_name=None, ex_force_service_region=None): self._ex_force_base_url = ex_force_base_url self._ex_force_auth_url = ex_force_auth_url self._auth_version = self._auth_version or ex_force_auth_version self._ex_tenant_name = ex_tenant_name self._ex_force_service_type = ex_force_service_type self._ex_force_service_name = ex_force_service_name self._ex_force_service_region = ex_force_service_region if ex_force_auth_token: self.auth_token = ex_force_auth_token if ex_force_auth_token and not ex_force_base_url: raise LibcloudError( 'Must also provide ex_force_base_url when specifying ' 'ex_force_auth_token.') if not self._auth_version: self._auth_version = AUTH_API_VERSION super(OpenStackBaseConnection, self).__init__(user_id, key, secure=secure, timeout=timeout)
def __init__(self, service_catalog, auth_version=AUTH_API_VERSION): self._auth_version = auth_version # Check this way because there are a couple of different 2.0_* # auth types. if '3.x' in self._auth_version: entries = self._parse_service_catalog_auth_v3( service_catalog=service_catalog) elif '2.0' in self._auth_version: entries = self._parse_service_catalog_auth_v2( service_catalog=service_catalog) elif ('1.1' in self._auth_version) or ('1.0' in self._auth_version): entries = self._parse_service_catalog_auth_v1( service_catalog=service_catalog) else: raise LibcloudError('auth version "%s" not supported' % (self._auth_version)) # Force consistent ordering by sorting the entries entries = sorted(entries, key=lambda x: x.service_type + (x.service_name or '')) self._entries = entries # stories all the service catalog entries
def get_auth_connection_instance(self): """ Return an OpenStackAuthConnection instance for this connection. """ auth_url = self.auth_url if self._ex_force_auth_url is not None: auth_url = self._ex_force_auth_url if auth_url is None: raise LibcloudError('OpenStack instance must ' + 'have auth_url set') if not self._auth_connection: self._auth_connection = OpenStackAuthConnection(self, auth_url, self._auth_version, self.user_id, self.key, tenant_name=self._ex_tenant_name, timeout=self.timeout) return self._auth_connection
def ex_create_group(self, name, location=None): """ Create an empty group. You can specify the location as well. @param name: name of the group (required) @type name: C{str} @param location: location were to create the group @type location: L{NodeLocation} @returns: the created group @rtype: L{NodeGroup} """ # prepare the element vapp = ET.Element('virtualAppliance') vapp_name = ET.SubElement(vapp, 'name') vapp_name.text = name if location is None: location = self.list_locations()[0] elif not location in self.list_locations(): raise LibcloudError('Location does not exist') link_vdc = self.connection.context['locations'][location] e_vdc = self.connection.request(link_vdc).object creation_link = get_href(e_vdc, 'virtualappliances') headers = {'Content-type': self.VAPP_MIME_TYPE} vapp = self.connection.request(creation_link, data=tostring(vapp), headers=headers, method='POST').object uri_vapp = get_href(vapp, 'edit') return NodeGroup(self, vapp.findtext('name'), uri=uri_vapp)
def add_default_headers(self, headers): headers[AUTH_TOKEN_HEADER] = self.auth_token headers["Accept"] = self.accept_format if self._ex_force_microversion: # If service not set in microversion, asume compute microversion = self._ex_force_microversion.strip().split() if len(microversion) == 2: service_type = microversion[0] microversion = microversion[1] elif len(microversion) == 1: service_type = "compute" microversion = microversion[0] else: raise LibcloudError( "Invalid microversion format: servicename X.XX") if self.service_type and self.service_type.startswith( service_type): headers["OpenStack-API-Version"] = "%s %s" % ( service_type, microversion, ) return headers
def authenticate(self, force=False): """ Authenticate against the keystone api. :param force: Forcefully update the token even if it's already cached and still valid. :type force: ``bool`` """ if not force and self.auth_version in AUTH_VERSIONS_WITH_EXPIRES \ and self.is_token_valid(): # If token is still valid, there is no need to re-authenticate return self if self.auth_version == "1.0": return self.authenticate_1_0() elif self.auth_version == "1.1": return self.authenticate_1_1() elif self.auth_version == "2.0" or self.auth_version == "2.0_apikey": return self.authenticate_2_0_with_apikey() elif self.auth_version == "2.0_password": return self.authenticate_2_0_with_password() else: raise LibcloudError('Unsupported Auth Version requested')
def ex_list_keypairs(self, ssh=False, password=False, key_group=None): """ List available console and server keys There are two types of keys for NephoScale, ssh and password keys. If run without arguments, lists all keys. Otherwise list only ssh keys, or only password keys. Password keys with key_group 4 are console keys. When a server is created, it has two keys, one password or ssh key, and one password console key. :keyword ssh: if specified, show ssh keys only (optional) :type ssh: ``bool`` :keyword password: if specified, show password keys only (optional) :type password: ``bool`` :keyword key_group: if specified, show keys with this key_group only eg key_group=4 for console password keys (optional) :type key_group: ``int`` :rtype: ``list`` of :class:`NodeKey` """ if (ssh and password): raise LibcloudError('You can only supply ssh or password. To \ get all keys call with no arguments') if ssh: result = self.connection.request('/key/sshrsa/').object elif password: result = self.connection.request('/key/password/').object else: result = self.connection.request('/key/').object keys = [self._to_key(value) for value in result.get('data', [])] if key_group: keys = [key for key in keys if key.key_group == key_group] return keys
def ex_register_iso(self, name, url, location=None, **kwargs): """ Registers an existing ISO by URL. :param name: Name which should be used :type name: ``str`` :param url: Url should be used :type url: ``str`` :param location: Location which should be used :type location: :class:`NodeLocation` :rtype: ``str`` """ if location is None: location = self.list_locations()[0] params = {'name': name, 'displaytext': name, 'url': url, 'zoneid': location.id} params['bootable'] = kwargs.pop('bootable', False) if params['bootable']: os_type_id = kwargs.pop('ostypeid', None) if not os_type_id: raise LibcloudError('If bootable=True, ostypeid is required!') params['ostypeid'] = os_type_id return self._sync_request(command='registerIso', name=name, displaytext=name, url=url, zoneid=location.id, params=params)
def ex_create_security_group(self, name, **kwargs): """ Creates a new Security Group :param name: name of the security group (required) :type name: ``str`` :param account: An optional account for the security group. Must be used with domainId. :type account: ``str`` :param domainid: An optional domainId for the security group. If the account parameter is used, domainId must also be used. :type domainid: ``str`` :param description: The description of the security group :type description: ``str`` :param projectid: Deploy vm for the project :type projectid: ``str`` :rtype: ``dict`` """ extra_args = kwargs.copy() for sg in self.ex_list_security_groups(): if name in sg['name']: raise LibcloudError('This Security Group name already exists') params = {'name': name} params.update(extra_args) return self._sync_request(command='createSecurityGroup', params=params, method='GET')['securitygroup']
def ex_create_keypair(self, name, **kwargs): """ Creates a SSH KeyPair, returns fingerprint and private key :param name: Name of the keypair (required) :type name: ``str`` :param projectid: An optional project for the ssh key :type projectid: ``str`` :param domainid: An optional domainId for the ssh key. If the account parameter is used, domainId must also be used. :type domainid: ``str`` :param account: An optional account for the ssh key. Must be used with domainId. :type account: ``str`` :return: A keypair dictionary :rtype: ``dict`` """ extra_args = kwargs.copy() for keypair in self.ex_list_keypairs(): if keypair['name'] == name: raise LibcloudError('SSH KeyPair with name=%s already exists' % (name)) params = {'name': name} params.update(extra_args) res = self._sync_request(command='createSSHKeyPair', params=params, method='GET') return res['keypair']
def _deploy_remote(self, e_vm): """ Asynchronous call to create the node. """ # -------------------------------------------------------- # Deploy the Node # -------------------------------------------------------- # prepare the element that forces the deploy vm_task = ET.Element("virtualmachinetask") force_deploy = ET.SubElement(vm_task, "forceEnterpriseSoftLimits") force_deploy.text = "True" # Prepare the headers headers = { "Accept": self.AR_MIME_TYPE, "Content-type": self.VM_TASK_MIME_TYPE } link_deploy = get_href(e_vm, "deploy") res = self.connection.async_request(action=link_deploy, method="POST", data=tostring(vm_task), headers=headers) if not res.async_success(): # pylint: disable=maybe-no-member raise LibcloudError("Could not run the node", self)
def get_endpoint(self): """ Selects the endpoint to use based on provider specific values, or overrides passed in by the user when setting up the driver. :returns: url of the relevant endpoint for the driver """ service_type = self.service_type service_name = self.service_name service_region = self.service_region if self._ex_force_service_type: service_type = self._ex_force_service_type if self._ex_force_service_name: service_name = self._ex_force_service_name if self._ex_force_service_region: service_region = self._ex_force_service_region ep = self.service_catalog.get_endpoint(service_type=service_type, name=service_name, region=service_region) if 'publicURL' in ep: return ep['publicURL'] raise LibcloudError('Could not find specified endpoint')
def _deploy_remote(self, e_vm): """ Asynchronous call to create the node. """ # -------------------------------------------------------- # Deploy the Node # -------------------------------------------------------- # prepare the element that forces the deploy vm_task = ET.Element('virtualmachinetask') force_deploy = ET.SubElement(vm_task, 'forceEnterpriseSoftLimits') force_deploy.text = 'True' # Prepare the headers headers = { 'Accept': self.AR_MIME_TYPE, 'Content-type': self.VM_TASK_MIME_TYPE } link_deploy = get_href(e_vm, 'deploy') res = self.connection.async_request(action=link_deploy, method='POST', data=tostring(vm_task), headers=headers) if not res.async_success(): raise LibcloudError('Could not run the node', self)
def ex_authorize_security_group_ingress(self, securitygroupname, protocol, cidrlist, startport=None, endport=None, icmptype=None, icmpcode=None, **kwargs): """ Creates a new Security Group Ingress rule :param securitygroupname: The name of the security group. Mutually exclusive with securitygroupid. :type securitygroupname: ``str`` :param protocol: Can be TCP, UDP or ICMP. Sometime other protocols can be used like AH, ESP, GRE. :type protocol: ``str`` :param cidrlist: Source address CIDR for which this rule applies. :type cidrlist: ``str`` :param startport: Start port of the range for this ingress rule. Applies to protocols TCP and UDP. :type startport: ``int`` :param endport: End port of the range for this ingress rule. It can be None to set only one port. Applies to protocols TCP and UDP. :type endport: ``int`` :param icmptype: Type of the ICMP packet (eg: 8 for Echo Request). -1 or None means "all types". Applies to protocol ICMP. :type icmptype: ``int`` :param icmpcode: Code of the ICMP packet for the specified type. If the specified type doesn't require a code set this value to 0. -1 or None means "all codes". Applies to protocol ICMP. :type icmpcode: ``int`` :keyword account: An optional account for the security group. Must be used with domainId. :type account: ``str`` :keyword domainid: An optional domainId for the security group. If the account parameter is used, domainId must also be used. :keyword projectid: An optional project of the security group :type projectid: ``str`` :param securitygroupid: The ID of the security group. Mutually exclusive with securitygroupname :type securitygroupid: ``str`` :param usersecuritygrouplist: User to security group mapping :type usersecuritygrouplist: ``dict`` :rtype: ``dict`` """ args = kwargs.copy() protocol = protocol.upper() args.update({ 'securitygroupname': securitygroupname, 'protocol': protocol, 'cidrlist': cidrlist }) if protocol not in ('TCP', 'UDP') and \ (startport is not None or endport is not None): raise LibcloudError('"startport" and "endport" are only valid with ' 'protocol TCP or UDP.') if protocol != 'ICMP' and (icmptype is not None or icmpcode is not None): raise LibcloudError('"icmptype" and "icmpcode" are only valid with ' 'protocol ICMP.') if protocol in ('TCP', 'UDP'): if startport is None: raise LibcloudError('Protocols TCP and UDP require at least ' '"startport" to be set.') if startport is not None and endport is None: endport = startport args.update({'startport': startport, 'endport': endport}) if protocol == 'ICMP': if icmptype is None: icmptype = -1 if icmpcode is None: icmpcode = -1 args.update({'icmptype': icmptype, 'icmpcode': icmpcode}) return self._async_request(command='authorizeSecurityGroupIngress', params=args, method='GET')['securitygroup']
def _populate_hosts_and_request_paths(self): """ OpenStack uses a separate host for API calls which is only provided after an initial authentication request. If we haven't made that request yet, do it here. Otherwise, just return the management host. """ if not self.auth_token: aurl = self.auth_url if self._ex_force_auth_url != None: aurl = self._ex_force_auth_url if aurl == None: raise LibcloudError( 'OpenStack instance must have auth_url set') osa = OpenStackAuthConnection(self, aurl, self._auth_version, self.user_id, self.key) # may throw InvalidCreds, etc osa.authenticate() self.auth_token = osa.auth_token # TODO: Multi-region support if self._auth_version == '2.0': for service in osa.urls: if service.get('type') == 'compute': self.server_url = self._get_default_region( service.get('endpoints', [])) elif self._auth_version in ['1.1', '1.0']: self.server_url = self._get_default_region( osa.urls.get('cloudServers', [])) self.cdn_management_url = self._get_default_region( osa.urls.get('cloudFilesCDN', [])) self.storage_url = self._get_default_region( osa.urls.get('cloudFiles', [])) # TODO: this is even more broken, the service catalog does NOT show load # balanacers :( You must hard code in the Rackspace Load balancer URLs... self.lb_url = self.server_url.replace("servers", "ord.loadbalancers") self.dns_url = self.server_url.replace("servers", "dns") else: raise LibcloudError('auth version "%s" not supported' % (self._auth_version)) for key in [ 'server_url', 'storage_url', 'cdn_management_url', 'lb_url', 'dns_url' ]: base_url = None if self._force_base_url != None: base_url = self._force_base_url else: base_url = getattr(self, key) scheme, server, request_path, param, query, fragment = ( urlparse.urlparse(base_url)) # Set host to where we want to make further requests to setattr(self, '__%s' % (key), server + request_path) setattr(self, '__request_path_%s' % (key), request_path) (self.host, self.port, self.secure, self.request_path) = self._tuple_from_url(self.base_url)
def ex_authorize_security_group_ingress(self, securitygroupname, protocol, cidrlist, startport, endport=None): """ Creates a new Security Group Ingress rule @param domainid: An optional domainId for the security group. If the account parameter is used, domainId must also be used. @type domainid: C{uuid} @param startport: Start port for this ingress rule @type startport: C{int} @param securitygroupid: The ID of the security group. Mutually exclusive with securityGroupName parameter @type securitygroupid: C{uuid} @param cidrlist: The cidr list associated @type cidrlist: C{list} @param usersecuritygrouplist: user to security group mapping @type usersecuritygrouplist: C{map} @param securitygroupname: The name of the security group. Mutually exclusive with securityGroupName parameter @type securitygroupname: C{str} @param account: An optional account for the security group. Must be used with domainId. @type account: C{str} @param icmpcode: Error code for this icmp message @type icmpcode: C{int} @param protocol: TCP is default. UDP is the other supported protocol @type protocol: C{str} @param icmptype: type of the icmp message being sent @type icmptype: C{int} @param projectid: An optional project of the security group @type projectid: C{uuid} @param endport: end port for this ingress rule @type endport: C{int} @rtype: C{list} """ protocol = protocol.upper() if protocol not in ('TCP', 'ICMP'): raise LibcloudError('Only TCP and ICMP are allowed') args = { 'securitygroupname': securitygroupname, 'protocol': protocol, 'startport': int(startport), 'cidrlist': cidrlist } if endport is None: args['endport'] = int(startport) return self._async_request('authorizeSecurityGroupIngress', **args)['securitygroup']
def __init__( self, user_id, key, secure=True, host=None, port=None, timeout=None, proxy_url=None, ex_force_base_url=None, ex_force_auth_url=None, ex_force_auth_version=None, ex_force_auth_token=None, ex_token_scope=OpenStackIdentityTokenScope.PROJECT, ex_domain_name="Default", ex_tenant_name=None, ex_tenant_domain_id="default", ex_force_service_type=None, ex_force_service_name=None, ex_force_service_region=None, ex_force_microversion=None, ex_auth_cache=None, retry_delay=None, backoff=None, ): super(OpenStackBaseConnection, self).__init__( user_id, key, secure=secure, timeout=timeout, retry_delay=retry_delay, backoff=backoff, proxy_url=proxy_url, ) if ex_force_auth_version: self._auth_version = ex_force_auth_version self.base_url = ex_force_base_url self._ex_force_base_url = ex_force_base_url self._ex_force_auth_url = ex_force_auth_url self._ex_force_auth_token = ex_force_auth_token self._ex_token_scope = ex_token_scope self._ex_domain_name = ex_domain_name self._ex_tenant_name = ex_tenant_name self._ex_tenant_domain_id = ex_tenant_domain_id self._ex_force_service_type = ex_force_service_type self._ex_force_service_name = ex_force_service_name self._ex_force_service_region = ex_force_service_region self._ex_force_microversion = ex_force_microversion self._ex_auth_cache = ex_auth_cache self._osa = None if ex_force_auth_token and not ex_force_base_url: raise LibcloudError( "Must also provide ex_force_base_url when specifying " "ex_force_auth_token.") if ex_force_auth_token: self.auth_token = ex_force_auth_token if not self._auth_version: self._auth_version = AUTH_API_VERSION auth_url = self._get_auth_url() if not auth_url: raise LibcloudError("OpenStack instance must " + "have auth_url set")