def test_fgip_raise(self, mock_get): """Test FedoraGeoIPProvider handling of exceptions""" task = GeolocationTask() mock_get.side_effect = RequestException with self.assertLogs(level="DEBUG") as logs: result = task._locate(GEOLOC_URL_FEDORA_GEOIP) assert isinstance(result, GeolocationData) assert result.is_empty() mock_get.assert_called_once() assert "RequestException" in "\n".join(logs.output) mock_get.reset_mock() # This is technically cheating, ValueError is expected to be raised elsewhere than the # request itself. But it's all wrapped in a single try....except block so it is a good # enough approximation. mock_get.side_effect = ValueError with self.assertLogs(level="DEBUG") as logs: result = task._locate(GEOLOC_URL_FEDORA_GEOIP) assert isinstance(result, GeolocationData) assert result.is_empty() mock_get.assert_called_once() assert "Unable to decode" in "\n".join(logs.output)
def test_fgip_emptydata(self, mock_get): """Test FedoraGeoIPProvider with empty data""" task = GeolocationTask() result = task._locate(GEOLOC_URL_FEDORA_GEOIP) assert isinstance(result, GeolocationData) assert result.is_empty() mock_get.assert_called_once()
def test_fgip_failure(self, mock_get): """Test FedoraGeoIPProvider with HTTP failure""" task = GeolocationTask() with self.assertLogs(level="DEBUG") as logs: result = task._locate(GEOLOC_URL_FEDORA_GEOIP) assert isinstance(result, GeolocationData) assert result.is_empty() mock_get.assert_called_once() assert "failed with status code: 503" in "\n".join(logs.output)
def test_hip_success(self, mock_get): """Test HostipGeoIPProvider success""" task = GeolocationTask() result = task._locate(GEOLOC_URL_HOSTIP) assert isinstance(result, GeolocationData) assert result.territory == "GB" assert result.timezone == "Europe/London" assert not result.is_empty() mock_get.assert_called_once()
def test_fgip_bad_timezone(self, mock_get): """Test FedoraGeoIPProvider with bad time zone data""" task = GeolocationTask() result = task._locate(GEOLOC_URL_FEDORA_GEOIP) assert isinstance(result, GeolocationData) assert result.territory == "GB" assert result.timezone == "Europe/London" assert not result.is_empty() mock_get.assert_called_once()
def start_geolocation(self): """Start geolocation, if not already started.""" if self._geoloc_task is not None: log.info("Geoloc: already started") return self._geoloc_task = GeolocationTask() self._geoloc_task.succeeded_signal.connect( self._set_geolocation_result) log.info("Geoloc: starting lookup") self._geoloc_task.start()
def test_no_network(self, time_mock, avail_mock, net_mock): """Test waiting for network when no network available""" time_mock.sleep = Mock() time_mock.perf_counter = Mock(side_effect=[0, 1, 2, 3, 4, 65536]) # fake time out net_mock.get_proxy = Mock(return_value=MockNetworkProxy( connect_results=[False])) task = GeolocationTask() result = task._wait_for_network() assert result is False time_mock.sleep.assert_not_called() avail_mock.assert_called_once_with(net_mock) assert net_mock.get_proxy.return_value.call_count == 0
def test_immediate_success(self, time_mock, avail_mock, net_mock): """Test waiting for network when already connected""" time_mock.sleep = Mock() time_mock.perf_counter = Mock() net_mock.get_proxy = Mock(return_value=MockNetworkProxy( connect_results=[True])) task = GeolocationTask() result = task._wait_for_network() assert result is True time_mock.sleep.assert_not_called() time_mock.perf_counter.assert_not_called() avail_mock.assert_called_once_with(net_mock) assert net_mock.get_proxy.return_value.call_count == 1
def test_success_after_waiting(self, time_mock, avail_mock, net_mock): """Test waiting for network when network becomes available at the very last moment""" time_mock.sleep = Mock() time_mock.perf_counter = Mock(side_effect=[0, 0, 1, 2, 3, 4, 65536]) # fake time out net_mock.get_proxy = Mock(return_value=MockNetworkProxy( connect_results=[False] + [False] * 5 + [True] # 1 before, 5 iterations, 1 timeout interation, last exit )) task = GeolocationTask() result = task._wait_for_network() assert result is True assert time_mock.sleep.call_count == 5 avail_mock.assert_called_once_with(net_mock) assert net_mock.get_proxy.return_value.call_count == 7 # same as number of results
def test_no_result(self, conf_mock): """Test GeolocationTask with no viable result""" conf_mock.timezone.geolocation_provider = GEOLOC_URL_FEDORA_GEOIP retval = GeolocationData() # empty by default with patch.object(GeolocationTask, "_wait_for_network", return_value=True) as wfn_mock: with patch.object(GeolocationTask, "_locate", return_value=retval) as loc_mock: task = GeolocationTask() result = task.run() assert isinstance(result, GeolocationData) assert result.is_empty() wfn_mock.assert_called_once_with() loc_mock.assert_called_once()
def test_success(self, conf_mock): """Test success case for GeolocationTask""" conf_mock.timezone.geolocation_provider = GEOLOC_URL_FEDORA_GEOIP retval = GeolocationData.from_values(territory="territory", timezone="timezone") with patch.object(GeolocationTask, "_wait_for_network", return_value=True) as wfn_mock: with patch.object(GeolocationTask, "_locate", return_value=retval) as loc_mock: task = GeolocationTask() result = task.run() assert isinstance(result, GeolocationData) assert result.timezone == retval.timezone assert result.territory == retval.territory wfn_mock.assert_called_once_with() loc_mock.assert_called_once()
def test_success_while_waiting(self, time_mock, avail_mock, net_mock): """Test waiting for network when network becomes available""" time_mock.sleep = Mock() time_mock.perf_counter = Mock( side_effect=[0, 0, 1, 2]) # init + 3 iterations only net_mock.get_proxy = Mock(return_value=MockNetworkProxy( connect_results=[ False, False, False, True # 1 before, 2 iterations keep going, 3rd succeeds ])) task = GeolocationTask() result = task._wait_for_network() assert result is True assert time_mock.sleep.call_count == 3 avail_mock.assert_called_once_with(net_mock) assert net_mock.get_proxy.return_value.call_count == 4 # same as number of results
def test_no_network(self, conf_mock): """Test GeolocationTask with no network access""" conf_mock.timezone.geolocation_provider = GEOLOC_URL_FEDORA_GEOIP retval = GeolocationData.from_values(territory="territory", timezone="timezone") with self.assertLogs(level="DEBUG") as logs: with patch.object(GeolocationTask, "_wait_for_network", return_value=False) as wfn_mock: with patch.object(GeolocationTask, "_locate", return_value=retval) as loc_mock: task = GeolocationTask() result = task.run() assert isinstance(result, GeolocationData) assert result.is_empty() wfn_mock.assert_called_once_with() loc_mock.assert_not_called() assert "no network connection" in "\n".join(logs.output)
class TimezoneService(KickstartService): """The Timezone service.""" def __init__(self): super().__init__() self.timezone_changed = Signal() self._timezone = "America/New_York" self._geoloc_task = None self.geolocation_result_changed = Signal() self._geoloc_result = GeolocationData() self.is_utc_changed = Signal() self._is_utc = False self.ntp_enabled_changed = Signal() self._ntp_enabled = True self.time_sources_changed = Signal() self._time_sources = [] def publish(self): """Publish the module.""" TaskContainer.set_namespace(TIMEZONE.namespace) DBus.publish_object(TIMEZONE.object_path, TimezoneInterface(self)) DBus.register_service(TIMEZONE.service_name) @property def kickstart_specification(self): """Return the kickstart specification.""" return TimezoneKickstartSpecification def process_kickstart(self, data): """Process the kickstart data.""" self.set_timezone(data.timezone.timezone) self.set_is_utc(data.timezone.isUtc) self.set_ntp_enabled(not data.timezone.nontp) sources = [] for hostname in data.timezone.ntpservers: source = TimeSourceData() source.type = TIME_SOURCE_SERVER source.hostname = hostname source.options = ["iburst"] sources.append(source) for source_data in data.timesource.dataList(): if source_data.ntp_disable: self.set_ntp_enabled(False) continue source = TimeSourceData() source.options = ["iburst"] if source_data.ntp_server: source.type = TIME_SOURCE_SERVER source.hostname = source_data.ntp_server elif source_data.ntp_pool: source.type = TIME_SOURCE_POOL source.hostname = source_data.ntp_pool else: KickstartParseError(_("Invalid time source."), lineno=source_data.lineno) if source_data.nts: source.options.append("nts") sources.append(source) self.set_time_sources(sources) def setup_kickstart(self, data): """Set up the kickstart data.""" data.timezone.timezone = self.timezone data.timezone.isUtc = self.is_utc source_data_list = data.timesource.dataList() if not self.ntp_enabled: source_data = data.TimesourceData() source_data.ntp_disable = True source_data_list.append(source_data) return for source in self.time_sources: source_data = data.TimesourceData() if source.type == TIME_SOURCE_SERVER: source_data.ntp_server = source.hostname elif source.type == TIME_SOURCE_POOL: source_data.ntp_pool = source.hostname else: log.warning("Skipping %s.", source) continue if "nts" in source.options: source_data.nts = True source_data_list.append(source_data) @property def timezone(self): """Return the timezone.""" return self._timezone def set_timezone(self, timezone): """Set the timezone.""" self._timezone = timezone self.timezone_changed.emit() log.debug("Timezone is set to %s.", timezone) @property def is_utc(self): """Is the hardware clock set to UTC?""" return self._is_utc def set_is_utc(self, is_utc): """Set if the hardware clock is set to UTC.""" self._is_utc = is_utc self.is_utc_changed.emit() log.debug("UTC is set to %s.", is_utc) @property def ntp_enabled(self): """Enable automatic starting of NTP service.""" return self._ntp_enabled def set_ntp_enabled(self, ntp_enabled): """Enable or disable automatic starting of NTP service.""" self._ntp_enabled = ntp_enabled self.ntp_enabled_changed.emit() log.debug("NTP is set to %s.", ntp_enabled) @property def time_sources(self): """Return a list of time sources.""" return self._time_sources def set_time_sources(self, servers): """Set time sources.""" self._time_sources = list(servers) self.time_sources_changed.emit() log.debug("Time sources are set to: %s", servers) def collect_requirements(self): """Return installation requirements for this module. :return: a list of requirements """ requirements = [] # Add ntp service requirements. if self._ntp_enabled: requirements.append( Requirement.for_package(NTP_PACKAGE, reason="Needed to run NTP service.")) return requirements def install_with_tasks(self): """Return the installation tasks of this module. :return: list of installation tasks """ return [ ConfigureHardwareClockTask(is_utc=self.is_utc), ConfigureTimezoneTask(sysroot=conf.target.system_root, timezone=self.timezone, is_utc=self.is_utc), ConfigureNTPTask(sysroot=conf.target.system_root, ntp_enabled=self.ntp_enabled, ntp_servers=self.time_sources) ] def _set_geolocation_result(self): """Set geolocation result when the task finished.""" self._geoloc_result = self._geoloc_task.get_result() self.geolocation_result_changed.emit() self._geoloc_task = None log.debug("Geolocation result is set, valid=%s", not self._geoloc_result.is_empty()) def start_geolocation(self): """Start geolocation, if not already started.""" if self._geoloc_task is not None: log.info("Geoloc: already started") return self._geoloc_task = GeolocationTask() self._geoloc_task.succeeded_signal.connect( self._set_geolocation_result) log.info("Geoloc: starting lookup") self._geoloc_task.start() @property def geolocation_result(self): """Get geolocation result. :return GeolocationData: result of the lookup, empty if not ready yet """ return self._geoloc_result