Projet

Général

Profil

0001-models-add-Recipe-33633.patch

Christophe Siraut, 03 juin 2019 15:34

Télécharger (21,3 ko)

Voir les différences:

Subject: [PATCH] models: add Recipe (#33633)

 hobo/environment/management/commands/cook.py | 239 +-----------------
 hobo/environment/models.py                   | 241 +++++++++++++++++++
 2 files changed, 245 insertions(+), 235 deletions(-)
hobo/environment/management/commands/cook.py
65 65

  
66 66
    def run_cook(self, filename):
67 67
        if filename == '-':
68
            recipe = json.loads(sys.stdin)
68
            content = sys.stdin
69 69
        else:
70
            recipe = json.load(open(filename))
71
        variables = {}
72
        steps = []
73
        if 'load-variables-from' in recipe:
74
            variables.update(json.load(open(
75
                os.path.join(os.path.dirname(filename), recipe['load-variables-from']))))
76
        variables.update(recipe.get('variables', {}))
77
        for step in recipe.get('steps', []):
78
            action, action_args = step.items()[0]
79
            for arg in action_args:
80
                if not isinstance(action_args[arg], basestring):
81
                    continue
82
                action_args[arg] = string.Template(
83
                        action_args[arg]).substitute(variables)
84
            if not self.permissive:
85
                self.check_action(action, action_args)
86
            steps.append((action, action_args))
87

  
88
        for action, action_args in steps:
89
            getattr(self, action.replace('-', '_'))(**action_args)
90
            if self.must_notify:
91
                notify_agents(None)
92
                self.wait_operationals(timeout=self.timeout)
93
                self.must_notify = False
94
        notify_agents(None)
95

  
96
    def wait_operationals(self, timeout):
97
        services = []
98
        for service_class in AVAILABLE_SERVICES:
99
            services.extend(service_class.objects.all())
100

  
101
        t0 = time.time()
102
        while len(services) > 0:
103
            for service in services[:]:
104
                if service.last_operational_success_timestamp:
105
                    services.remove(service)
106
                    continue
107
                service.check_operational()
108
            if len(services) == 0:
109
                break
110
            if self.verbosity:
111
                sys.stderr.write('.')
112
            time.sleep(0.5)
113
            if time.time() - t0 > timeout:
114
                if self.verbosity:
115
                    sys.stderr.write('\n')
116
                raise CommandError('timeout waiting for %s' % ', '.join(
117
                    [x.base_url for x in services]))
118

  
119
    def create_hobo(self, url, primary=False, title=None, slug=None):
120
        if connection.get_tenant().schema_name == 'public':
121
            # if we're not currently in a tenant then we force the creation of
122
            # a primary hobo
123
            primary = True
124

  
125
        if not primary:
126
            if not slug:
127
                slug = 'hobo-%s' % slugify(title)
128
            self.create_site(Hobo, url, title, slug,
129
                    template_name='', variables=None)
130
            domain = get_domain(url)
131
            tenant = TenantMiddleware.get_tenant_by_hostname(domain)
132
            connection.set_tenant(tenant)
133
            return
134

  
135
        domain = get_domain(url)
136
        try:
137
            call_command('create_hobo_tenant', domain)
138
        except CommandError:
139
            pass
140
        tenant = TenantMiddleware.get_tenant_by_hostname(domain)
141

  
142
        base_url_filename = os.path.join(tenant.get_directory(), 'base_url')
143
        if not os.path.exists(base_url_filename):
144
            fd = open(base_url_filename, 'w')
145
            fd.write(url.rstrip('/'))
146
            fd.close()
147

  
148
        connection.set_tenant(tenant)
149

  
150
    def create_superuser(self, username='admin', email='admin@localhost',
151
            first_name='Admin', last_name='', password=None):
152
        user, created = User.objects.get_or_create(
153
                username=username, email=email, is_superuser=True)
154
        if created:
155
            user.first_name = first_name
156
            user.last_name = last_name
157
            password = password or User.objects.make_random_password(length=30)
158
        if password:
159
            user.set_password(password)
160
            user.save()
161
        if created and self.verbosity:
162
            print 'superuser account: %s / %s' % (username, password)
163

  
164
    def create_site(self, klass, base_url, title, slug, template_name, variables):
165
        if slug is None:
166
            slug = klass.Extra.service_default_slug
167
        obj, must_save = klass.objects.get_or_create(
168
                slug=slug,
169
                defaults={
170
                    'title': title,
171
                    'base_url': base_url,
172
                    'template_name': template_name
173
                })
174
        for attr in ('title', 'base_url', 'template_name'):
175
            if getattr(obj, attr) != locals().get(attr):
176
                setattr(obj, attr, locals().get(attr))
177
                must_save = True
178
        if must_save:
179
            try:
180
                obj.full_clean(exclude=['last_operational_success_timestamp', 'last_operational_check_timestamp'])
181
            except ValidationError as e:
182
                raise CommandError(str(e))
183

  
184
            obj.save()
185
            self.must_notify = True
186
        variables = variables or {}
187
        obj_type = ContentType.objects.get_for_model(klass)
188
        for variable_name in variables.keys():
189
            label = variables[variable_name].get('label')
190
            variable, created = Variable.objects.get_or_create(name=variable_name,
191
                    service_type=obj_type,
192
                    service_pk=obj.id,
193
                    defaults={'label': label or variable_name})
194
            if label:
195
                variable.label = label
196
            value = variables[variable_name].get('value')
197
            if isinstance(value, dict) or isinstance(value, list):
198
                value = json.dumps(value)
199
            variable.value = value
200
            variable.save()
201

  
202
    def create_authentic(self, url, title, slug=None, template_name='', variables=None):
203
        return self.create_site(Authentic, url, title, slug, template_name, variables)
204

  
205
    def set_idp(self, url=None):
206
        if url:
207
            obj = Authentic.objects.get(base_url=url)
208
        else:
209
            obj = Authentic.objects.all()[0]
210
        if not obj.use_as_idp_for_self:
211
            obj.use_as_idp_for_self = True
212
        obj.save()
213

  
214
    def create_combo(self, url, title, slug=None, template_name='', variables=None):
215
        return self.create_site(Combo, url, title, slug, template_name, variables)
216

  
217
    def create_wcs(self, url, title, slug=None, template_name='', variables=None):
218
        return self.create_site(Wcs, url, title, slug, template_name, variables)
219

  
220
    def create_passerelle(self, url, title, slug=None, template_name='', variables=None):
221
        return self.create_site(Passerelle, url, title, slug, template_name, variables)
222

  
223
    def create_fargo(self, url, title, slug=None, template_name='', variables=None):
224
        return self.create_site(Fargo, url, title, slug, template_name, variables)
225

  
226
    def create_welco(self, url, title, slug=None, template_name='', variables=None):
227
        return self.create_site(Welco, url, title, slug, template_name, variables)
228

  
229
    def create_chrono(self, url, title, slug=None, template_name='', variables=None):
230
        return self.create_site(Chrono, url, title, slug, template_name, variables)
231

  
232
    def create_corbo(self, url, title, slug=None, template_name='', variables=None):
233
        return self.create_site(Corbo, url, title, slug, template_name, variables)
234

  
235
    def create_bijoe(self, url, title, slug=None, template_name='', variables=None):
236
        return self.create_site(BiJoe, url, title, slug, template_name, variables)
237

  
238
    def set_theme(self, theme):
239
        set_theme(theme)
240
        HoboDeployCommand().configure_theme(
241
                {'variables': {'theme': theme}},
242
                connection.get_tenant())
243

  
244
    def set_variable(self, name, value, label=None):
245
        variable, created = Variable.objects.get_or_create(name=name,
246
                defaults={'label': label or name})
247
        if isinstance(value, dict) or isinstance(value, list):
248
            value = json.dumps(value)
249
        if variable.label != label or variable.value != value or created:
250
            if label:
251
                variable.label = label
252
            variable.value = value
253
            variable.save()
254

  
255
    def enable_attribute(self, name):
256
        try:
257
            attribute = AttributeDefinition.objects.get(name=name)
258
        except AttributeDefinition.DoesNotExist:
259
            return
260
        if attribute.disabled:
261
            attribute.disabled = False
262
            attribute.save()
263

  
264
    def disable_attribute(self, name):
265
        try:
266
            attribute = AttributeDefinition.objects.get(name=name)
267
        except AttributeDefinition.DoesNotExist:
268
            return
269
        if not attribute.disabled:
270
            attribute.disabled = True
271
            attribute.save()
272

  
273
    def set_attribute(self, name, label, **kwargs):
274
        # possible keys in kwargs are: description, required,
275
        # asked_on_registration, user_editable, user_visible, kind, order
276
        attribute, created = AttributeDefinition.objects.get_or_create(
277
                name=name, defaults={'label': label, 'order': 0})
278
        kwargs['label'] = label
279
        attribute_fields = [x.name for x in AttributeDefinition._meta.fields]
280
        for arg in kwargs:
281
            if arg in attribute_fields:
282
                setattr(attribute, arg, kwargs.get(arg))
283

  
284
        if created and not attribute.order:
285
            attribute.order = AttributeDefinition.objects.all().aggregate(
286
                    Max('order')).get('order__max') + 1
287
        attribute.save()
288

  
289
    def cook(self, filename):
290
        current_tenant = connection.get_tenant()
291
        self.run_cook(filename)
292
        connection.set_tenant(current_tenant)
293

  
294
    def check_action(self, action, action_args):
295
        if not hasattr(self, action.replace('-', '_')):
296
            raise CommandError('Error: Unknown action %s' % action)
297
        if 'url' in action_args.keys():
298
            url = action_args['url']
299
            service = ServiceBase(title='dummy', base_url=url)
300
            if not service.is_resolvable():
301
                raise CommandError('Error: %s is not resolvable' % url)
302
            if not service.has_valid_certificate():
303
                raise CommandError('Error: %s has no valid certificate' % url)
70
            content = open(filename).read()
71
        recipe = Recipe(name=temp, content=content)
72
        recipe.cook()
hobo/environment/models.py
487 487

  
488 488
AVAILABLE_SERVICES = [Authentic, Wcs, Passerelle, Combo, Fargo, Welco,
489 489
                      MandayeJS, Chrono, Corbo, BiJoe, Hobo]
490

  
491

  
492
class Recipe(models.Model):
493
    name = models.CharField(max_length=100, verbose_name=_('name'))
494
    content = models.TextField(blank=True, verbose_name=_('content'))
495

  
496
    def cook(self)
497
        recipe = json.loads(self.content)
498
        variables = {}
499
        steps = []
500
        if 'load-variables-from' in recipe:
501
            variables.update(json.load(open(
502
                os.path.join(os.path.dirname(filename), recipe['load-variables-from']))))
503
        variables.update(recipe.get('variables', {}))
504
        for step in recipe.get('steps', []):
505
            action, action_args = step.items()[0]
506
            for arg in action_args:
507
                if not isinstance(action_args[arg], basestring):
508
                    continue
509
                action_args[arg] = string.Template(
510
                        action_args[arg]).substitute(variables)
511
            if not self.permissive:
512
                self.check_action(action, action_args)
513
            steps.append((action, action_args))
514

  
515
        for action, action_args in steps:
516
            getattr(self, action.replace('-', '_'))(**action_args)
517
            if self.must_notify:
518
                notify_agents(None)
519
                self.wait_operationals(timeout=self.timeout)
520
                self.must_notify = False
521
        notify_agents(None)
522

  
523
    def wait_operationals(self, timeout):
524
        services = []
525
        for service_class in AVAILABLE_SERVICES:
526
            services.extend(service_class.objects.all())
527

  
528
        t0 = time.time()
529
        while len(services) > 0:
530
            for service in services[:]:
531
                if service.last_operational_success_timestamp:
532
                    services.remove(service)
533
                    continue
534
                service.check_operational()
535
            if len(services) == 0:
536
                break
537
            if self.verbosity:
538
                sys.stderr.write('.')
539
            time.sleep(0.5)
540
            if time.time() - t0 > timeout:
541
                if self.verbosity:
542
                    sys.stderr.write('\n')
543
                raise CommandError('timeout waiting for %s' % ', '.join(
544
                    [x.base_url for x in services]))
545

  
546
    def create_hobo(self, url, primary=False, title=None, slug=None):
547
        if connection.get_tenant().schema_name == 'public':
548
            # if we're not currently in a tenant then we force the creation of
549
            # a primary hobo
550
            primary = True
551

  
552
        if not primary:
553
            if not slug:
554
                slug = 'hobo-%s' % slugify(title)
555
            self.create_site(Hobo, url, title, slug,
556
                    template_name='', variables=None)
557
            domain = get_domain(url)
558
            tenant = TenantMiddleware.get_tenant_by_hostname(domain)
559
            connection.set_tenant(tenant)
560
            return
561

  
562
        domain = get_domain(url)
563
        try:
564
            call_command('create_hobo_tenant', domain)
565
        except CommandError:
566
            pass
567
        tenant = TenantMiddleware.get_tenant_by_hostname(domain)
568

  
569
        base_url_filename = os.path.join(tenant.get_directory(), 'base_url')
570
        if not os.path.exists(base_url_filename):
571
            fd = open(base_url_filename, 'w')
572
            fd.write(url.rstrip('/'))
573
            fd.close()
574

  
575
        connection.set_tenant(tenant)
576

  
577
    def create_superuser(self, username='admin', email='admin@localhost',
578
            first_name='Admin', last_name='', password=None):
579
        user, created = User.objects.get_or_create(
580
                username=username, email=email, is_superuser=True)
581
        if created:
582
            user.first_name = first_name
583
            user.last_name = last_name
584
            password = password or User.objects.make_random_password(length=30)
585
        if password:
586
            user.set_password(password)
587
            user.save()
588
        if created and self.verbosity:
589
            print 'superuser account: %s / %s' % (username, password)
590

  
591
    def create_site(self, klass, base_url, title, slug, template_name, variables):
592
        if slug is None:
593
            slug = klass.Extra.service_default_slug
594
        obj, must_save = klass.objects.get_or_create(
595
                slug=slug,
596
                defaults={
597
                    'title': title,
598
                    'base_url': base_url,
599
                    'template_name': template_name
600
                })
601
        for attr in ('title', 'base_url', 'template_name'):
602
            if getattr(obj, attr) != locals().get(attr):
603
                setattr(obj, attr, locals().get(attr))
604
                must_save = True
605
        if must_save:
606
            try:
607
                obj.full_clean(exclude=['last_operational_success_timestamp', 'last_operational_check_timestamp'])
608
            except ValidationError as e:
609
                raise CommandError(str(e))
610

  
611
            obj.save()
612
            self.must_notify = True
613
        variables = variables or {}
614
        obj_type = ContentType.objects.get_for_model(klass)
615
        for variable_name in variables.keys():
616
            label = variables[variable_name].get('label')
617
            variable, created = Variable.objects.get_or_create(name=variable_name,
618
                    service_type=obj_type,
619
                    service_pk=obj.id,
620
                    defaults={'label': label or variable_name})
621
            if label:
622
                variable.label = label
623
            value = variables[variable_name].get('value')
624
            if isinstance(value, dict) or isinstance(value, list):
625
                value = json.dumps(value)
626
            variable.value = value
627
            variable.save()
628

  
629
    def create_authentic(self, url, title, slug=None, template_name='', variables=None):
630
        return self.create_site(Authentic, url, title, slug, template_name, variables)
631

  
632
    def set_idp(self, url=None):
633
        if url:
634
            obj = Authentic.objects.get(base_url=url)
635
        else:
636
            obj = Authentic.objects.all()[0]
637
        if not obj.use_as_idp_for_self:
638
            obj.use_as_idp_for_self = True
639
        obj.save()
640

  
641
    def create_combo(self, url, title, slug=None, template_name='', variables=None):
642
        return self.create_site(Combo, url, title, slug, template_name, variables)
643

  
644
    def create_wcs(self, url, title, slug=None, template_name='', variables=None):
645
        return self.create_site(Wcs, url, title, slug, template_name, variables)
646

  
647
    def create_passerelle(self, url, title, slug=None, template_name='', variables=None):
648
        return self.create_site(Passerelle, url, title, slug, template_name, variables)
649

  
650
    def create_fargo(self, url, title, slug=None, template_name='', variables=None):
651
        return self.create_site(Fargo, url, title, slug, template_name, variables)
652

  
653
    def create_welco(self, url, title, slug=None, template_name='', variables=None):
654
        return self.create_site(Welco, url, title, slug, template_name, variables)
655

  
656
    def create_chrono(self, url, title, slug=None, template_name='', variables=None):
657
        return self.create_site(Chrono, url, title, slug, template_name, variables)
658

  
659
    def create_corbo(self, url, title, slug=None, template_name='', variables=None):
660
        return self.create_site(Corbo, url, title, slug, template_name, variables)
661

  
662
    def create_bijoe(self, url, title, slug=None, template_name='', variables=None):
663
        return self.create_site(BiJoe, url, title, slug, template_name, variables)
664

  
665
    def set_theme(self, theme):
666
        set_theme(theme)
667
        HoboDeployCommand().configure_theme(
668
                {'variables': {'theme': theme}},
669
                connection.get_tenant())
670

  
671
    def set_variable(self, name, value, label=None):
672
        variable, created = Variable.objects.get_or_create(name=name,
673
                defaults={'label': label or name})
674
        if isinstance(value, dict) or isinstance(value, list):
675
            value = json.dumps(value)
676
        if variable.label != label or variable.value != value or created:
677
            if label:
678
                variable.label = label
679
            variable.value = value
680
            variable.save()
681

  
682
    def enable_attribute(self, name):
683
        try:
684
            attribute = AttributeDefinition.objects.get(name=name)
685
        except AttributeDefinition.DoesNotExist:
686
            return
687
        if attribute.disabled:
688
            attribute.disabled = False
689
            attribute.save()
690

  
691
    def disable_attribute(self, name):
692
        try:
693
            attribute = AttributeDefinition.objects.get(name=name)
694
        except AttributeDefinition.DoesNotExist:
695
            return
696
        if not attribute.disabled:
697
            attribute.disabled = True
698
            attribute.save()
699

  
700
    def set_attribute(self, name, label, **kwargs):
701
        # possible keys in kwargs are: description, required,
702
        # asked_on_registration, user_editable, user_visible, kind, order
703
        attribute, created = AttributeDefinition.objects.get_or_create(
704
                name=name, defaults={'label': label, 'order': 0})
705
        kwargs['label'] = label
706
        attribute_fields = [x.name for x in AttributeDefinition._meta.fields]
707
        for arg in kwargs:
708
            if arg in attribute_fields:
709
                setattr(attribute, arg, kwargs.get(arg))
710

  
711
        if created and not attribute.order:
712
            attribute.order = AttributeDefinition.objects.all().aggregate(
713
                    Max('order')).get('order__max') + 1
714
        attribute.save()
715

  
716
    def cook(self, filename):
717
        current_tenant = connection.get_tenant()
718
        self.run_cook(filename)
719
        connection.set_tenant(current_tenant)
720

  
721
    def check_action(self, action, action_args):
722
        if not hasattr(self, action.replace('-', '_')):
723
            raise CommandError('Error: Unknown action %s' % action)
724
        if 'url' in action_args.keys():
725
            url = action_args['url']
726
            service = ServiceBase(title='dummy', base_url=url)
727
            if not service.is_resolvable():
728
                raise CommandError('Error: %s is not resolvable' % url)
729
            if not service.has_valid_certificate():
730
                raise CommandError('Error: %s has no valid certificate' % url)
490
-