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