Example #1
0
 def setUp(self):
     # FirmwareConfig --> migration firmware/0034
     config = Config.objects.get()
    
     # BaseImage for i686 nodes
     self.base_image = BaseImage.objects.create(name='bi', config=config,
                                                architectures=['i686'])
     
     # ServerApi --> migration nodes/0013 + configure server cert
     self.assertTrue(ServerApi.objects.filter(type='registry').exists())
     call_command('setuppki', verbosity=0, interactive=False)
     server = Server.objects.get_default()
     server.api.filter(base_uri__contains=server.mgmt_net.addr).update(
         cert=ca.get_cert().as_pem()
     )
     
     # Node
     self.group = Group.objects.create(name='group', allow_nodes=True)
     self.node = Node.objects.create(name='node', group=self.group, arch='i686')
Example #2
0
 def handle(self, *args, **options):
     # TODO correct key file permissions
     override = options.get('override')
     interactive = options.get('interactive')
     
     try:
         key = ca.get_key()
     except IOError:
         key = False
     
     if override or not key:
         self.stdout.write('writing new private key to \'%s\'' % ca.priv_key_path)
         self.stdout.write('writing new public key to \'%s\'' % ca.pub_key_path)
         ca.gen_key(commit=True)
         override = True
     
     try:
         ca.get_cert()
     except IOError:
         override = True
     
     if override or not ca.get_cert():
         # Avoid import errors
         from nodes.models import Server
         server = Server.objects.first()
         common_name = options.get('common_name') or str(server.mgmt_net.addr)
         country = options.get('dn_country')
         state = options.get('dn_state')
         locality = options.get('dn_locality')
         org_name = options.get('dn_org_name')
         org_unit = options.get('dn_org_unit')
         email = options.get('dn_email')
         
         if interactive:
             msg = ('-----\n'
                 'You are about to be asked to enter information that\n'
                 'will be incorporated\n'
                 'into your certificate request.\n'
                 'What you are about to enter is what is called a\n'
                 'Distinguished Name or a DN.\n'
                 'There are quite a few fields but you can leave some blank\n'
                 '-----\n')
             self.stdout.write(msg)
             
             msg = 'Country Name (2 letter code) [%s]: ' % country
             country = input(msg) or country
             
             msg = 'State or Province Name (full name) [%s]: ' % state
             state = input(msg) or state
             
             msg = 'Locality Name (eg, city) [%s]: ' % locality
             locality = input(msg) or locality
             
             msg = 'Organization Name (eg, company) [%s]: ' % org_name
             org_name = input(msg) or org_name
             
             msg = 'Organizational Unit Name (eg, section) [%s]: ' % org_unit
             org_unit = input(msg) or org_unit
             
             msg = 'Email Address [%s]: ' % email
             email = input(msg) or email
         
         self.stdout.write('Common Name: %s' % common_name)
         subject = {
             'C': country,
             'S': state,
             'L': locality,
             'O': org_name,
             'OU': org_unit,
             'Email': email,
             'CN': common_name }
         cert = ca.gen_cert(commit=True, **subject)
         self.stdout.write('writing new certificate to \'%s\'' % ca.cert_path)
         
         # Update mgmt network Server APIs certificate
         server.api.filter(base_uri__contains=server.mgmt_net.addr).update(
             cert=cert.as_pem())
         return
     
     self.stdout.write('\nYour cert and keys are already in place.\n'
                       ' Use --override in order to override them.\n\n')
 def handle(self, *args, **options):
     # Warn about deprecated options
     if options.get('local'):
         self.stderr.write("Warning: 'local' option is deprecated and will be ignored.\n")
     
     version = options.get('version')
     upgrade_notes = []
     if version:
         try:
             major, major2, minor = decode_version(version)
         except ValueError as e:
             raise CommandError(e)
         # Represent version as two digits per number: 1.2.2 -> 10202
         version = int(str(major) + "%02d" % int(major2) + "%02d" % int(minor))
         
         # Pre-upgrade operations (version specific)
         if version < 835:
             # prevent schema migrations from failing
             if is_installed('firmware'):
                 from firmware.models import Build
                 Build.objects.filter(base_image=None).update(base_image='')
         if version <= 902:
             if is_installed('maintenance'):
                 # Apply losed migrations
                 from south.models import MigrationHistory
                 migrated = MigrationHistory.objects.filter(app_name='maintenance').exists()
                 if not migrated:
                     run('python manage.py migrate maintenance 0001 --fake')
         if version < 1002:
             # Update monitor settings (fix typo and add DiskFreeMonitor)
             context = {
                 'settings': run("find . -type f -name 'settings.py'|grep -v 'vct/'")
             }
             # Try automaticate update (making a backup)
             if context['settings']:
                 run("cp %(settings)s %(settings)s.upgrade.bak" % context)
                 # fix NumProcessesMonitor typo
                 run("sed -i 's/NumPocessesMonitor/NumProcessesMonitor/g' "
                     "%(settings)s" % context)
                 # append disk monitor (if needed)
                 # this is a rude check (but runned in a conservative way)
                 if 'DiskFreeMonitor' not in open(context['settings']).read():
                     run("sed -i '/MONITOR_MONITORS = ($/ a\ "
                         "   (\"monitor.monitors.DiskFreeMonitor\",),' "
                         "%(settings)s" % context)
             # warn the user about settings changes
             autoupdate_status = 'OK' if context['settings'] else 'FAIL'
             upgrade_notes.append('The monitor application has changed and .'
                 'some settings updates are required:\n'
                 ' - Fix typo on NumProcessesMonitor (missing "r")\n'
                 ' - Enable disk monitor\n'
                 ' Please read the monitor app doc (MONITOR_MONITORS setting)\n'
                 'AUTOUPDATE: %s' % autoupdate_status)
         if version <= 1102:
             # Handle InconsistentMigrationHistory on tinc app
             # * v0.11.2 tinc includes 0022, 0028..0030
             # * v0.11.3 tinc adds 0023..0027
             # We can force south to merge migrations because
             # are implemented to be runned without dependencies
             run('python manage.py migrate tinc 0030 --merge --noinput')
     
     if not options.get('specifics_only'):
         # Common stuff
         development = options.get('development')
         controller_admin = os.path.join(os.path.dirname(__file__), '../../bin/')
         controller_admin = os.path.join(controller_admin, 'controller-admin.sh')
         run('chmod +x %s' % controller_admin)
         
         extra = '--development' if development else ''
         if options.get('proxy'):
             extra += ' --proxy %s' % options.get('proxy')
         run("%s install_requirements " % controller_admin + extra)
         run("python manage.py collectstatic --noinput")
         
         run("python manage.py syncdb --noinput")
         run("python manage.py migrate --noinput")
         if is_installed('firmware'):
             run("python manage.py syncfirmwareplugins")
         if is_installed('notifications'):
             run("python manage.py syncnotifications")
         if is_installed('resources'):
             run("python manage.py syncresources")
         if options.get('restart'):
             run("python manage.py restartservices")
     
     if not version:
         self.stderr.write('\nNext time you migth want to provide a --from argument '
                           'in order to run version specific upgrade operations\n')
         return
     
     # Post-upgrade operations (version specific)
     if version <= 629:
         # Clean existing sessions because of change on auth backend
         run('echo "delete from django_session;" | python manage.py dbshell')
     if version < 801:
         deprecate_periodic_tasks(('state.ping',))
     if version < 809:
         # Add PKI directories
         from pki import ca
         from controller.utils.paths import get_site_root
         site_root = get_site_root()
         username = run("stat -c %%U %s" % site_root)
         get_dir = lambda f: os.path.dirname(getattr(ca, f+'_path'))
         for d in set( get_dir(f) for f in ['priv_key', 'pub_key', 'cert'] ):
             run('mkdir -p %s' % d)
             run('chown %s %s' % (username, d))
         upgrade_notes.append('HTTPS certificate support for the management '
             'network has been introduced in version 0.8.9.\n'
             'In order to use it you sould run:\n'
             '  > python manage.py setuppki\n'
             '  > sudo python manage.py setupapache\n')
     if version < 838:
         # Purge communitynetworks.periodic_cache_node_db
         from djcelery.models import PeriodicTask
         PeriodicTask.objects.filter(name='communitynetworks.periodic_cache_node_db').delete()
         run('rabbitmqctl stop_app')
         run('rabbitmqctl reset')
         run('rabbitmqctl start_app')
         run('service celeryd restart')
         upgrade_notes.append('New Celeryd init.d configuration has been '
             'introduced in 0.8.38.\nIt is strongly recommended to upgrade by\n'
             '  > sudo python manage.py setupceleryd\n')
         # Deprecate x86 and amd64 architectures
         from nodes.models import Node
         Node.objects.filter(arch='x86').update(arch='i686')
         Node.objects.filter(arch='amd64').update(arch='x86_64')
         upgrade_notes.append('In order to support Base authentication while downloading '
             'firmwares you should add "WSGIPassAuthorization On" on your apache config.\n'
             'Alternatively you can perform this operation with the following command\n'
             '  > sudo python manage.py setupapache\n'
             '  > /etc/init.d/apache2 reload\n')
     if version < 900:
         upgrade_notes.append('Apache configuration is now placed under '
             '/etc/apache2/conf.d/<project_name>.conf. It is convenient for you '
             'to migrate your current configuration located on /etc/apache2/httpd.conf '
             'to this new location.\n')
         upgrade_notes.append('Celery workers configuration has been updated. '
             'Please update it by running:\n'
             '  > sudo python manage.py setupceleryd\n')
     if version < 905:
         # TODO find the root cause of this
         # maybe is shit imported on settings that import settings like add_app
         # Prevent crazy import erros to appear :S
         from django.utils import translation
         translation.activate('en-us')
         # Change template types for more generic ones
         from slices.models import Template
         from slices.settings import SLICES_TEMPLATE_TYPES
         template_types = [ t[0] for t in SLICES_TEMPLATE_TYPES ]
         if 'debian' in template_types:
             Template.objects.filter(type='debian6').update(type='debian')
         if 'openwrt' in template_types:
             Template.objects.filter(type='openwrt-backfire').update(type='openwrt')
     if version < 906:
         deprecate_periodic_tasks(('state.nodestate', 'state.sliverstate'))
     if version <= 907:
         # Generate sha256
         from slices.models import Template
         for template in Template.objects.all():
             template.save()
         upgrade_notes.append("It is extremly recommended to update your database "
             "settings to enable atomic request behaviour:\n"
             "  https://docs.djangoproject.com/en/dev/topics/db/transactions/#tying-transactions-to-http-requests\n"
             "Just add:\n"
             "   'ATOMIC_REQUESTS': True,\n"
             "into DATABASES setting within <project_name>/<project_name>/settings.py")
     if version <= 1003:
         # Update firmware configuration after Island refactor (#264)
         from firmware.models import ConfigFile
         try:
             cfg_file = ConfigFile.objects.get(path__contains="node.tinc.connect_to")
         except (ConfigFile.DoesNotExist, ConfigFile.MultipleObjectsReturned):
             # Warn the user that needs to perform manual update
             msg = "Firmware configuration update has failed. "
         else:
             cfg_file.content = cfg_file.content.replace("node.tinc.island", "node.island")
             cfg_file.save()
             msg = "Firmware configuration updated successfully. Updated ConfigFile ID: %i." % cfg_file.pk
         upgrade_notes.append("%s\nPlease check version 0.10.4 release notes:\n"
             "https://wiki.confine-project.eu/soft:server-release-notes#section0104" % msg)
     if version < 1103:
         # Update mgmt network Server APIs certificate
         # perform raw SQL querie because models hasn't been
         # reloaded yet and cannot access e.g. server.api
         from django.db import connection
         from nodes.models import Server
         from pki import ca
         
         server_id = Server.objects.order_by('id').first().pk
         try:
             cert = ca.get_cert().as_pem()
         except IOError:
             msg = ("Failed to update Server APIs certificate. Missing "
                    "server certificate '%s'.\n"
                    "Run 'python manage.py setuppki --help'" % ca.cert_path)
             upgrade_notes.append(msg)
         else:
             update_sql = ('UPDATE "nodes_serverapi" SET cert = %s '
                           'WHERE "nodes_serverapi"."server_id" = %s')
             cursor = connection.cursor()
             cursor.execute(update_sql, [cert, server_id])
             del cursor
             upgrade_notes.append("Updated Server APIs certificate.")
     
     if upgrade_notes and options.get('print_upgrade_notes'):
         self.stdout.write('\n\033[1m\n'
             '    ===================\n'
             '    ** UPGRADE NOTES **\n'
             '    ===================\n\n' +
             '\n'.join(upgrade_notes) + '\033[m\n')
    def handle(self, *args, **options):
        # Warn about deprecated options
        if options.get('local'):
            self.stderr.write(
                "Warning: 'local' option is deprecated and will be ignored.\n")

        version = options.get('version')
        upgrade_notes = []
        if version:
            try:
                major, major2, minor = decode_version(version)
            except ValueError as e:
                raise CommandError(e)
            # Represent version as two digits per number: 1.2.2 -> 10202
            version = int(
                str(major) + "%02d" % int(major2) + "%02d" % int(minor))

            # Pre-upgrade operations (version specific)
            if version < 835:
                # prevent schema migrations from failing
                if is_installed('firmware'):
                    from firmware.models import Build
                    Build.objects.filter(base_image=None).update(base_image='')
            if version <= 902:
                if is_installed('maintenance'):
                    # Apply losed migrations
                    from south.models import MigrationHistory
                    migrated = MigrationHistory.objects.filter(
                        app_name='maintenance').exists()
                    if not migrated:
                        run('python manage.py migrate maintenance 0001 --fake')
            if version < 1002:
                # Update monitor settings (fix typo and add DiskFreeMonitor)
                context = {
                    'settings':
                    run("find . -type f -name 'settings.py'|grep -v 'vct/'")
                }
                # Try automaticate update (making a backup)
                if context['settings']:
                    run("cp %(settings)s %(settings)s.upgrade.bak" % context)
                    # fix NumProcessesMonitor typo
                    run("sed -i 's/NumPocessesMonitor/NumProcessesMonitor/g' "
                        "%(settings)s" % context)
                    # append disk monitor (if needed)
                    # this is a rude check (but runned in a conservative way)
                    if 'DiskFreeMonitor' not in open(
                            context['settings']).read():
                        run("sed -i '/MONITOR_MONITORS = ($/ a\ "
                            "   (\"monitor.monitors.DiskFreeMonitor\",),' "
                            "%(settings)s" % context)
                # warn the user about settings changes
                autoupdate_status = 'OK' if context['settings'] else 'FAIL'
                upgrade_notes.append(
                    'The monitor application has changed and .'
                    'some settings updates are required:\n'
                    ' - Fix typo on NumProcessesMonitor (missing "r")\n'
                    ' - Enable disk monitor\n'
                    ' Please read the monitor app doc (MONITOR_MONITORS setting)\n'
                    'AUTOUPDATE: %s' % autoupdate_status)
            if version <= 1102:
                # Handle InconsistentMigrationHistory on tinc app
                # * v0.11.2 tinc includes 0022, 0028..0030
                # * v0.11.3 tinc adds 0023..0027
                # We can force south to merge migrations because
                # are implemented to be runned without dependencies
                run('python manage.py migrate tinc 0030 --merge --noinput')

        if not options.get('specifics_only'):
            # Common stuff
            development = options.get('development')
            controller_admin = os.path.join(os.path.dirname(__file__),
                                            '../../bin/')
            controller_admin = os.path.join(controller_admin,
                                            'controller-admin.sh')
            run('chmod +x %s' % controller_admin)

            extra = '--development' if development else ''
            if options.get('proxy'):
                extra += ' --proxy %s' % options.get('proxy')
            run("%s install_requirements " % controller_admin + extra)
            run("python manage.py collectstatic --noinput")

            run("python manage.py syncdb --noinput")
            run("python manage.py migrate --noinput")
            if is_installed('firmware'):
                run("python manage.py syncfirmwareplugins")
            if is_installed('notifications'):
                run("python manage.py syncnotifications")
            if is_installed('resources'):
                run("python manage.py syncresources")
            if options.get('restart'):
                run("python manage.py restartservices")

        if not version:
            self.stderr.write(
                '\nNext time you migth want to provide a --from argument '
                'in order to run version specific upgrade operations\n')
            return

        # Post-upgrade operations (version specific)
        if version <= 629:
            # Clean existing sessions because of change on auth backend
            run('echo "delete from django_session;" | python manage.py dbshell'
                )
        if version < 801:
            deprecate_periodic_tasks(('state.ping', ))
        if version < 809:
            # Add PKI directories
            from pki import ca
            from controller.utils.paths import get_site_root
            site_root = get_site_root()
            username = run("stat -c %%U %s" % site_root)
            get_dir = lambda f: os.path.dirname(getattr(ca, f + '_path'))
            for d in set(get_dir(f) for f in ['priv_key', 'pub_key', 'cert']):
                run('mkdir -p %s' % d)
                run('chown %s %s' % (username, d))
            upgrade_notes.append(
                'HTTPS certificate support for the management '
                'network has been introduced in version 0.8.9.\n'
                'In order to use it you sould run:\n'
                '  > python manage.py setuppki\n'
                '  > sudo python manage.py setupapache\n')
        if version < 838:
            # Purge communitynetworks.periodic_cache_node_db
            from djcelery.models import PeriodicTask
            PeriodicTask.objects.filter(
                name='communitynetworks.periodic_cache_node_db').delete()
            run('rabbitmqctl stop_app')
            run('rabbitmqctl reset')
            run('rabbitmqctl start_app')
            run('service celeryd restart')
            upgrade_notes.append(
                'New Celeryd init.d configuration has been '
                'introduced in 0.8.38.\nIt is strongly recommended to upgrade by\n'
                '  > sudo python manage.py setupceleryd\n')
            # Deprecate x86 and amd64 architectures
            from nodes.models import Node
            Node.objects.filter(arch='x86').update(arch='i686')
            Node.objects.filter(arch='amd64').update(arch='x86_64')
            upgrade_notes.append(
                'In order to support Base authentication while downloading '
                'firmwares you should add "WSGIPassAuthorization On" on your apache config.\n'
                'Alternatively you can perform this operation with the following command\n'
                '  > sudo python manage.py setupapache\n'
                '  > /etc/init.d/apache2 reload\n')
        if version < 900:
            upgrade_notes.append(
                'Apache configuration is now placed under '
                '/etc/apache2/conf.d/<project_name>.conf. It is convenient for you '
                'to migrate your current configuration located on /etc/apache2/httpd.conf '
                'to this new location.\n')
            upgrade_notes.append(
                'Celery workers configuration has been updated. '
                'Please update it by running:\n'
                '  > sudo python manage.py setupceleryd\n')
        if version < 905:
            # TODO find the root cause of this
            # maybe is shit imported on settings that import settings like add_app
            # Prevent crazy import erros to appear :S
            from django.utils import translation
            translation.activate('en-us')
            # Change template types for more generic ones
            from slices.models import Template
            from slices.settings import SLICES_TEMPLATE_TYPES
            template_types = [t[0] for t in SLICES_TEMPLATE_TYPES]
            if 'debian' in template_types:
                Template.objects.filter(type='debian6').update(type='debian')
            if 'openwrt' in template_types:
                Template.objects.filter(type='openwrt-backfire').update(
                    type='openwrt')
        if version < 906:
            deprecate_periodic_tasks(('state.nodestate', 'state.sliverstate'))
        if version <= 907:
            # Generate sha256
            from slices.models import Template
            for template in Template.objects.all():
                template.save()
            upgrade_notes.append(
                "It is extremly recommended to update your database "
                "settings to enable atomic request behaviour:\n"
                "  https://docs.djangoproject.com/en/dev/topics/db/transactions/#tying-transactions-to-http-requests\n"
                "Just add:\n"
                "   'ATOMIC_REQUESTS': True,\n"
                "into DATABASES setting within <project_name>/<project_name>/settings.py"
            )
        if version <= 1003:
            # Update firmware configuration after Island refactor (#264)
            from firmware.models import ConfigFile
            try:
                cfg_file = ConfigFile.objects.get(
                    path__contains="node.tinc.connect_to")
            except (ConfigFile.DoesNotExist,
                    ConfigFile.MultipleObjectsReturned):
                # Warn the user that needs to perform manual update
                msg = "Firmware configuration update has failed. "
            else:
                cfg_file.content = cfg_file.content.replace(
                    "node.tinc.island", "node.island")
                cfg_file.save()
                msg = "Firmware configuration updated successfully. Updated ConfigFile ID: %i." % cfg_file.pk
            upgrade_notes.append(
                "%s\nPlease check version 0.10.4 release notes:\n"
                "https://wiki.confine-project.eu/soft:server-release-notes#section0104"
                % msg)
        if version < 1103:
            # Update mgmt network Server APIs certificate
            # perform raw SQL querie because models hasn't been
            # reloaded yet and cannot access e.g. server.api
            from django.db import connection
            from nodes.models import Server
            from pki import ca

            server_id = Server.objects.order_by('id').first().pk
            try:
                cert = ca.get_cert().as_pem()
            except IOError:
                msg = ("Failed to update Server APIs certificate. Missing "
                       "server certificate '%s'.\n"
                       "Run 'python manage.py setuppki --help'" % ca.cert_path)
                upgrade_notes.append(msg)
            else:
                update_sql = ('UPDATE "nodes_serverapi" SET cert = %s '
                              'WHERE "nodes_serverapi"."server_id" = %s')
                cursor = connection.cursor()
                cursor.execute(update_sql, [cert, server_id])
                del cursor
                upgrade_notes.append("Updated Server APIs certificate.")

        if upgrade_notes and options.get('print_upgrade_notes'):
            self.stdout.write('\n\033[1m\n'
                              '    ===================\n'
                              '    ** UPGRADE NOTES **\n'
                              '    ===================\n\n' +
                              '\n'.join(upgrade_notes) + '\033[m\n')