mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-11-04 00:02:37 +01:00 
			
		
		
		
	* DB : Add support for MySQL 8
- Splits commands to create user and grant access on database, the old
statement is no longer supported by MySQL 8.x
- `NO_AUTO_CREATE_USER` is no longer supported by MySQL 8.x so remove
that from db.properties conn parameters
For mysql-server 8.x setup the following changes were added/tested to
make it work with CloudStack in /etc/mysql/mysql.conf.d/mysqld.cnf and
then restart the mysql-server process:
    server_id = 1
    sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION,ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE,NO_ZERO_IN_DATE,NO_ENGINE_SUBSTITUTION"
    innodb_rollback_on_timeout=1
    innodb_lock_wait_timeout=600
    max_connections=1000
    log-bin=mysql-bin
    binlog-format = 'ROW'
    default-authentication-plugin=mysql_native_password
Notice the last line above, this is to reset the old password based
authentication used by MySQL 5.x.
Developers can set empty password as follows:
    > sudo mysql -u root
    ALTER USER 'root'@'localhost' IDENTIFIED BY '';
In libvirt repository, there are two related commits
2019-08-23 13:13 Daniel P. Berrangé            ● rpm: don't enable socket activation in upgrade if --listen present
2019-08-22 14:52 Daniel P. Berrangé            ● remote: forbid the --listen arg when systemd socket activation
In libvirt.spec.in
        /bin/systemctl mask libvirtd.socket >/dev/null 2>&1 || :
        /bin/systemctl mask libvirtd-ro.socket >/dev/null 2>&1 || :
        /bin/systemctl mask libvirtd-admin.socket >/dev/null 2>&1 || :
        /bin/systemctl mask libvirtd-tls.socket >/dev/null 2>&1 || :
        /bin/systemctl mask libvirtd-tcp.socket >/dev/null 2>&1 || :
Co-authored-by: Wei Zhou <w.zhou@global.leaseweb.com>
Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
Co-authored-by: Rohit Yadav <rohit.yadav@shapeblue.com>
		
	
			
		
			
				
	
	
		
			199 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# 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.
 | 
						|
 | 
						|
 | 
						|
 | 
						|
'''Implements the CloudStack API'''
 | 
						|
 | 
						|
 | 
						|
from cloudtool.utils import describe
 | 
						|
import urllib.request, urllib.parse, urllib.error
 | 
						|
import urllib.request, urllib.error, urllib.parse
 | 
						|
import os
 | 
						|
import xml.dom.minidom
 | 
						|
import re
 | 
						|
import base64
 | 
						|
import hmac
 | 
						|
import hashlib
 | 
						|
import http.client
 | 
						|
 | 
						|
class CloudAPI:
 | 
						|
 | 
						|
    @describe("server", "Management Server host name or address")
 | 
						|
    @describe("apikey", "Management Server apiKey")
 | 
						|
    @describe("securitykey", "Management Server securityKey")
 | 
						|
    @describe("responseformat", "Response format: xml or json")
 | 
						|
    @describe("stripxml", "True if xml tags have to be stripped in the output, false otherwise")
 | 
						|
    def __init__(self,
 | 
						|
            server="127.0.0.1:8096",
 | 
						|
            responseformat="xml",
 | 
						|
            stripxml="true",
 | 
						|
            apiKey=None,
 | 
						|
            securityKey=None
 | 
						|
            ):
 | 
						|
        self.__dict__.update(locals())
 | 
						|
 | 
						|
    def _make_request_with_keys(self,command,requests={}):
 | 
						|
        requests["command"] = command
 | 
						|
        requests["apiKey"] = self.apiKey
 | 
						|
        requests["response"] = "xml"
 | 
						|
        requests = list(zip(list(requests.keys()), list(requests.values())))
 | 
						|
        requests.sort(key=lambda x: str.lower(x[0]))
 | 
						|
 | 
						|
        requestUrl = "&".join(["=".join([request[0], urllib.parse.quote_plus(str(request[1]))]) for request in requests])
 | 
						|
        hashStr = "&".join(["=".join([str.lower(request[0]), urllib.parse.quote_plus(str.lower(str(request[1])))]) for request in requests])
 | 
						|
 | 
						|
        sig = urllib.parse.quote_plus(base64.encodestring(hmac.new(self.securityKey, hashStr, hashlib.sha1).digest()).strip())
 | 
						|
 | 
						|
        requestUrl += "&signature=%s"%sig
 | 
						|
        return requestUrl
 | 
						|
 | 
						|
 | 
						|
    def _make_request_with_auth(self, command, requests):
 | 
						|
        self.connection = http.client.HTTPConnection("%s"%(self.server))
 | 
						|
        requests["command"] = command
 | 
						|
        requests["apiKey"] = self.apiKey
 | 
						|
        requests["response"] = self.responseformat
 | 
						|
        requests = list(zip(list(requests.keys()), list(requests.values())))
 | 
						|
        requests.sort(key=lambda x: str.lower(x[0]))
 | 
						|
 | 
						|
        requestUrl = "&".join(["=".join([request[0], urllib.parse.quote(str(request[1],""))]) for request in requests])
 | 
						|
        hashStr = "&".join(["=".join([str.lower(request[0]), urllib.parse.quote(str.lower(str(request[1])),"")]) for request in requests])
 | 
						|
 | 
						|
        sig = urllib.parse.quote_plus(base64.encodestring(hmac.new(self.securityKey, str.lower(hashStr), hashlib.sha1).digest()).strip())
 | 
						|
 | 
						|
        requestUrl += "&signature=%s"%sig
 | 
						|
 | 
						|
        self.connection.request("GET", "/client/api?%s"%requestUrl)
 | 
						|
        return self.connection.getresponse().read()
 | 
						|
 | 
						|
    def _make_request(self,command,parameters=None):
 | 
						|
 | 
						|
        '''Command is a string, parameters is a dictionary'''
 | 
						|
        if ":" in self.server:
 | 
						|
            host,port = self.server.split(":")
 | 
						|
            port = int(port)
 | 
						|
        else:
 | 
						|
            host = self.server
 | 
						|
            port = 8096
 | 
						|
 | 
						|
        url = "http://" + self.server + "/client/api?"
 | 
						|
 | 
						|
        if not parameters: parameters = {}
 | 
						|
        if self.apiKey is not None and self.securityKey is not None:
 | 
						|
            return self._make_request_with_auth(command, parameters)
 | 
						|
        else:
 | 
						|
            parameters["command"] = command
 | 
						|
            parameters["response"] = self.responseformat
 | 
						|
            querystring = urllib.parse.urlencode(parameters)
 | 
						|
 | 
						|
        url += querystring
 | 
						|
 | 
						|
        f = urllib.request.urlopen(url)
 | 
						|
        data = f.read()
 | 
						|
        if self.stripxml == "true":
 | 
						|
            data=re.sub("<\?.*\?>", "\n", data);
 | 
						|
            data=re.sub("</[a-z]*>", "\n", data);
 | 
						|
            data=data.replace(">", "=");
 | 
						|
            data=data.replace("=<", "\n");
 | 
						|
            data=data.replace("\n<", "\n");
 | 
						|
            data=re.sub("\n.*cloud-stack-version=.*", "", data);
 | 
						|
            data=data.replace("\n\n\n", "\n");
 | 
						|
 | 
						|
        return data
 | 
						|
 | 
						|
 | 
						|
def load_dynamic_methods():
 | 
						|
    '''creates smart function objects for every method in the commands.xml file'''
 | 
						|
 | 
						|
    def getText(nodelist):
 | 
						|
        rc = []
 | 
						|
        for node in nodelist:
 | 
						|
            if node.nodeType == node.TEXT_NODE: rc.append(node.data)
 | 
						|
        return ''.join(rc)
 | 
						|
 | 
						|
    # FIXME figure out installation and packaging
 | 
						|
    xmlfile = os.path.join("/etc/cloud/cli/","commands.xml")
 | 
						|
    dom = xml.dom.minidom.parse(xmlfile)
 | 
						|
 | 
						|
    for cmd in dom.getElementsByTagName("command"):
 | 
						|
        name = getText(cmd.getElementsByTagName('name')[0].childNodes).strip()
 | 
						|
        assert name
 | 
						|
 | 
						|
        description = getText(cmd.getElementsByTagName('description')[0].childNodes).strip()
 | 
						|
        if description:
 | 
						|
                    description = '"""%s"""' % description
 | 
						|
        else: description = ''
 | 
						|
        arguments = []
 | 
						|
        options = []
 | 
						|
        descriptions = []
 | 
						|
 | 
						|
        for param in cmd.getElementsByTagName("request")[0].getElementsByTagName("arg"):
 | 
						|
            argname = getText(param.getElementsByTagName('name')[0].childNodes).strip()
 | 
						|
            assert argname
 | 
						|
 | 
						|
            required = getText(param.getElementsByTagName('required')[0].childNodes).strip()
 | 
						|
            if required == 'true': required = True
 | 
						|
            elif required == 'false': required = False
 | 
						|
            else: raise AssertionError("Not reached")
 | 
						|
            if required: arguments.append(argname)
 | 
						|
            options.append(argname)
 | 
						|
 | 
						|
                        #import ipdb; ipdb.set_trace()
 | 
						|
            requestDescription = param.getElementsByTagName('description')
 | 
						|
            if requestDescription:
 | 
						|
                descriptionParam = getText(requestDescription[0].childNodes)
 | 
						|
            else:
 | 
						|
                descriptionParam = ''
 | 
						|
            if descriptionParam: descriptions.append( (argname,descriptionParam) )
 | 
						|
 | 
						|
        funcparams = ["self"] + [ "%s=None"%o for o in options ]
 | 
						|
        funcparams = ", ".join(funcparams)
 | 
						|
 | 
						|
        code = """
 | 
						|
        def %s(%s):
 | 
						|
            %s
 | 
						|
            parms = dict(locals())
 | 
						|
            del parms["self"]
 | 
						|
            for arg in %r:
 | 
						|
                if locals()[arg] is None:
 | 
						|
                    raise TypeError, "%%s is a required option"%%arg
 | 
						|
            for k,v in parms.items():
 | 
						|
                if v is None: del parms[k]
 | 
						|
            output = self._make_request("%s",parms)
 | 
						|
            return output
 | 
						|
        """%(name,funcparams,description,arguments,name)
 | 
						|
 | 
						|
        namespace = {}
 | 
						|
        exec(code.strip(), namespace)
 | 
						|
 | 
						|
        func = namespace[name]
 | 
						|
        for argname,description in descriptions:
 | 
						|
            func = describe(argname,description)(func)
 | 
						|
 | 
						|
        yield (name,func)
 | 
						|
 | 
						|
 | 
						|
for name,meth in load_dynamic_methods():
 | 
						|
    setattr(CloudAPI, name, meth)
 | 
						|
 | 
						|
implementor = CloudAPI
 | 
						|
 | 
						|
del name,meth,describe,load_dynamic_methods
 | 
						|
 | 
						|
 |