def test_constructor3(self): '''! Test when we call the constructor function with incomplete or wrong parameters ''' dl = [('identifier', 'product-name')] d = dict(dl) dl_url = [('source', d)] d_url = dict(dl_url) durl = DynamicURL(d_url, 'abc') with pytest.raises(TypeError): durl = DynamicURL(d_url, [12])
def test_constructor2(self): '''! Test when we call the constructor function with incomplete or wrong parameters ''' dl = [('identifier', 'product-name')] d = dict(dl) dl_url = [('source', d)] d_url = dict(dl_url) durl = DynamicURL(d_url) s = durl.getSource() assert (s == sysEeprom.get_product_name())
def test_constructor1(self): '''! Test when we call the constructor function with incomplete or wrong parameters ''' with pytest.raises(TypeError): durl = DynamicURL() dl_url = [('source', 'http://localhost:2000/test.txt'), ('destination', 'abc')] d_url = dict(dl_url) with pytest.raises(TypeError): durl = DynamicURL(d_url)
def test_download1(self): '''! Test when we call download() method ''' dl = [('identifier', 'product-name'), ('prefix', 'XYZ_')] d = dict(dl) dl_url = [('source', d)] d_url = dict(dl_url) durl = DynamicURL(d_url) s = durl.getSource() assert (s == 'XYZ_' + sysEeprom.get_product_name()) durl.download()
def test_constructor7(self): '''! Test when we call the constructor function with incomplete or wrong parameters ''' dl = [('identifier', dict([('url', None)]))] d = dict(dl) dl_url = [('source', d)] d_url = dict(dl_url) with pytest.raises(ValueError): durl = DynamicURL(d_url)
def test_download2(self): '''! Test when we call download() method ''' content = 'Hello the world!' self.__write_file("/tmp/test_firmware_" + sysEeprom.get_product_name(), content) dl = [('identifier', 'product-name'), ('prefix', 'file:///tmp/test_firmware_')] d = dict(dl) dl_url = [('source', d)] d_url = dict(dl_url) durl = DynamicURL(d_url) s = durl.getSource() assert (s == 'file:///tmp/test_firmware_' + sysEeprom.get_product_name()) rc, dest = durl.download() assert (rc == 0) assert (self.__read_file(dest) == content)
def __init__(self, json_src_file=None, json_dst_file=None): '''! Constructor for ZTPJson class. As part of the object instantiation, ZTP JSON file is read and split into individual configuration sections. The contents of ZTP JSON are saved as a dictionary to be used. Missing objects are added assuming default values. A list of configuration sections defined in the ZTP JSON is collected. @param json_src_file (str, optional) Configuration section input.json file to be prcoessed. If not specified, /etc/sonic/ztp_data.json file is used. @param json_dst_file (str, optional) Destination file to which processed JSON data is saved to. If not specified, json_src_file is used as destination file. @exception Raise ValueError if any error or exception encountered while processing the json_src_file ''' # Call base class constructor ConfigSection.__init__(self, json_src_file, json_dst_file) # Raise exception if top level ztp section is not found self.ztpDict = self.jsonDict.get('ztp') if self.ztpDict is None or isinstance(self.ztpDict, dict) is False: raise ValueError('ztp section not found in JSON data') # Read ZTP JSON version if self.ztpDict.get('ztp-json-version') is None: self.ztpDict['ztp-json-version'] = getCfg('ztp-json-version', 'Not Available') # Check for presence of url or dynamic-url and download the actual json content reloadZTPJson = False try: if self.ztpDict.get('dynamic-url'): dyn_url_data = self.ztpDict.get('dynamic-url') if isinstance(dyn_url_data, dict): dyn_url_data['destination'] = self.json_dst_file objDynamicURL = DynamicURL(dyn_url_data, self.json_dst_file) rc, filename = objDynamicURL.download() if rc == 0: reloadZTPJson = True else: raise ValueError( 'url section provided in ztp section could not be resolved' ) elif self.ztpDict.get('url'): url_data = self.ztpDict.get('url') if isinstance(url_data, dict): url_data['destination'] = self.json_dst_file objURL = URL(url_data, self.json_dst_file) rc, filename = objURL.download() if rc == 0: reloadZTPJson = True else: raise ValueError( 'dynamic-url section provided in ztp section could not be resolved' ) except: raise ValueError( 'url/dynamic-url sections provided in ztp section could not be interpreted' ) if reloadZTPJson is True and os.path.isfile(self.json_dst_file): ConfigSection.__init__(self, self.json_dst_file, self.json_dst_file) self.ztpDict = self.jsonDict.get('ztp') if self.ztpDict is None or isinstance(self.ztpDict, dict) is False: raise ValueError('ztp section not found in JSON data') # Read ZTP JSON version if self.ztpDict.get('ztp-json-version') is None: self.ztpDict['ztp-json-version'] = getCfg( 'ztp-json-version', 'Not Available') # Insert default values self.__buildDefaults() # Cleanup previous ZTP run files if self.ztpDict['status'] == 'BOOT': self.__cleanup() # Identify valid configuration sections self.section_names = sorted(self.ztpDict.keys()) for k, v in self.ztpDict.items(): # Remove leaf objects as they are not configuration sections if isinstance(v, dict) is False: self.section_names.remove(k) continue # Insert default values in configuration sections self._ConfigSection__buildDefaults(v) # Split confguration sections to individual files self.__writeConfigSections(k, v) # Write ZTP JSON data to file self.objJson.writeJson() # Update the shadow ZTP JSON file with new information self.__writeShadowJSON()
def plugin(self, section_name): '''! Resolve the plugin used to process a configuration section. If the plugin is specified as a url object, the plugin is downloaded. @param section_name (str) Configuration section name whose plugin needs to be resolved. @return If plugin is resolved using configuration section data: \n Expanded file path to plugin file used to process configuration section. \n If plugin is not found or error encountered: \n None ''' if isString(section_name) is False: raise TypeError('Invalid argument used as section name') elif self.ztpDict.get(section_name) is None: logger.error('Configuration Section %s not found.' % section_name) return None plugin_data = self.ztpDict.get(section_name).get('plugin') name = None if plugin_data is not None and isinstance(plugin_data, dict): logger.debug( 'User defined plugin detected for configuration section %s.' % section_name) plugin_file = getCfg( 'ztp-tmp-persistent') + '/' + section_name + '/' + 'plugin' try: # Re-use the plugin if already present if os.path.isfile(plugin_file) is True: return plugin_file if plugin_data.get('dynamic-url'): dyn_url_data = plugin_data.get('dynamic-url') if isinstance(dyn_url_data, dict) and dyn_url_data.get( 'destination') is not None: objDynUrl = DynamicURL(dyn_url_data) else: objDynUrl = DynamicURL(dyn_url_data, plugin_file) rc, plugin_file = objDynUrl.download() return plugin_file elif plugin_data.get('url'): url_data = plugin_data.get('url') if isinstance( url_data, dict) and url_data.get('destination') is not None: objUrl = URL(url_data) else: objUrl = URL(url_data, plugin_file) updateActivity( 'Downloading plugin \'%s\' for configuration section %s' % (objUrl.getSource(), section_name)) rc, plugin_file = objUrl.download() if rc != 0: logger.error( 'Failed to download plugin \'%s\' for configuration section %s.' % (objUrl.getSource(), section_name)) return plugin_file elif plugin_data.get('name') is not None: name = plugin_data.get('name') except (TypeError, ValueError, OSError, IOError) as e: logger.error( 'Exception [%s] encountered while determining plugin for configuration section %s.' % (str(e), section_name)) return None elif plugin_data is not None and isString(plugin_data): name = plugin_data elif plugin_data is not None: logger.error( 'Invalid plugin data type used for configuration section %s.' % section_name) return None # plugin name is not provided in section data, use section name as plugin name if name is None: res = re.split("^[0-9]+-", section_name, maxsplit=1) if len(res) > 1: name = res[1] else: name = res[0] logger.debug( 'ZTP provided plugin %s is being used for configuration section %s.' % (name, section_name)) if os.path.isfile(getCfg('plugins-dir') + '/' + name) is True: return getCfg('plugins-dir') + '/' + name return None