Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | import os |
| 3 | import sys,json,datetime,time,types,httplib,re |
| 4 | import mimetypes |
| 5 | |
| 6 | DEFAULT_HOST = "127.0.0.1" |
| 7 | OPENECOMP_BE = "127.0.0.1" |
| 8 | |
| 9 | HOST = DEFAULT_HOST |
| 10 | DEFAULT_PORT = "8080" |
| 11 | DEFAULT_USERNAME = "cs0008" |
| 12 | DEFAULT_PASSWORD = "cs0008" |
| 13 | |
| 14 | ONBOARD_BASE_PATH = "/onboarding-api/v1.0" |
| 15 | VSP_LIST_PATH = "{0}/vendor-software-products".format(ONBOARD_BASE_PATH) |
| 16 | VSP_ACTIONS_PATH = "{0}/vendor-software-products/{{vspId}}/actions".format(ONBOARD_BASE_PATH) |
| 17 | VSP_UPLOAD_PATH = "{0}/vendor-software-products/{{vspId}}/upload".format(ONBOARD_BASE_PATH) |
| 18 | VSP_DOWNLOAD_PATH = "{0}/vendor-software-products/{{vspId}}/downloadHeat".format(ONBOARD_BASE_PATH) |
| 19 | VSP_GET_URL = "{0}/vendor-software-products/{{vspId}}".format(ONBOARD_BASE_PATH) |
| 20 | |
| 21 | def main(argv): |
| 22 | username=DEFAULT_USERNAME |
| 23 | password=DEFAULT_PASSWORD |
| 24 | host=DEFAULT_HOST |
| 25 | |
| 26 | if not argv: |
| 27 | print "Going to use default values" |
| 28 | else: |
| 29 | if argv[0].lower() == 'h' or argv[0].lower() == '-h': |
| 30 | printHelp() |
| 31 | return |
| 32 | |
| 33 | if argv[0] == '-ip': |
| 34 | host=argv[1] |
| 35 | else: |
| 36 | if argv[0].lower() == '-a' and '/' not in argv[1]: |
| 37 | print '\n>>> Error: Credentials required (username/password)\n' |
| 38 | printHelp() |
| 39 | return |
| 40 | |
| 41 | else: |
| 42 | creds = argv[1].split('/') |
| 43 | username = creds[0] |
| 44 | password = creds[1] # not used |
| 45 | |
| 46 | try: |
| 47 | cmdIp=argv[2] |
| 48 | host=argv[3] |
| 49 | except IndexError: |
| 50 | host=DEFAULT_HOST |
| 51 | print "Going to use user defined values" |
| 52 | Service.server(host) |
| 53 | |
| 54 | webHandler=WebHandler(host=host, port=DEFAULT_PORT) |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 55 | response, headers = webHandler.rest(url=VSP_LIST_PATH, method='GET', data=None, userId=username) |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 56 | jResult = json.loads(response) |
| 57 | jSrvices = jResult["results"] |
| 58 | reportFileName = 'upgradereport.csv' #datetime.now() |
| 59 | reportFile = open(reportFileName, 'w') |
| 60 | reportFile.write(Service.header()) |
| 61 | |
| 62 | for jService in jSrvices: |
| 63 | serviceName = jService["name"] |
| 64 | vendorName = jService["vendorName"] |
| 65 | vspId = jService["id"] |
| 66 | status = jService["status"] |
| 67 | if status != "Locked": |
| 68 | lockingUser = "None" |
| 69 | else: |
| 70 | lockingUser = jService["lockingUser"] |
| 71 | |
| 72 | service = Service(serviceName=serviceName, vspId=vspId, vendorName=vendorName, lockingUser=lockingUser ) |
| 73 | print service |
| 74 | # Will try to GET the service |
| 75 | res = service.Get() |
| 76 | if res == 500: |
| 77 | serviceMigration(service, status, username) |
| 78 | else: |
| 79 | print "Service {0} was tested and does not need a migration".format(serviceName) |
| 80 | |
| 81 | reportFile.write(service.line()) |
| 82 | reportFile.close() |
| 83 | |
| 84 | |
| 85 | def serviceMigration(service, serviceStatus, username): |
| 86 | print "Service {0} was tested and it needs a migration".format(service.serviceName) |
| 87 | print "Service {0} - Migration start" |
| 88 | if serviceStatus == "Locked": |
| 89 | print "Service {0} is locked - forcing checkin".format(service.serviceName) |
| 90 | service.Checkin() |
| 91 | print "Doing new checkout" |
| 92 | service.Checkout(username) |
| 93 | |
| 94 | zipName = service.DownloadHeat() |
| 95 | if not zipName: |
| 96 | print "no heat found" |
| 97 | service.uploadStatus = "no heat found" |
| 98 | else: |
| 99 | uploadResponse = service.UploadHeat(zipName) |
| 100 | uploadResults = json.loads(uploadResponse) |
| 101 | if uploadResults['status'] == 'Success' and uploadResults['errors'].__len__() == 0: |
| 102 | service.uploadStatus = "Heat uploaded successfully" |
| 103 | else: |
| 104 | service.uploadStatus = "Heat uploaded with errors" |
| 105 | print "Doing new checkin" |
| 106 | service.Checkin() |
| 107 | |
| 108 | print "Service {0} - Migration end" |
| 109 | |
| 110 | |
| 111 | def printHelp(): |
| 112 | print("Upgrade script Help:") |
| 113 | print("==================================") |
| 114 | print("1607_to_1610 -h --> get help") |
| 115 | print("1607_to_1610 -a <username>/<password> [-ip {ip}]") |
| 116 | print("Example: 1607_to_1610 -a root/secret") |
| 117 | |
| 118 | class Service(object): |
| 119 | def __init__(self, serviceName, vspId ,vendorName, lockingUser): |
| 120 | self.serviceName = serviceName |
| 121 | self.vspId = vspId |
| 122 | self.vendorName = vendorName |
| 123 | self.lockingUser = lockingUser |
| 124 | self.webHandler = WebHandler(host=Service.serveraddress, port=DEFAULT_PORT) # Schema? |
| 125 | self.uploadStatus = "not started" |
| 126 | |
| 127 | def __repr__(self): |
| 128 | return 'Name: {0}, Id: {1}, Vendor: {2}, locked by: {3}, status {4}'.format(self.serviceName, self.vspId ,self.vendorName, self.lockingUser, self.uploadStatus) |
| 129 | @classmethod |
| 130 | def header(cls): |
| 131 | return 'Name,Id,Vendor,locked-by,status\n' |
| 132 | |
| 133 | @classmethod |
| 134 | def server(cls, address): |
| 135 | cls.serveraddress=address |
| 136 | |
| 137 | def line(self): |
| 138 | return '{0},{1},{2},{3},{4}\n'.format(self.serviceName, self.vspId ,self.vendorName, self.lockingUser, self.uploadStatus) |
| 139 | |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 140 | def Checkout(self, userId): |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 141 | # /v1.0/vendor-software-products/{vspId}/actions |
| 142 | urlpath=VSP_ACTIONS_PATH.format(vspId=self.vspId) |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 143 | response, headers = self.webHandler.rest( url=urlpath, method='PUT', data={"action": "Checkout"}, userId=userId) |
| 144 | self.lockingUser=userId #we will later use this user to checkin |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 145 | return response |
| 146 | |
| 147 | def Checkin(self): |
| 148 | # /v1.0/vendor-software-products/{vspId}/actions |
| 149 | urlpath = VSP_ACTIONS_PATH.format(vspId=self.vspId) |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 150 | response, headers = self.webHandler.rest(url=urlpath, method='PUT', data={"action": "Checkin"}, userId=self.lockingUser) |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 151 | return response |
| 152 | |
| 153 | def Get(self): |
| 154 | # /v1.0/vendor-software-products/{vspId} |
| 155 | urlpath = VSP_GET_URL.format(vspId=self.vspId) |
| 156 | try: |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 157 | response, headers = self.webHandler.rest(url=urlpath, method='GET', data=None, userId=self.lockingUser) |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 158 | except HttpError as e: |
| 159 | print e.message |
| 160 | response = e.status |
| 161 | return response |
| 162 | |
| 163 | def UploadHeat(self, zipName): |
| 164 | #/v1.0/vendor-software-products/{vspId}/upload |
| 165 | urlpath = VSP_UPLOAD_PATH.format(vspId=self.vspId) |
| 166 | try: |
| 167 | fields = [] |
| 168 | with open(zipName, 'rb') as fin: |
| 169 | buffer = fin.read() |
| 170 | fin.close() |
| 171 | files = [('upload', 'heatfile.zip', buffer)] |
| 172 | response = self.webHandler.post_multipart('HTTP', urlpath, fields, files, self.lockingUser) |
| 173 | |
| 174 | return response |
| 175 | finally: |
| 176 | print "done upload" |
| 177 | |
| 178 | def DownloadHeat(self): |
| 179 | urlpath=VSP_DOWNLOAD_PATH.format(vspId=self.vspId) |
| 180 | try: |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 181 | response, headers = self.webHandler.rest(url=urlpath, method='Get', data=None, userId=self.lockingUser, accept='application/octet-stream') |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 182 | except HttpError as e: |
| 183 | if e.status == 404: |
| 184 | return "" |
| 185 | |
| 186 | for (key, value) in headers: |
| 187 | if key.lower() == "content-disposition": |
| 188 | file_name = value[value.index('=')+1:] |
| 189 | break |
| 190 | heatsDir= os.path.join(os.path.dirname(__file__), 'heats') |
| 191 | if not os.path.exists(heatsDir): |
| 192 | os.makedirs(heatsDir) |
| 193 | file_name = os.path.join(heatsDir, file_name) |
| 194 | with open(file_name, "wb") as fout: |
| 195 | fout.write(response) |
| 196 | fout.close() |
| 197 | |
| 198 | return file_name |
| 199 | |
| 200 | |
| 201 | |
| 202 | class WebHandler(object): |
| 203 | def __init__(self, host, port): |
| 204 | self.host = host |
| 205 | self.port = port |
| 206 | |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 207 | def rest(self, url, method, data, userId, accept='application/json', content_type='application/json'): |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 208 | connection = httplib.HTTPConnection(host=self.host, port=self.port) |
| 209 | |
| 210 | try: |
| 211 | headers = {'Content-Type':content_type ,'Accept':accept} |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 212 | headers['USER_ID'] = userId |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 213 | |
| 214 | connection.request(method=method, headers=headers, body=json.dumps(data), url=url) |
| 215 | response = connection.getresponse() |
| 216 | if response.status not in range(200, 300): |
| 217 | raise HttpError(status= response.status, message=response.reason) |
| 218 | |
| 219 | return response.read(), response.getheaders() |
| 220 | finally: |
| 221 | connection.close() |
| 222 | |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 223 | def post_multipart(self, scheme, selector, fields, files, userId): |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 224 | """ |
| 225 | Post fields and files to an http host as multipart/form-data. |
| 226 | fields is a sequence of (name, value) elements for regular form fields. |
| 227 | files is a sequence of (name, filename, value) elements for data to be uploaded as files |
| 228 | Return the server's response page. |
| 229 | """ |
| 230 | content_type, body = self.encode_multipart_form_data(fields, files) |
| 231 | if scheme and scheme.lower() == "http": |
| 232 | h = httplib.HTTP(self.host, self.port) |
| 233 | else: |
| 234 | h = httplib.HTTPS(self.host, self.port) |
| 235 | h.putrequest('POST', selector) |
| 236 | h.putheader('content-type', content_type) |
| 237 | h.putheader('content-length', str(len(body))) |
| 238 | h.putheader('Accept', 'application/json') |
AviZi | 280f801 | 2017-06-09 02:39:56 +0300 | [diff] [blame^] | 239 | h.putheader('USER_ID', userId) |
Michael Lando | f5f13c4 | 2017-02-19 12:35:04 +0200 | [diff] [blame] | 240 | |
| 241 | h.endheaders() |
| 242 | h.send(body) |
| 243 | errcode, errmsg, headers = h.getreply() |
| 244 | print errcode, errmsg, headers |
| 245 | return h.file.read() |
| 246 | |
| 247 | def encode_multipart_form_data(self, fields, files): |
| 248 | LIMIT = '----------lImIt_of_THE_fIle_eW_$' |
| 249 | CRLF = '\r\n' |
| 250 | L = [] |
| 251 | for (key, value) in fields: |
| 252 | L.append('--' + LIMIT) |
| 253 | L.append('Content-Disposition: form-data; name="%s"' % key) |
| 254 | L.append('') |
| 255 | L.append(value) |
| 256 | for (key, filename, value) in files: |
| 257 | L.append('--' + LIMIT) |
| 258 | L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) |
| 259 | L.append('Content-Type: %s' % self.get_content_type(filename)) |
| 260 | L.append('') |
| 261 | L.append(value) |
| 262 | L.append('--' + LIMIT + '--') |
| 263 | L.append('') |
| 264 | body = CRLF.join(L) |
| 265 | content_type = 'multipart/form-data; boundary=%s' % LIMIT |
| 266 | return content_type, body |
| 267 | |
| 268 | def get_content_type(self, filename): |
| 269 | return mimetypes.guess_type(filename)[0] or 'application/octet-stream' |
| 270 | |
| 271 | class HttpError(Exception): |
| 272 | def __init__(self, status, message): |
| 273 | self.status = status |
| 274 | self.message=message |
| 275 | def __str__(self): |
| 276 | return repr(self.value, self.message) |
| 277 | |
| 278 | if __name__ == "__main__": |
| 279 | main(sys.argv[1:]) |
| 280 | |
| 281 | |