mirror of
https://github.com/ansible/awx.git
synced 2026-03-21 02:47:35 -02:30
Add support for inbound hop nodes
This commit is contained in:
@@ -82,6 +82,7 @@ from awx.main.models import (
|
|||||||
Project,
|
Project,
|
||||||
ProjectUpdate,
|
ProjectUpdate,
|
||||||
ProjectUpdateEvent,
|
ProjectUpdateEvent,
|
||||||
|
ReceptorAddress,
|
||||||
RefreshToken,
|
RefreshToken,
|
||||||
Role,
|
Role,
|
||||||
Schedule,
|
Schedule,
|
||||||
@@ -5476,6 +5477,18 @@ class InstanceNodeSerializer(BaseSerializer):
|
|||||||
fields = ('id', 'hostname', 'node_type', 'node_state', 'enabled')
|
fields = ('id', 'hostname', 'node_type', 'node_state', 'enabled')
|
||||||
|
|
||||||
|
|
||||||
|
class ReceptorAddressSerializer(BaseSerializer):
|
||||||
|
full_address = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ReceptorAddress
|
||||||
|
fields = ('id', 'address', 'port', 'protocol', 'websocket_path', 'is_internal', 'instance', 'full_address')
|
||||||
|
read_only = 'full_address'
|
||||||
|
|
||||||
|
def get_full_address(self, obj):
|
||||||
|
return obj.get_full_address()
|
||||||
|
|
||||||
|
|
||||||
class InstanceSerializer(BaseSerializer):
|
class InstanceSerializer(BaseSerializer):
|
||||||
show_capabilities = ['edit']
|
show_capabilities = ['edit']
|
||||||
|
|
||||||
@@ -5544,6 +5557,7 @@ class InstanceSerializer(BaseSerializer):
|
|||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(InstanceSerializer, self).get_related(obj)
|
res = super(InstanceSerializer, self).get_related(obj)
|
||||||
|
res['receptor_addresses'] = self.reverse('api:receptor_addresses_list', kwargs={'pk': obj.pk})
|
||||||
res['jobs'] = self.reverse('api:instance_unified_jobs_list', kwargs={'pk': obj.pk})
|
res['jobs'] = self.reverse('api:instance_unified_jobs_list', kwargs={'pk': obj.pk})
|
||||||
res['instance_groups'] = self.reverse('api:instance_instance_groups_list', kwargs={'pk': obj.pk})
|
res['instance_groups'] = self.reverse('api:instance_instance_groups_list', kwargs={'pk': obj.pk})
|
||||||
if obj.node_type in [Instance.Types.EXECUTION, Instance.Types.HOP]:
|
if obj.node_type in [Instance.Types.EXECUTION, Instance.Types.HOP]:
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from awx.api.views import (
|
|||||||
InstanceInstanceGroupsList,
|
InstanceInstanceGroupsList,
|
||||||
InstanceHealthCheck,
|
InstanceHealthCheck,
|
||||||
InstancePeersList,
|
InstancePeersList,
|
||||||
|
ReceptorAddressesList,
|
||||||
)
|
)
|
||||||
from awx.api.views.instance_install_bundle import InstanceInstallBundle
|
from awx.api.views.instance_install_bundle import InstanceInstallBundle
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ urls = [
|
|||||||
re_path(r'^(?P<pk>[0-9]+)/instance_groups/$', InstanceInstanceGroupsList.as_view(), name='instance_instance_groups_list'),
|
re_path(r'^(?P<pk>[0-9]+)/instance_groups/$', InstanceInstanceGroupsList.as_view(), name='instance_instance_groups_list'),
|
||||||
re_path(r'^(?P<pk>[0-9]+)/health_check/$', InstanceHealthCheck.as_view(), name='instance_health_check'),
|
re_path(r'^(?P<pk>[0-9]+)/health_check/$', InstanceHealthCheck.as_view(), name='instance_health_check'),
|
||||||
re_path(r'^(?P<pk>[0-9]+)/peers/$', InstancePeersList.as_view(), name='instance_peers_list'),
|
re_path(r'^(?P<pk>[0-9]+)/peers/$', InstancePeersList.as_view(), name='instance_peers_list'),
|
||||||
|
re_path(r'^(?P<pk>[0-9]+)/receptor_addresses/$', ReceptorAddressesList.as_view(), name='receptor_addresses_list'),
|
||||||
re_path(r'^(?P<pk>[0-9]+)/install_bundle/$', InstanceInstallBundle.as_view(), name='instance_install_bundle'),
|
re_path(r'^(?P<pk>[0-9]+)/install_bundle/$', InstanceInstallBundle.as_view(), name='instance_install_bundle'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -384,6 +384,19 @@ class InstancePeersList(SubListAPIView):
|
|||||||
relationship = 'peers'
|
relationship = 'peers'
|
||||||
|
|
||||||
|
|
||||||
|
class ReceptorAddressesList(ListCreateAPIView):
|
||||||
|
name = _("Receptor Addresses")
|
||||||
|
model = models.ReceptorAddress
|
||||||
|
serializer_class = serializers.ReceptorAddressSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return models.ReceptorAddress.objects.filter(instance=self.kwargs['pk'])
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
request.data.update({'instance': self.kwargs['pk']})
|
||||||
|
return super().post(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class InstanceInstanceGroupsList(InstanceGroupMembershipMixin, SubListCreateAttachDetachAPIView):
|
class InstanceInstanceGroupsList(InstanceGroupMembershipMixin, SubListCreateAttachDetachAPIView):
|
||||||
name = _("Instance's Instance Groups")
|
name = _("Instance's Instance Groups")
|
||||||
model = models.InstanceGroup
|
model = models.InstanceGroup
|
||||||
|
|||||||
38
awx/main/management/commands/add_receptor_address.py
Normal file
38
awx/main/management/commands/add_receptor_address.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
|
# All Rights Reserved
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
|
from awx.main.models import Instance, ReceptorAddress
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
"""
|
||||||
|
Internal tower command.
|
||||||
|
Register receptor address to an already-registered instance.
|
||||||
|
"""
|
||||||
|
|
||||||
|
help = "Add receptor address to an instance."
|
||||||
|
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
parser.add_argument('--hostname', dest='hostname', type=str, help="Hostname this address is added to")
|
||||||
|
parser.add_argument('--address', dest='address', type=str, help="Receptor address")
|
||||||
|
parser.add_argument('--port', dest='port', type=int, help="Receptor listener port")
|
||||||
|
parser.add_argument('--protocol', dest='protocol', type=str, default='tcp', choices=['tcp', 'ws'], help="Protocol of the backend connection")
|
||||||
|
parser.add_argument('--websocket_path', dest='websocket_path', type=str, default="", help="Path for websockets")
|
||||||
|
parser.add_argument(
|
||||||
|
'--is_internal', dest='is_internal', type=bool, default=False, help="If true, address only resolvable within the Kubernetes cluster"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _add_address(self, **kwargs):
|
||||||
|
i = Instance.objects.get(hostname=kwargs.pop('hostname'))
|
||||||
|
kwargs['instance'] = i
|
||||||
|
ReceptorAddress.objects.create(**kwargs)
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def handle(self, **options):
|
||||||
|
address_options = {k: options[k] for k in ('hostname', 'address', 'port', 'protocol', 'websocket_path', 'is_internal')}
|
||||||
|
self._add_address(**address_options)
|
||||||
25
awx/main/migrations/0188_inbound_ingress.py
Normal file
25
awx/main/migrations/0188_inbound_ingress.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Generated by Django 4.2.5 on 2023-10-03 18:31
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('main', '0187_hop_nodes'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ReceptorAddress',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('address', models.CharField(max_length=255)),
|
||||||
|
('port', models.IntegerField(null=True)),
|
||||||
|
('protocol', models.CharField(max_length=10)),
|
||||||
|
('websocket_path', models.CharField(blank=True, default='', max_length=255)),
|
||||||
|
('is_internal', models.BooleanField(default=False)),
|
||||||
|
('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='receptor_addresses', to='main.instance')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -14,6 +14,7 @@ from awx.main.models.unified_jobs import UnifiedJob, UnifiedJobTemplate, StdoutM
|
|||||||
from awx.main.models.organization import Organization, Profile, Team, UserSessionMembership # noqa
|
from awx.main.models.organization import Organization, Profile, Team, UserSessionMembership # noqa
|
||||||
from awx.main.models.credential import Credential, CredentialType, CredentialInputSource, ManagedCredentialType, build_safe_env # noqa
|
from awx.main.models.credential import Credential, CredentialType, CredentialInputSource, ManagedCredentialType, build_safe_env # noqa
|
||||||
from awx.main.models.projects import Project, ProjectUpdate # noqa
|
from awx.main.models.projects import Project, ProjectUpdate # noqa
|
||||||
|
from awx.main.models.receptor_address import ReceptorAddress # noqa
|
||||||
from awx.main.models.inventory import ( # noqa
|
from awx.main.models.inventory import ( # noqa
|
||||||
CustomInventoryScript,
|
CustomInventoryScript,
|
||||||
Group,
|
Group,
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ class Instance(HasPolicyEditsMixin, BaseModel):
|
|||||||
default="",
|
default="",
|
||||||
max_length=50,
|
max_length=50,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Auto-fields, implementation is different from BaseModel
|
# Auto-fields, implementation is different from BaseModel
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
modified = models.DateTimeField(auto_now=True)
|
modified = models.DateTimeField(auto_now=True)
|
||||||
|
|||||||
29
awx/main/models/receptor_address.py
Normal file
29
awx/main/models/receptor_address.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class ReceptorAddress(models.Model):
|
||||||
|
address = models.CharField(max_length=255)
|
||||||
|
port = models.IntegerField(null=True)
|
||||||
|
protocol = models.CharField(max_length=10)
|
||||||
|
websocket_path = models.CharField(max_length=255, default="", blank=True)
|
||||||
|
is_internal = models.BooleanField(default=False)
|
||||||
|
instance = models.ForeignKey(
|
||||||
|
'Instance',
|
||||||
|
related_name='receptor_addresses',
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_full_address(self):
|
||||||
|
scheme = ""
|
||||||
|
path = ""
|
||||||
|
port = ""
|
||||||
|
if self.protocol == "ws":
|
||||||
|
scheme = "wss://"
|
||||||
|
|
||||||
|
if self.protocol == "ws" and self.websocket_path:
|
||||||
|
path = f"/{self.websocket_path}"
|
||||||
|
|
||||||
|
if self.port:
|
||||||
|
port = f":{self.port}"
|
||||||
|
|
||||||
|
return f"{scheme}{self.address}{port}{path}"
|
||||||
Reference in New Issue
Block a user