Merge lp:~blake-rouse/maas/osystem-preseed-cleanup into lp:~maas-committers/maas/trunk

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 2385
Proposed branch: lp:~blake-rouse/maas/osystem-preseed-cleanup
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 295 lines (+60/-53)
8 files modified
src/maasserver/compose_preseed.py (+8/-0)
src/maasserver/enum.py (+0/-33)
src/maasserver/models/bootsourceselection.py (+10/-6)
src/maasserver/testing/factory.py (+3/-2)
src/maasserver/tests/test_api_boot_source_selections.py (+7/-7)
src/maasserver/tests/test_compose_preseed.py (+15/-0)
src/maasserver/tests/test_forms.py (+5/-5)
src/provisioningserver/driver/__init__.py (+12/-0)
To merge this branch: bzr merge lp:~blake-rouse/maas/osystem-preseed-cleanup
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+221440@code.launchpad.net

This proposal supersedes a proposal from 2014-04-24.

Commit message

Removed DISTRO_SERIES enums. Added the ability for an operating system to compose its own preseed.

Description of the change

This is the final change in the series of changes to allow MAAS to deploy other operating systems. As we have Windows and CentOS support coming soon this is needed to easily add new operating systems.

Removed the DISTRO_SERIES enums as they are no longer needed, all information comes from the OperatingSystemRegistry.

Added the ability for an operating system to compose its own preseed. This will be used for Windows, CentOS and other operating systems.

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote : Posted in a previous version of this proposal

Thanks for doing this work. It's good to see those old hard-coded enums go at last. Unfortunately there's that pervasive problem of the registries living in a different process, possibly on a different machine. We should probably have been a lot clearer about that.

This branch answers a question I asked on one of your other merge proposals: does OperatingSystem really need to be a base class? If it's going to have a method for generating preseed data, and if it really can't be addressed with simple text templating, then it probably does.

An uncomfortable number of dimensional axes are coming together in compose_preseed: commissioning vs. deployment, operating systems with and without preseed code, conventional installer or fastpath (or whatever install options some other OS might have in their place). Gotta nip that sort of burgeoning complexity in the bud, or it will eventually stifle further development. Fight it with fire because by nature it will just keep growing.

So, assuming that we really do need OS-specific preseed-generating code, I would look for a way to move the Ubuntu-specific parts into the Ubuntu OperatingSystem. Or if most-but-not-all operating systems can generate their preseeds using templates, delegate most operating systems' implementations to a single template-based helper. (If templates can reasonably do the job for all operating systems we have in mind, of course, just use templating and stick with generic code.)

The hasattr check is best avoided, I think. If you're going to rely on dynamic dispatch for this, go all the way. You can accommodate some kind of sensible default in the dynamic method's definition, but an unconditional code path is going to be more manageable than special-casing in the code. Conditionals are our profession's equivalent of moving parts: they introduce complexity and risk.

There also seems to be a tacit rule in there that the commissioning OS must be, or work like, Ubuntu. That is worth being very explicit about. This isn't just a series of "if" statements to make things work, it's defining how a node can or cannot be installed. The details of of how these conditions interact are crucial, and yet I don't see any tests covering things like “we generate the customary Ubuntu preseed for a commissioning node, even if it's to be deployed with a different OS.”

By the way, I do appreciate how this branch is small and focused and not thrown in with a large bag of other changes!

review: Needs Fixing
Revision history for this message
Blake Rouse (blake-rouse) wrote : Posted in a previous version of this proposal

Removed the hasattr check to use a NotImplemented exception. Can you give this another review?

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote : Posted in a previous version of this proposal

Thanks, this branch looks a lot simpler than I remember it! Could you add a note in the docstring for compose_preseed about the NotImplementedError and what it means? Similar to “:param …:” and “:return:” the format also supports “:raise …:”.

review: Approve
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Already approved by jtv. Had to re-submit to fix the invalid prequisite branch. Self approving.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Download full text (80.7 KiB)

The attempt to merge lp:~blake-rouse/maas/osystem-preseed-cleanup into lp:maas failed. Below is the output from the failed tests.

Ign http://security.ubuntu.com trusty-security InRelease
Hit http://security.ubuntu.com trusty-security Release.gpg
Ign http://nova.clouds.archive.ubuntu.com trusty InRelease
Hit http://security.ubuntu.com trusty-security Release
Ign http://nova.clouds.archive.ubuntu.com trusty-updates InRelease
Hit http://nova.clouds.archive.ubuntu.com trusty Release.gpg
Get:1 http://nova.clouds.archive.ubuntu.com trusty-updates Release.gpg [933 B]
Hit http://nova.clouds.archive.ubuntu.com trusty Release
Get:2 http://nova.clouds.archive.ubuntu.com trusty-updates Release [58.5 kB]
Hit http://security.ubuntu.com trusty-security/main Sources
Hit http://security.ubuntu.com trusty-security/universe Sources
Hit http://security.ubuntu.com trusty-security/main amd64 Packages
Hit http://security.ubuntu.com trusty-security/universe amd64 Packages
Hit http://security.ubuntu.com trusty-security/main Translation-en
Hit http://security.ubuntu.com trusty-security/universe Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty/main Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Sources
Hit http://nova.clouds.archive.ubuntu.com trusty/main amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/universe amd64 Packages
Hit http://nova.clouds.archive.ubuntu.com trusty/main Translation-en
Ign http://security.ubuntu.com trusty-security/main Translation-en_US
Hit http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en
Ign http://security.ubuntu.com trusty-security/universe Translation-en_US
Get:3 http://nova.clouds.archive.ubuntu.com trusty-updates/main Sources [45.7 kB]
Get:4 http://nova.clouds.archive.ubuntu.com trusty-updates/universe Sources [28.2 kB]
Get:5 http://nova.clouds.archive.ubuntu.com trusty-updates/main amd64 Packages [109 kB]
Get:6 http://nova.clouds.archive.ubuntu.com trusty-updates/universe amd64 Packages [74.9 kB]
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en
Hit http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en
Ign http://nova.clouds.archive.ubuntu.com trusty/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty/universe Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty-updates/main Translation-en_US
Ign http://nova.clouds.archive.ubuntu.com trusty-updates/universe Translation-en_US
Fetched 318 kB in 0s (975 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
     --no-install-recommends install apache2 bind9 bind9utils build-essential bzr-builddeb curl daemontools debhelper dh-apport distro-info dnsutils firefox freeipmi-tools ipython isc-dhcp-common libjs-raphael libjs-yui3-full libjs-yui3-min libpq-dev make postgresql python-amqplib python-bzrlib python-celery python-convoy python-crochet python-cssselect python-curtin python-dev python-distro-info python-django python-django-piston python-django-south python-djorm-ext-pgarray python-docutils python-formencode python-httplib2 python-jinja2 python-jsonschema python-lockfile python-lxml python-netaddr python-netif...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/compose_preseed.py'
--- src/maasserver/compose_preseed.py 2013-10-18 09:54:17 +0000
+++ src/maasserver/compose_preseed.py 2014-05-29 18:37:02 +0000
@@ -20,6 +20,7 @@
2020
21from maasserver.enum import NODE_STATUS21from maasserver.enum import NODE_STATUS
22from maasserver.utils import absolute_reverse22from maasserver.utils import absolute_reverse
23from provisioningserver.driver import OperatingSystemRegistry
23import yaml24import yaml
2425
2526
@@ -104,6 +105,13 @@
104 if node.status == NODE_STATUS.COMMISSIONING:105 if node.status == NODE_STATUS.COMMISSIONING:
105 return compose_commissioning_preseed(token, base_url)106 return compose_commissioning_preseed(token, base_url)
106 else:107 else:
108 osystem = OperatingSystemRegistry[node.get_osystem()]
109 metadata_url = absolute_reverse('metadata', base_url=base_url)
110 try:
111 return osystem.compose_preseed(node, token, metadata_url)
112 except NotImplementedError:
113 pass
114
107 if node.should_use_traditional_installer():115 if node.should_use_traditional_installer():
108 return compose_cloud_init_preseed(token, base_url)116 return compose_cloud_init_preseed(token, base_url)
109 else:117 else:
110118
=== modified file 'src/maasserver/enum.py'
--- src/maasserver/enum.py 2014-03-28 16:36:32 +0000
+++ src/maasserver/enum.py 2014-05-29 18:37:02 +0000
@@ -13,7 +13,6 @@
1313
14__metaclass__ = type14__metaclass__ = type
15__all__ = [15__all__ = [
16 'COMMISSIONING_DISTRO_SERIES_CHOICES',
17 'COMPONENT',16 'COMPONENT',
18 'NODEGROUP_STATUS',17 'NODEGROUP_STATUS',
19 'NODEGROUP_STATUS_CHOICES',18 'NODEGROUP_STATUS_CHOICES',
@@ -25,8 +24,6 @@
25 'NODE_STATUS_CHOICES',24 'NODE_STATUS_CHOICES',
26 'NODE_STATUS_CHOICES_DICT',25 'NODE_STATUS_CHOICES_DICT',
27 'PRESEED_TYPE',26 'PRESEED_TYPE',
28 'DISTRO_SERIES',
29 'DISTRO_SERIES_CHOICES',
30 'USERDATA_TYPE',27 'USERDATA_TYPE',
31 ]28 ]
3229
@@ -86,36 +83,6 @@
86NODE_STATUS_CHOICES_DICT = OrderedDict(NODE_STATUS_CHOICES)83NODE_STATUS_CHOICES_DICT = OrderedDict(NODE_STATUS_CHOICES)
8784
8885
89class DISTRO_SERIES:
90 """List of supported ubuntu releases."""
91 #:
92 default = ''
93 #:
94 precise = 'precise'
95 #:
96 quantal = 'quantal'
97 #:
98 raring = 'raring'
99 #:
100 saucy = 'saucy'
101 #:
102 trusty = 'trusty'
103
104DISTRO_SERIES_CHOICES = (
105 (DISTRO_SERIES.default, 'Default Ubuntu Release'),
106 (DISTRO_SERIES.precise, 'Ubuntu 12.04 LTS "Precise Pangolin"'),
107 (DISTRO_SERIES.quantal, 'Ubuntu 12.10 "Quantal Quetzal"'),
108 (DISTRO_SERIES.raring, 'Ubuntu 13.04 "Raring Ringtail"'),
109 (DISTRO_SERIES.saucy, 'Ubuntu 13.10 "Saucy Salamander"'),
110 (DISTRO_SERIES.trusty, 'Ubuntu 14.04 LTS "Trusty Tahr"'),
111)
112
113
114COMMISSIONING_DISTRO_SERIES_CHOICES = (
115 (DISTRO_SERIES.trusty, dict(DISTRO_SERIES_CHOICES)[DISTRO_SERIES.trusty]),
116)
117
118
119class NODE_PERMISSION:86class NODE_PERMISSION:
120 """Permissions relating to nodes."""87 """Permissions relating to nodes."""
121 VIEW = 'view_node'88 VIEW = 'view_node'
12289
=== modified file 'src/maasserver/models/bootsourceselection.py'
--- src/maasserver/models/bootsourceselection.py 2014-05-16 11:41:18 +0000
+++ src/maasserver/models/bootsourceselection.py 2014-05-29 18:37:02 +0000
@@ -24,12 +24,16 @@
24 )24 )
25import djorm_pgarray.fields25import djorm_pgarray.fields
26from maasserver import DefaultMeta26from maasserver import DefaultMeta
27from maasserver.enum import (
28 DISTRO_SERIES,
29 DISTRO_SERIES_CHOICES,
30 )
31from maasserver.models.cleansave import CleanSave27from maasserver.models.cleansave import CleanSave
32from maasserver.models.timestampedmodel import TimestampedModel28from maasserver.models.timestampedmodel import TimestampedModel
29from provisioningserver.driver.os_ubuntu import UbuntuOS
30
31
32def list_release_choices():
33 """Return Django "choices" list for Ubuntu releases."""
34 osystem = UbuntuOS()
35 releases = osystem.get_supported_releases()
36 return osystem.format_release_choices(releases)
3337
3438
35class BootSourceSelectionManager(Manager):39class BootSourceSelectionManager(Manager):
@@ -47,8 +51,8 @@
47 boot_source = ForeignKey('maasserver.BootSource', blank=False)51 boot_source = ForeignKey('maasserver.BootSource', blank=False)
4852
49 release = CharField(53 release = CharField(
50 max_length=20, choices=DISTRO_SERIES_CHOICES, blank=True,54 max_length=20, choices=list_release_choices(), blank=True,
51 default=DISTRO_SERIES.default,55 default='',
52 help_text="The Ubuntu release for which to import resources.")56 help_text="The Ubuntu release for which to import resources.")
5357
54 arches = djorm_pgarray.fields.ArrayField(dbtype="text")58 arches = djorm_pgarray.fields.ArrayField(dbtype="text")
5559
=== modified file 'src/maasserver/testing/factory.py'
--- src/maasserver/testing/factory.py 2014-05-29 17:03:20 +0000
+++ src/maasserver/testing/factory.py 2014-05-29 18:37:02 +0000
@@ -23,7 +23,6 @@
23from django.contrib.auth.models import User23from django.contrib.auth.models import User
24from maasserver.clusterrpc.power_parameters import get_power_types24from maasserver.clusterrpc.power_parameters import get_power_types
25from maasserver.enum import (25from maasserver.enum import (
26 DISTRO_SERIES_CHOICES,
27 NODE_STATUS,26 NODE_STATUS,
28 NODEGROUP_STATUS,27 NODEGROUP_STATUS,
29 NODEGROUPINTERFACE_MANAGEMENT,28 NODEGROUPINTERFACE_MANAGEMENT,
@@ -62,6 +61,7 @@
62# XXX 2014-05-13 blake-rouse bug=131914361# XXX 2014-05-13 blake-rouse bug=1319143
63# Need to not import directly, use RPC to info from cluster.62# Need to not import directly, use RPC to info from cluster.
64from provisioningserver.driver import OperatingSystemRegistry63from provisioningserver.driver import OperatingSystemRegistry
64from provisioningserver.driver.os_ubuntu import UbuntuOS
6565
66# We have a limited number of public keys:66# We have a limited number of public keys:
67# src/maasserver/tests/data/test_rsa{0, 1, 2, 3, 4}.pub67# src/maasserver/tests/data/test_rsa{0, 1, 2, 3, 4}.pub
@@ -717,7 +717,8 @@
717 if boot_source is None:717 if boot_source is None:
718 boot_source = self.make_boot_source()718 boot_source = self.make_boot_source()
719 if release is None:719 if release is None:
720 release = self.getRandomChoice(DISTRO_SERIES_CHOICES)720 ubuntu_os = UbuntuOS()
721 release = self.getRandomRelease(ubuntu_os)
721 if arches is None:722 if arches is None:
722 arch_count = random.randint(1, 10)723 arch_count = random.randint(1, 10)
723 arches = [self.make_name("arch") for i in range(arch_count)]724 arches = [self.make_name("arch") for i in range(arch_count)]
724725
=== modified file 'src/maasserver/tests/test_api_boot_source_selections.py'
--- src/maasserver/tests/test_api_boot_source_selections.py 2014-05-27 13:53:21 +0000
+++ src/maasserver/tests/test_api_boot_source_selections.py 2014-05-29 18:37:02 +0000
@@ -19,11 +19,11 @@
1919
20from django.core.urlresolvers import reverse20from django.core.urlresolvers import reverse
21from maasserver.api import DISPLAYED_BOOTSOURCESELECTION_FIELDS21from maasserver.api import DISPLAYED_BOOTSOURCESELECTION_FIELDS
22from maasserver.enum import DISTRO_SERIES_CHOICES
23from maasserver.models import BootSourceSelection22from maasserver.models import BootSourceSelection
24from maasserver.testing import reload_object23from maasserver.testing import reload_object
25from maasserver.testing.api import APITestCase24from maasserver.testing.api import APITestCase
26from maasserver.testing.factory import factory25from maasserver.testing.factory import factory
26from provisioningserver.driver.os_ubuntu import UbuntuOS
27from testtools.matchers import MatchesStructure27from testtools.matchers import MatchesStructure
2828
2929
@@ -98,8 +98,8 @@
98 def test_PUT_updates_boot_source_selection(self):98 def test_PUT_updates_boot_source_selection(self):
99 self.become_admin()99 self.become_admin()
100 boot_source_selection = factory.make_boot_source_selection()100 boot_source_selection = factory.make_boot_source_selection()
101 new_release = factory.getRandomChoice(101 ubuntu_os = UbuntuOS()
102 DISTRO_SERIES_CHOICES)102 new_release = factory.getRandomRelease(ubuntu_os)
103 new_values = {103 new_values = {
104 'release': new_release,104 'release': new_release,
105 'arches': [factory.make_name('arch'), factory.make_name('arch')],105 'arches': [factory.make_name('arch'), factory.make_name('arch')],
@@ -160,8 +160,8 @@
160 def test_POST_creates_boot_source_selection(self):160 def test_POST_creates_boot_source_selection(self):
161 self.become_admin()161 self.become_admin()
162 boot_source = factory.make_boot_source()162 boot_source = factory.make_boot_source()
163 new_release = factory.getRandomChoice(163 ubuntu_os = UbuntuOS()
164 DISTRO_SERIES_CHOICES)164 new_release = factory.getRandomRelease(ubuntu_os)
165 params = {165 params = {
166 'release': new_release,166 'release': new_release,
167 'arches': [factory.make_name('arch'), factory.make_name('arch')],167 'arches': [factory.make_name('arch'), factory.make_name('arch')],
@@ -182,8 +182,8 @@
182182
183 def test_POST_requires_admin(self):183 def test_POST_requires_admin(self):
184 boot_source = factory.make_boot_source()184 boot_source = factory.make_boot_source()
185 new_release = factory.getRandomChoice(185 ubuntu_os = UbuntuOS()
186 DISTRO_SERIES_CHOICES)186 new_release = factory.getRandomRelease(ubuntu_os)
187 params = {187 params = {
188 'release': new_release,188 'release': new_release,
189 'arches': [factory.make_name('arch'), factory.make_name('arch')],189 'arches': [factory.make_name('arch'), factory.make_name('arch')],
190190
=== modified file 'src/maasserver/tests/test_compose_preseed.py'
--- src/maasserver/tests/test_compose_preseed.py 2013-10-07 09:12:40 +0000
+++ src/maasserver/tests/test_compose_preseed.py 2014-05-29 18:37:02 +0000
@@ -17,8 +17,10 @@
17from maasserver.compose_preseed import compose_preseed17from maasserver.compose_preseed import compose_preseed
18from maasserver.enum import NODE_STATUS18from maasserver.enum import NODE_STATUS
19from maasserver.testing.factory import factory19from maasserver.testing.factory import factory
20from maasserver.testing.osystems import make_usable_osystem
20from maasserver.testing.testcase import MAASServerTestCase21from maasserver.testing.testcase import MAASServerTestCase
21from maasserver.utils import absolute_reverse22from maasserver.utils import absolute_reverse
23from maastesting.matchers import MockCalledOnceWith
22from metadataserver.models import NodeKey24from metadataserver.models import NodeKey
23from testtools.matchers import (25from testtools.matchers import (
24 KeysEqual,26 KeysEqual,
@@ -110,3 +112,16 @@
110 self.assertEqual(112 self.assertEqual(
111 absolute_reverse('curtin-metadata'),113 absolute_reverse('curtin-metadata'),
112 preseed['datasource']['MAAS']['metadata_url'])114 preseed['datasource']['MAAS']['metadata_url'])
115
116 def test_compose_preseed_with_osystem_compose_preseed(self):
117 osystem = make_usable_osystem(self)
118 mock_compose = self.patch(osystem, 'compose_preseed')
119 node = factory.make_node(
120 osystem=osystem.name, status=NODE_STATUS.READY)
121
122 token = NodeKey.objects.get_token_for_node(node)
123 url = absolute_reverse('metadata')
124 compose_preseed(node)
125 self.assertThat(
126 mock_compose,
127 MockCalledOnceWith(node, token, url))
113128
=== modified file 'src/maasserver/tests/test_forms.py'
--- src/maasserver/tests/test_forms.py 2014-05-29 17:03:20 +0000
+++ src/maasserver/tests/test_forms.py 2014-05-29 18:37:02 +0000
@@ -29,7 +29,6 @@
29from django.http import QueryDict29from django.http import QueryDict
30from maasserver.clusterrpc.power_parameters import get_power_type_choices30from maasserver.clusterrpc.power_parameters import get_power_type_choices
31from maasserver.enum import (31from maasserver.enum import (
32 DISTRO_SERIES_CHOICES,
33 NODE_STATUS,32 NODE_STATUS,
34 NODEGROUP_STATUS,33 NODEGROUP_STATUS,
35 NODEGROUPINTERFACE_MANAGEMENT,34 NODEGROUPINTERFACE_MANAGEMENT,
@@ -112,6 +111,7 @@
112from metadataserver.models import CommissioningScript111from metadataserver.models import CommissioningScript
113from netaddr import IPNetwork112from netaddr import IPNetwork
114from provisioningserver import tasks113from provisioningserver import tasks
114from provisioningserver.driver.os_ubuntu import UbuntuOS
115from testtools import TestCase115from testtools import TestCase
116from testtools.matchers import (116from testtools.matchers import (
117 AllMatch,117 AllMatch,
@@ -2062,8 +2062,8 @@
20622062
2063 def test_edits_boot_source_selection_object(self):2063 def test_edits_boot_source_selection_object(self):
2064 boot_source_selection = factory.make_boot_source_selection()2064 boot_source_selection = factory.make_boot_source_selection()
2065 new_release = factory.getRandomChoice(2065 ubuntu_os = UbuntuOS()
2066 DISTRO_SERIES_CHOICES)2066 new_release = factory.getRandomRelease(ubuntu_os)
2067 params = {2067 params = {
2068 'release': new_release,2068 'release': new_release,
2069 'arches': [factory.make_name('arch'), factory.make_name('arch')],2069 'arches': [factory.make_name('arch'), factory.make_name('arch')],
@@ -2080,8 +2080,8 @@
20802080
2081 def test_creates_boot_source_selection_object(self):2081 def test_creates_boot_source_selection_object(self):
2082 boot_source = factory.make_boot_source()2082 boot_source = factory.make_boot_source()
2083 new_release = factory.getRandomChoice(2083 ubuntu_os = UbuntuOS()
2084 DISTRO_SERIES_CHOICES)2084 new_release = factory.getRandomRelease(ubuntu_os)
2085 params = {2085 params = {
2086 'release': new_release,2086 'release': new_release,
2087 'arches': [factory.make_name('arch'), factory.make_name('arch')],2087 'arches': [factory.make_name('arch'), factory.make_name('arch')],
20882088
=== modified file 'src/provisioningserver/driver/__init__.py'
--- src/provisioningserver/driver/__init__.py 2014-05-15 17:27:53 +0000
+++ src/provisioningserver/driver/__init__.py 2014-05-29 18:37:02 +0000
@@ -158,6 +158,18 @@
158 :returns: list of supported purposes158 :returns: list of supported purposes
159 """159 """
160160
161 def compose_preseed(self, node, token, metadata_url):
162 """Composes the preseed for the given node.
163
164 :param node: Node preseed needs generating.
165 :param token: OAuth token for url.
166 :param metadata_url: Metdata url for node.
167 :returns: Preseed for node.
168 :raise:
169 NotImplementedError: doesn't implement a custom preseed
170 """
171 raise NotImplementedError()
172
161173
162class HardwareDiscoverContext:174class HardwareDiscoverContext:
163175