From b797d74253c4a89981ccdb08e5ea57bd1bf17b86 Mon Sep 17 00:00:00 2001 From: Jay Srinivasan Date: Wed, 31 Oct 2012 13:13:13 -0700 Subject: [PATCH] Upgrade cros_image_to_target.py to support Omaha v3 protocol. As part of the efforts to support HTTP-based downloads for autoupdate, we are upgrading update_engine from Omaha v2 to v3. So, we need to update cros_image_to_target.py to also understand the v3 XML. BUG=chromium-os:35930 TEST=Successfully updated my ZGB which was running the v3 update_engine. Change-Id: I2b4831c1e87ccf064e79cd6d34205f19aedc9d57 Reviewed-on: https://gerrit.chromium.org/gerrit/37065 Reviewed-by: Chris Sosa Reviewed-by: Paul Stewart Commit-Ready: Chris Sosa Tested-by: Chris Sosa --- bin/cros_image_to_target.py | 147 +++++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 37 deletions(-) diff --git a/bin/cros_image_to_target.py b/bin/cros_image_to_target.py index bb497f9249..5f758f2c76 100755 --- a/bin/cros_image_to_target.py +++ b/bin/cros_image_to_target.py @@ -39,6 +39,42 @@ STATEFUL_FILENAME = 'stateful.tgz' # How long do we wait for the server to start before launching client SERVER_STARTUP_WAIT = 1 +UPDATE_RESPONSE = """ + + + + + + + + + + + + + + + + + + + + """ +NO_UPDATE_RESPONSE = """ + + + + + + + + """ class Command(object): """Shell command ease-ups for Python.""" @@ -449,34 +485,6 @@ class StringUpdateResponse(UpdateResponse): class PingUpdateResponse(StringUpdateResponse): """Respond to a client ping with pre-fab XML response.""" - app_id = '87efface-864d-49a5-9bb3-4b050a7c227a' - xmlns = 'http://www.google.com/update2/response' - payload_success_template = """ - - - - - - - - """ - payload_failure_template = """ - - - - - - - - """ - def __init__(self): self.content_type = 'text/xml' @@ -486,25 +494,90 @@ class PingUpdateResponse(StringUpdateResponse): PingUpdateResponse.file_sha256 = filesha256 PingUpdateResponse.file_size = filesize + def GetCommonResponseValues(self): + """Returns a dictionary of default values that'll be substituted in the + response irrespective of the protocol version.""" + response_values = {} + response_values['appid'] = '87efface-864d-49a5-9bb3-4b050a7c227a' + response_values['time_elapsed'] = self.SecondsSinceMidnight() + return response_values + + def GetSubstitutedResponse(self, response_template, response_values): + """Substitutes the response template with response_values. + + Args: + response_dict: Canned response template + response_values: Values to be substituted in the canned template. + Returns: + Xml string to be passed back to client. + Raises: + AutoupdateError if required response values are not present. + """ + try: + response_xml = response_template % response_values + return response_xml + except KeyError as e: + self.Fatal('Missing response value: %s' % e) + + def GetUpdateResponse(self, sha1, sha256, size, url, is_delta_format): + """Returns a response to the client corresponding to a new update. + + Args: + sha1: SHA1 hash of update blob + sha256: SHA256 hash of update blob + size: size of update blob + url: where to find update blob + is_delta_format: true if url refers to a delta payload + Returns: + Xml string to be passed back to client. + """ + response_values = self.GetCommonResponseValues() + response_values['sha1'] = sha1 + response_values['sha256'] = sha256 + response_values['size'] = size + response_values['url'] = url + (codebase, filename) = os.path.split(url) + response_values['codebase'] = codebase + response_values['filename'] = filename + response_values['is_delta_format'] = is_delta_format + response_values['extra_attr'] = '' + response_xml = self.GetSubstitutedResponse(UPDATE_RESPONSE, + response_values) + return response_xml + + def GetNoUpdateResponse(self): + """Returns a response to the client corresponding to no update.""" + response_values = self.GetCommonResponseValues() + response_xml = self.GetSubstitutedResponse(NO_UPDATE_RESPONSE, + response_values) + return response_xml + + def Reply(self, handler, send_content=True, post_data=None): """Return (using StringResponse) an XML reply to ForcedUpdate clients.""" if not post_data: return UpdateResponse.Reply(self, handler) - request_version = (minidom.parseString(post_data).firstChild. - getElementsByTagName('o:app')[0]. - getAttribute('version')) + root = minidom.parseString(post_data) + protocol = root.firstChild.getAttribute('protocol') + if (protocol != '3.0'): + raise BaseException('You first need to update your device to a build ' + 'that uses Omaha v3 protocol for the update_engine.') + + app = root.firstChild.getElementsByTagName('app')[0] + request_version = app.getAttribute('version') if request_version == 'ForcedUpdate': host, pdict = cgi.parse_header(handler.headers.getheader('Host')) - self.string = (self.payload_success_template % - (self.xmlns, self.SecondsSinceMidnight(), - self.app_id, 'http://%s/%s' % (host, UPDATE_FILENAME), - self.file_hash, self.file_sha256, self.file_size)) + url = 'http://%s/%s' % (host, UPDATE_FILENAME) + self.string = self.GetUpdateResponse(self.file_hash, + self.file_sha256, + self.file_size, + url, + False) # is_delta_format else: - self.string = (self.payload_failure_template % - (self.xmlns, self.SecondsSinceMidnight(), self.app_id)) + self.string = (self.GetNoUpdateResponse()) StringUpdateResponse.Reply(self, handler, send_content)