• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2021 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""
17Description : Create script file for updater
18"""
19
20import os
21import re
22import tempfile
23from decimal import getcontext
24from decimal import Decimal
25
26from log_exception import VendorExpandError
27from log_exception import UPDATE_LOGGER
28from utils import OPTIONS_MANAGER
29from utils import IMAGE_FILE_MOUNT_TO_PARTITION_DICT
30from utils import PARTITION_FILE
31from utils import TWO_STEP
32from utils import TOTAL_SCRIPT_FILE_NAME
33from utils import SCRIPT_FILE_NAME
34from utils import SCRIPT_KEY_LIST
35
36
37class Script:
38
39    def __init__(self):
40        self.script = []
41        self.version = 0
42        self.info = {}
43
44    def add_command(self, cmd=None):
45        """
46        Add command content to the script.
47        :param cmd: command content
48        :return:
49        """
50        self.script.append(cmd)
51
52    def get_script(self):
53        """
54        Get the script list.
55        :return: script list
56        """
57        return self.script
58
59    def sha_check(self, *args, **kwargs):
60        raise VendorExpandError(type(self), 'sha_check')
61
62    def first_block_check(self, *args, **kwargs):
63        raise VendorExpandError(type(self), 'first_block_check')
64
65    def abort(self, *args, **kwargs):
66        raise VendorExpandError(type(self), 'abort')
67
68    def show_progress(self, *args, **kwargs):
69        raise VendorExpandError(type(self), 'show_progress')
70
71    def block_update(self, *args, **kwargs):
72        raise VendorExpandError(type(self), 'block_update')
73
74    def sparse_image_write(self, *args, **kwargs):
75        raise VendorExpandError(type(self), 'sparse_image_write')
76
77    def raw_image_write(self, *args, **kwargs):
78        raise VendorExpandError(type(self), 'raw_image_write')
79
80    def get_status(self, *args, **kwargs):
81        raise VendorExpandError(type(self), 'get_status')
82
83    def set_status(self, *args, **kwargs):
84        raise VendorExpandError(type(self), 'set_status')
85
86    def reboot_now(self, *args, **kwargs):
87        raise VendorExpandError(type(self), 'reboot_now')
88
89    def updater_partitions(self, *args, **kwargs):
90        raise VendorExpandError(type(self), 'updater_partitions')
91
92
93class PreludeScript(Script):
94    def __init__(self):
95        super().__init__()
96
97
98class VerseScript(Script):
99    def __init__(self):
100        super().__init__()
101
102    def sha_check(self, ranges_str, expected_sha, partition):
103        """
104        Get the sha_check command.
105        :param ranges_str: ranges string
106        :param expected_sha: hash value
107        :param partition: image name
108        :return:
109        """
110        cmd = ('sha_check("/{partition}", "{ranges_str}", '
111               '"{expected_sha}")').format(
112            ranges_str=ranges_str,
113            expected_sha=expected_sha, partition=partition)
114        return cmd
115
116    def first_block_check(self, partition):
117        """
118        Get the first_block_check command.
119        :param partition: image name
120        :return:
121        """
122        cmd = 'first_block_check("/{partition}")'.format(
123            partition=partition)
124        return cmd
125
126    def abort(self, partition):
127        """
128        Get the abort command.
129        :param partition: image name
130        :return:
131        """
132        cmd = 'abort("ERROR: {partition} partition ' \
133              'fails to incremental check!");\n'.format(
134                partition=partition)
135        return cmd
136
137    def show_progress(self, start_progress, dur):
138        """
139        Get the show_progress command.
140        'dur' may be zero to advance the progress via SetProgress
141        :param start_progress: start progress
142        :param dur: seconds
143        :return:
144        """
145        cmd = 'show_progress({start_progress}, {dur});\n'.format(
146            start_progress=start_progress, dur=dur)
147        return cmd
148
149    def block_update(self, partition):
150        """
151        Get the block_update command.
152        :param partition:  image name
153        :return:
154        """
155        cmd = 'block_update("/{partition}", ' \
156              '"{partition}.transfer.list", "{partition}.new.dat", ' \
157              '"{partition}.patch.dat");\n'.format(partition=partition)
158        return cmd
159
160    def sparse_image_write(self, partition):
161        """
162        Get the sparse_image_write command.
163        :param partition:  image name
164        :return:
165        """
166        cmd = 'sparse_image_write("/%s");\n' % partition
167        return cmd
168
169    def raw_image_write(self, partition, image_file_name):
170        """
171        Get the raw_image_write command.
172        :param partition:  image name
173        :param image_file_name:  image file name
174        :return:
175        """
176        if partition in IMAGE_FILE_MOUNT_TO_PARTITION_DICT.keys():
177            partition = IMAGE_FILE_MOUNT_TO_PARTITION_DICT.get(partition)
178        cmd = 'raw_image_write("/%s", "/%s");\n' % (partition, image_file_name)
179        return cmd
180
181
182    def get_status(self):
183        """
184        Get the get_status command.
185        :return:
186        """
187        cmd = 'get_status("/misc")'
188        return cmd
189
190    def set_status(self, status_value):
191        """
192        Get the set_status command.
193        :param status_value: status value to be set
194        :return:
195        """
196        cmd = 'set_status("/misc", %s);\n' % status_value
197        return cmd
198
199    def reboot_now(self):
200        """
201        Get the reboot_now command.
202        :return:
203        """
204        cmd = 'reboot_now();\n'
205        return cmd
206
207    def updater_partitions(self):
208        """
209        Get the updater_partitions command.
210        :return:
211        """
212        cmd = 'update_partitions("/%s");\n' % PARTITION_FILE
213        return cmd
214
215
216class RefrainScript(Script):
217    def __init__(self):
218        super().__init__()
219
220
221class EndingScript(Script):
222    def __init__(self):
223        super().__init__()
224
225
226def write_script(script_content, opera_name):
227    """
228    Generate the {opera}script.
229    :param script_content: script content
230    :param opera_name: Opera phase names corresponding to the script content
231                    'prelude', 'verse', 'refrain', and 'ending'.
232    :return:
233    """
234    script_file = tempfile.NamedTemporaryFile(mode='w+')
235    script_file.write(script_content)
236    script_file.seek(0)
237    script_file_name = ''.join([opera_name.title(), SCRIPT_FILE_NAME])
238    OPTIONS_MANAGER.opera_script_file_name_dict[opera_name].\
239        append((script_file_name, script_file))
240    UPDATE_LOGGER.print_log("%s generation complete!" % script_file_name)
241
242
243def generate_total_script():
244    """
245    Generate the overall script.
246    """
247    content_list = []
248    for each_key, each_value in \
249            OPTIONS_MANAGER.opera_script_file_name_dict.items():
250        for each in each_value:
251            each_content = "LoadScript(\"%s\", %s);" % \
252                           (each[0], SCRIPT_KEY_LIST.index(each_key))
253            content_list.append(each_content)
254    script_total = tempfile.NamedTemporaryFile(mode='w+')
255    script_total.write('\n'.join(content_list))
256    script_total.seek(0)
257    OPTIONS_MANAGER.total_script_file_obj = script_total
258    UPDATE_LOGGER.print_log("%s generation complete!" % TOTAL_SCRIPT_FILE_NAME)
259
260
261def get_progress_value(distributable_value=100):
262    """
263    Allocate a progress value to each image update.
264    :param distributable_value: distributable value
265    :return:
266    """
267    progress_value_dict = {}
268    full_img_list = OPTIONS_MANAGER.full_img_list
269    incremental_img_list = OPTIONS_MANAGER.incremental_img_list
270    file_size_list = []
271    if len(full_img_list) == 0 and len(incremental_img_list) == 0:
272        UPDATE_LOGGER.print_log(
273            "get progress value failed! > getting progress value failed!",
274            UPDATE_LOGGER.ERROR_LOG)
275        return False
276    for idx, _ in enumerate(incremental_img_list):
277        # Obtain the size of the incremental image file.
278        if OPTIONS_MANAGER.two_step and incremental_img_list[idx] == TWO_STEP:
279            # Updater images are not involved in progress calculation.
280            incremental_img_list.remove(TWO_STEP)
281            continue
282        file_obj = OPTIONS_MANAGER.incremental_image_file_obj_list[idx]
283        each_img_size = os.path.getsize(file_obj.name)
284        file_size_list.append(each_img_size)
285
286    for idx, _ in enumerate(full_img_list):
287        # Obtain the size of the full image file.
288        if OPTIONS_MANAGER.two_step and full_img_list[idx] == TWO_STEP:
289            # Updater images are not involved in progress calculation.
290            continue
291        file_obj = OPTIONS_MANAGER.full_image_file_obj_list[idx]
292        each_img_size = os.path.getsize(file_obj.name)
293        file_size_list.append(each_img_size)
294    if OPTIONS_MANAGER.two_step and TWO_STEP in full_img_list:
295        full_img_list.remove(TWO_STEP)
296
297    proportion_value_list = get_proportion_value_list(
298        file_size_list, distributable_value=distributable_value)
299
300    adjusted_proportion_value_list = adjust_proportion_value_list(
301        proportion_value_list, distributable_value)
302
303    all_img_list = incremental_img_list + full_img_list
304    current_progress = 40
305    for idx, each_img in enumerate(all_img_list):
306        temp_progress = current_progress + adjusted_proportion_value_list[idx]
307        progress_value_dict[each_img] = (current_progress, temp_progress)
308        current_progress = temp_progress
309    return progress_value_dict
310
311
312def get_proportion_value_list(file_size_list, distributable_value=100):
313    """
314    Obtain the calculated progress proportion value list
315    (proportion_value_list).
316    :param file_size_list: file size list
317    :param distributable_value: distributable value
318    :return proportion_value_list: progress proportion value list
319    """
320    sum_size = sum(file_size_list)
321    getcontext().prec = 2
322    proportion_value_list = []
323    for each_size_value in file_size_list:
324        proportion = Decimal(str(float(each_size_value))) / Decimal(
325            str(float(sum_size)))
326        proportion_value = int(
327            Decimal(str(proportion)) *
328            Decimal(str(float(distributable_value))))
329        if proportion_value == 0:
330            proportion_value = 1
331        proportion_value_list.append(proportion_value)
332    return proportion_value_list
333
334
335def adjust_proportion_value_list(proportion_value_list, distributable_value):
336    """
337    Adjust the calculated progress proportion value list to ensure that
338    sum is equal to distributable_value.
339    :param proportion_value_list: calculated progress proportion value list
340    :param distributable_value: number of distributable progress values
341    :return proportion_value_list: new progress proportion value list
342    """
343    if len(proportion_value_list) == 0:
344        return []
345    sum_proportion_value = sum(proportion_value_list)
346    if sum_proportion_value > distributable_value:
347        max_value = max(proportion_value_list)
348        max_idx = proportion_value_list.index(max_value)
349        proportion_value_list[max_idx] = \
350            max_value - (sum_proportion_value - distributable_value)
351    elif sum_proportion_value < distributable_value:
352        min_value = min(proportion_value_list)
353        min_idx = proportion_value_list.index(min_value)
354        proportion_value_list[min_idx] = \
355            min_value + (distributable_value - sum_proportion_value)
356    return proportion_value_list
357
358
359def create_script(prelude_script, verse_script,
360                  refrain_script, ending_script):
361    """
362    Generate the script file.
363    :param prelude_script: prelude script
364    :param verse_script: verse script
365    :param refrain_script: refrain script
366    :param ending_script: ending script
367    :return:
368    """
369    # Generate the prelude script.
370    prelude_script.add_command("\n# ---- prelude ----\n")
371
372    # Get the distribution progress.
373    progress_value_dict = get_progress_value()
374    if progress_value_dict is False:
375        return False
376    verse_script_content_list = verse_script.get_script()
377    updater_content = []
378    if OPTIONS_MANAGER.two_step:
379        for idx, each_cmd in enumerate(verse_script_content_list[1:]):
380            if "/%s" % TWO_STEP in each_cmd:
381                updater_content.append(each_cmd)
382                each_cmd = \
383                    '\n'.join(
384                        ['    %s' % each for each in each_cmd.split('\n')])
385                verse_script_content_list[0] = \
386                    verse_script_content_list[0].replace(
387                        "UPDATER_WRITE_FLAG",
388                        "%s\nUPDATER_WRITE_FLAG" % each_cmd)
389        verse_script_content_list[0] = \
390            verse_script_content_list[0].replace("UPDATER_WRITE_FLAG", "")
391        verse_script_content_list[0] = \
392            verse_script_content_list[0].replace("updater_WRITE_FLAG", "")
393        for each in updater_content:
394            verse_script_content_list.remove(each)
395        verse_script_content = '\n'.join(verse_script_content_list[1:])
396    else:
397        verse_script_content = '\n'.join(verse_script_content_list)
398
399    for key, value in progress_value_dict.items():
400        show_progress_content = \
401            verse_script.show_progress((value[1] - value[0]) / 100, 0)
402        verse_script_content = \
403            re.sub(r'%s_WRITE_FLAG' % key, '%s' % show_progress_content,
404                   verse_script_content, count=1)
405    if OPTIONS_MANAGER.two_step:
406        verse_script_content = '\n'.join(
407            ['    %s' % each for each in verse_script_content.split('\n')])
408        verse_script_content = verse_script_content_list[0].replace(
409            "ALL_WRITE_FLAG", verse_script_content)
410    # Generate the verse script.
411    write_script(verse_script_content, 'verse')
412    # Generate the refrain script.
413    refrain_script.add_command("\n# ---- refrain ----\n")
414    # Generate the ending script.
415    ending_script.add_command("\n# ---- ending ----\n")
416
417    generate_total_script()
418